68 lines
4.0 KiB
TypeScript
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;
|
|
}
|