Files
Dune/tests/fixture-advisor-worm-ride-flip.ts
T
Quintine c608d6fea6
Verify and publish checkpoint container / container (push) Canceled after 0s
Handle advisor conversion after worm rides
2026-10-29 09:10:53 +11:00

68 lines
4.0 KiB
TypeScript

import assert from 'node:assert/strict';
import { createGame, joinGame, newPlayer, type Action, type Game } from '../game/engine';
import { actAdvisorPreparation as act, advisorPreparationInventory as inventory,
advisorPreparationPlayer as player, nextAdvisorPreparationStep } from './fixture-advisor-preparation';
export const actAdvisorWormRide = act;
export const advisorWormRidePlayer = player;
export const advisorWormRideInventory = inventory;
/** Genuine Basic-faction Advanced public start, prediction, native placements
* and Traitors. Only the unopened starting Treachery deal is selected. The
* later phase, current ride, queued ride and conserved force/stance positions
* are explicitly staged to isolate the source departure. */
export function createAdvisorWormRideFixture(options: {intrusion?: boolean; partial?: boolean} = {}) {
const bg = 'beneGesserit', fremen = 'fremen', holder = 'atreides';
let game = createGame('ADVISORWORMRIDE', newPlayer(bg,'Bene Gesserit','beneGesserit'),true);
joinGame(game,newPlayer(fremen,'Fremen','fremen'));
joinGame(game,newPlayer(holder,'Atreides','atreides'));
for (const seat of game.players) game = act(game,seat.id,{type:'ready'});
game = act(game,bg,{type:'start',advancedPreview:true});
assert.equal(game.status,'setup');
const originalCards = inventory(game);
const karama = game.deck.find(card => card.effect === 'karama'); assert.ok(karama);
const ordinary = game.deck.filter(card => ['projectile','poison','shield','snooper'].includes(card.kind)).slice(0,2);
assert.equal(ordinary.length,2);
for (const [position,card] of [ordinary[0],ordinary[1],karama].entries()) {
const index = game.deck.findIndex(candidate => candidate.id === card.id); assert.ok(index >= position);
[game.deck[position],game.deck[index]] = [game.deck[index],game.deck[position]];
}
for (let count=0; game.status === 'setup' && count<100; count++) {
if (game.setupStage === 'forces' && player(game,fremen).reserves === 20) {
game = act(game,fremen,{type:'fremenSetup',placements:{sietch_tabr:10}});
continue;
}
const step = nextAdvisorPreparationStep(game); assert.ok(step);
game = act(game,step.actor,step.action);
}
assert.equal(game.status,'playing'); assert.equal(game.advanced,true);
assert.ok(player(game,holder).hand.some(card => card.id === karama.id));
assert.deepEqual(inventory(game),originalCards);
const bgSeat = player(game,bg), f = player(game,fremen), a = player(game,holder);
bgSeat.forces = {'red_chasm:7':2, ...(options.intrusion ? {'imperial_basin:10':1} : {})};
bgSeat.reserves = 20 - (options.intrusion ? 3 : 2); bgSeat.tanks = 0;
bgSeat.advisors = {red_chasm:{}};
f.forces = {'red_chasm:7':2, 'hagga_basin:12':1}; f.reserves = 17; f.tanks = 0;
f.elites = {reserves:3,tanks:0,forces:{},revived:0};
a.forces = {}; a.reserves = 20; a.tanks = 0;
Object.assign(game,{phase:1,turn:2,nexus:true,active:null,ready:[],storm:18,
stormPending:null,phaseOpening:null,response:null,truthtrance:null,
spiceWindow:null,spiceResolution:null,spiceSequence:{pile:1,skipped:[]},
wormRides:['hagga_basin'],decision:{kind:'wormRide',player:fremen,territory:'red_chasm'}});
const action:Action = {type:'decision',accept:true,territory:'imperial_basin',sector:10,
forces:{'red_chasm:7':options.partial ? 1 : 2},eliteForces:{'red_chasm:7':0}};
return {game,bg,fremen,holder,karama:karama.id,action,origin:'red_chasm',source:'red_chasm:7',
destination:'imperial_basin:10',second:'hagga_basin',originalCards,partial:!!options.partial,
intrusion:!!options.intrusion};
}
/** Pass only the committed source conversion; preserve any next decision. */
export function passAdvisorWormRide(source:Game):Game {
let game = source;
for (let count=0; game.response?.automaticAdvisorFlip && count<30; count++) {
const actor = game.players.find(seat => !game.response!.passed.includes(seat.id)); assert.ok(actor);
game = act(game,actor.id,{type:'passResponse'});
}
assert.ok(!game.response?.automaticAdvisorFlip); return game;
}