Files
Dune/tests/coalition-bribe-alliance-guides.test.ts
T
Quintine 0faa7923b0
Verify and publish checkpoint container / container (push) Successful in 10m51s
Allow group departures with bribe commitments
2026-10-29 00:46:59 +11:00

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);
});