77 lines
4.4 KiB
TypeScript
77 lines
4.4 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 send = (game:Game, actor:string, action:Action) => applyAction(game, actor, action);
|
|
const seat = (game:Game, id:string) => game.players.find(player => player.id === id)!;
|
|
|
|
/** Stage the Nexus, respondent's wallet and the two physical Truthtrance
|
|
* cards. Alliance formation, both questions and all guided actions are real. */
|
|
function initial(): {game:Game; cards:string[]} {
|
|
let game = createGame('GROUPBOTH', 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,ready:[],
|
|
order:['a','b','c','d','e'],deck:baseDeck()});
|
|
game.coalitionState = emptyCoalitionState('unlimited'); seat(game,'a').spice = 8;
|
|
game = send(game,'a',{type:'alliance',target:'b'});
|
|
game = send(game,'b',{type:'alliance',target:'a'});
|
|
const pair = coalitionOf(game.players,game.coalitionState,'a'); assert.ok(pair);
|
|
game = send(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 = send(game,actor,{type:'coalitionAnswer',event:offer.event,nexus:offer.nexus,
|
|
turn:offer.turn,accept:true,expectedRevision:game.coalitionState!.revision});
|
|
}
|
|
const cards:string[] = [];
|
|
for (let count=0;count<2;count++) {
|
|
const index = game.deck.findIndex(card => card.effect === 'truthtrance'); assert.ok(index >= 0);
|
|
const [card] = game.deck.splice(index,1); cards.push(card.id); seat(game,'e').hand.push(card);
|
|
}
|
|
return {game,cards};
|
|
}
|
|
function ask(game:Game, card:string, question:unknown, answer:'yes'|'no'):Game {
|
|
game = send(game,'e',{type:'card',card});
|
|
while(game.truthtrance?.stage === 'priority') {
|
|
const next = game.players.find(player => !game.truthtrance!.passed.includes(player.id)); assert.ok(next);
|
|
game = send(game,next.id,{type:'truthPass'});
|
|
}
|
|
game = send(game,'e',{type:'truthAsk',question});
|
|
const view = viewGame(game,'a');
|
|
const answers = game.truthtrance?.question?.kind === 'bribe' ? view.truthBribeAnswers : view.truthAllianceAnswers;
|
|
assert.ok(answers?.includes(answer), `The ${answer} answer must be legal with both commitments.`);
|
|
return send(game,'a',{type:'truthAnswer',answer});
|
|
}
|
|
|
|
void test('a group alliance No and positive bribe answer retain both legal guides',()=>{
|
|
const fixture = initial();
|
|
let game = ask(fixture.game,fixture.cards[0],
|
|
{kind:'bribe',target:'a',turn:fixture.game.turn,recipient:'d',minimum:2},'yes');
|
|
game = ask(game,fixture.cards[1],
|
|
{kind:'alliance',target:'a',turn:game.turn,partner:'b'},'no');
|
|
const bribe = game.bribePromises!.find(row=>row.player === 'a')!;
|
|
const alliance = game.alliancePromises!.find(row=>row.player === 'a')!;
|
|
assert.ok(!bribe.released && !bribe.fulfilled && !alliance.released && !alliance.fulfilled);
|
|
const beforeReady = structuredClone(game);
|
|
assert.throws(()=>send(game,'a',{type:'ready'}));
|
|
assert.deepEqual(game,beforeReady);
|
|
const guide = viewGame(game,'a').alliancePromiseGuide; assert.ok(guide);
|
|
assert.equal(guide.action.type,'coalitionLeave');
|
|
game = send(game,'a',guide.action);
|
|
assert.equal(coalitionOf(game.players,game.coalitionState,'a'),null);
|
|
assert.deepEqual(coalitionOf(game.players,game.coalitionState,'b')!.members,['b','c']);
|
|
assert.ok(!game.alliancePromises!.find(row=>row.event === alliance.event)!.released);
|
|
assert.ok(!game.bribePromises!.find(row=>row.event === bribe.event)!.released);
|
|
const payment = viewGame(game,'a').bribePromiseGuide; assert.ok(payment);
|
|
assert.equal(payment.action.type,'bribe'); assert.equal(payment.action.target,'d');
|
|
game = send(game,'a',payment.action);
|
|
assert.equal(game.bribePromises!.find(row=>row.event === bribe.event)!.fulfilled,true);
|
|
assert.equal(seat(game,'d').bribes,2);
|
|
game = send(game,'a',{type:'ready'});
|
|
assert.ok(game.ready.includes('a'));
|
|
for(const card of fixture.cards) assert.equal(game.discard.filter(row=>row.id === card).length,1);
|
|
});
|