56 lines
3.1 KiB
TypeScript
56 lines
3.1 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { baseDeck } from '../game/cards';
|
|
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
|
|
import { applyAction, createGame, newPlayer, viewGame, type Action, type Game } from '../game/engine';
|
|
|
|
const act = (game:Game, actor:string, action:Action) => applyAction(game,actor,action);
|
|
|
|
/** The quiet Nexus, wallet, Truthtrance and the later conflicting scalar
|
|
* answer are staged. Formation, the whole question and payment are real. */
|
|
function fixture(): Game {
|
|
let game = createGame('GROUPWHOLELEAVE',newPlayer('a','Atreides','atreides'),false);
|
|
game.players.push(newPlayer('b','Emperor','emperor'),newPlayer('c','Fremen','fremen'),
|
|
newPlayer('d','Harkonnen','harkonnen'),newPlayer('e','Guild','guild'));
|
|
Object.assign(game,{status:'playing',phase:1,turn:2,nexus:true,
|
|
order:['a','b','c','d','e'],ready:[],deck:baseDeck()});
|
|
game.coalitionState=emptyCoalitionState('unlimited');
|
|
game.players.find(player=>player.id==='a')!.spice=8;
|
|
game=act(game,'a',{type:'alliance',target:'b'});
|
|
game=act(game,'b',{type:'alliance',target:'a'});
|
|
const pair=coalitionOf(game.players,game.coalitionState,'a'); assert.ok(pair);
|
|
game=act(game,'a',{type:'coalitionPropose',groupId:pair.id,entrant:'c',
|
|
expectedRevision:game.coalitionState!.revision});
|
|
for (const actor of ['b','c']) {
|
|
const offer=game.coalitionState!.proposal!;
|
|
game=act(game,actor,{type:'coalitionAnswer',event:offer.event,nexus:offer.nexus,
|
|
turn:offer.turn,accept:true,expectedRevision:game.coalitionState!.revision});
|
|
}
|
|
const index=game.deck.findIndex(card=>card.effect==='truthtrance'); assert.ok(index>=0);
|
|
const [card]=game.deck.splice(index,1);
|
|
game.players.find(player=>player.id==='e')!.hand.push(card);
|
|
game=act(game,'e',{type:'card',card:card.id});
|
|
while (game.truthtrance?.stage==='priority') {
|
|
const actor=game.players.find(player=>!game.truthtrance!.passed.includes(player.id)); assert.ok(actor);
|
|
game=act(game,actor.id,{type:'truthPass'});
|
|
}
|
|
game=act(game,'e',{type:'truthAsk',question:{kind:'turnActions',target:'a',turn:game.turn,
|
|
mixed:{kind:'and',terms:[{kind:'action',action:{kind:'payment',recipient:'d',minimum:2}},
|
|
{kind:'action',action:{kind:'ship',territory:'polar_sink',minimum:1}}]}}});
|
|
game=act(game,'a',{type:'truthAnswer',answer:'yes'});
|
|
game=act(game,'a',{type:'bribe',target:'d',amount:2});
|
|
assert.ok(game.turnActionPromises?.some(row=>!row.fulfilled && !row.released));
|
|
const source=JSON.stringify(['ordinaryNexus',game.turn,game.spiceDiscard.map(pile=>pile.length),
|
|
game.spiceSequence?.pile ?? null,game.spiceSequence?.skipped?.length ?? 0]);
|
|
game.alliancePromises=[{event:'staged-no',source,player:'a',asker:'e',turn:game.turn,
|
|
partner:'b',answer:false}];
|
|
return game;
|
|
}
|
|
|
|
void test('a live whole-turn promise does not expose a forbidden group-leave guide',()=>{
|
|
const game=fixture();
|
|
const guide=viewGame(game,'a').turnActionGuide;
|
|
assert.notEqual(guide?.action.type,'coalitionLeave');
|
|
if (guide) assert.doesNotThrow(()=>act(game,'a',guide.action));
|
|
});
|