60 lines
2.9 KiB
TypeScript
60 lines
2.9 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
|
|
import { applyAction, createGame, newPlayer, type Action, type Game } from '../game/engine';
|
|
|
|
const act = (game: Game, actor: string, action: Action) => applyAction(game, actor, action);
|
|
|
|
/** A player's retained partner and unrelated bribe remain jointly feasible.
|
|
* The empty joint ledger is significant: it delegates accepted-order
|
|
* reconciliation to the scalar alliance planner. */
|
|
function fixture(): Game {
|
|
let game = createGame('ADMISSIONJOINT', 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:[]});
|
|
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 source = JSON.stringify(['ordinaryNexus', game.turn, game.spiceDiscard.map(pile => pile.length),
|
|
game.spiceSequence?.pile ?? null, game.spiceSequence?.skipped?.length ?? 0]);
|
|
game.alliancePromises = [{event:'alliance', source, player:'a', asker:'e', turn:game.turn,
|
|
partner:'b', answer:true}];
|
|
game.bribePromises = [{event:'bribe', player:'a', asker:'e', turn:game.turn,
|
|
recipient:'e', minimum:1, answer:true, progress:0}];
|
|
game.turnActionPromises = [];
|
|
return game;
|
|
}
|
|
|
|
function proposal(game: Game) {
|
|
const group = coalitionOf(game.players, game.coalitionState, 'a'); assert.ok(group);
|
|
return {type:'coalitionPropose', groupId:group.id, entrant:'c',
|
|
expectedRevision:game.coalitionState!.revision};
|
|
}
|
|
function answer(game: Game, accept: boolean) {
|
|
const offer = game.coalitionState!.proposal; assert.ok(offer);
|
|
return {type:'coalitionAnswer', event:offer.event, nexus:offer.nexus, turn:offer.turn,
|
|
accept, expectedRevision:game.coalitionState!.revision};
|
|
}
|
|
function commitmentsLive(game: Game) {
|
|
assert.equal(game.bribePromises![0].released ?? false, false);
|
|
assert.equal(game.alliancePromises![0].released ?? false, false);
|
|
}
|
|
|
|
void test('joint accepted-order promises remain binding after group admission', () => {
|
|
let game = fixture();
|
|
game = act(game, 'a', proposal(game));
|
|
commitmentsLive(game);
|
|
game = act(game, 'b', answer(game, true));
|
|
commitmentsLive(game);
|
|
game = act(game, 'c', answer(game, true));
|
|
commitmentsLive(game);
|
|
assert.equal(game.coalitionState!.proposal, null);
|
|
assert.deepEqual(coalitionOf(game.players, game.coalitionState, 'a')!.members, ['a','b','c']);
|
|
game = act(game, 'a', {type:'bribe', target:'e', amount:1});
|
|
assert.equal(game.bribePromises![0].fulfilled, true);
|
|
assert.equal(game.alliancePromises![0].released ?? false, false);
|
|
});
|