70 lines
4.0 KiB
TypeScript
70 lines
4.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { baseDeck } from '../game/cards';
|
|
import { runBots } from '../game/bots';
|
|
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
|
|
import { applyAction, createGame, newPlayer, viewGame, type Action, type Game } from '../game/engine';
|
|
import { createRevivalOrder } from '../game/revival-order';
|
|
|
|
const act = (game: Game, actor: string, action: Action) => applyAction(game, actor, action);
|
|
const seat = (game: Game, id: string) => {
|
|
const found = game.players.find(player => player.id === id); assert.ok(found); return found;
|
|
};
|
|
|
|
/** Advanced group is built by genuine pair and group Nexus actions. The Nexus
|
|
* itself, original public order and subsequent Revival phase are staged, not a
|
|
* native full game. Atreides and Harkonnen each move four of their own ordinary
|
|
* reserves to Tanks; the queue is made with the production order helper, and
|
|
* Fremen actually finishes his opportunity before the first ally acts. */
|
|
function fixture() {
|
|
let game = createGame('ORDEREDGROUPREVIVAL', newPlayer('f', 'Fremen', 'fremen'), true);
|
|
game.players.push(newPlayer('a', 'Atreides', 'atreides'),
|
|
newPlayer('h', 'Harkonnen', 'harkonnen'), newPlayer('g', 'Guild', 'guild'));
|
|
Object.assign(game, {status: 'playing', phase: 1, turn: 2, nexus: true,
|
|
order: ['f', 'a', 'h', 'g'], ready: [], storm: 18, deck: baseDeck(),
|
|
response: null, decision: null, phaseOpening: null});
|
|
game.coalitionState = emptyCoalitionState();
|
|
game = act(game, 'f', {type: 'alliance', target: 'a'});
|
|
game = act(game, 'a', {type: 'alliance', target: 'f'});
|
|
const group = coalitionOf(game.players, game.coalitionState, 'f'); assert.ok(group);
|
|
game = act(game, 'f', {type: 'coalitionPropose', groupId: group.id, entrant: 'h',
|
|
expectedRevision: game.coalitionState!.revision});
|
|
for (const id of ['a', 'h']) {
|
|
const proposal = game.coalitionState!.proposal; assert.ok(proposal);
|
|
game = act(game, id, {type: 'coalitionAnswer', event: proposal.event, nexus: proposal.nexus,
|
|
turn: proposal.turn, accept: true, expectedRevision: game.coalitionState!.revision});
|
|
}
|
|
assert.deepEqual(coalitionOf(game.players, game.coalitionState, 'f')!.members, ['a', 'f', 'h']);
|
|
Object.assign(game, {phase: 4, nexus: false, ready: [], response: null, decision: null,
|
|
active: 'f', phaseOpening: null});
|
|
for (const player of game.players) {
|
|
player.forces = {}; player.reserves = 20; player.tanks = 0; player.spice = 10;
|
|
player.hand = []; player.traitors = []; player.traitorChoices = [];
|
|
}
|
|
for (const id of ['a', 'h']) {seat(game, id).reserves -= 4; seat(game, id).tanks += 4;}
|
|
game.revivalOrder = createRevivalOrder({turn: game.turn, order: game.order, ready: game.ready});
|
|
game = act(game, 'f', {type: 'ready'});
|
|
game = act(game, 'a', {type: 'setAutopilot', difficulty: 'Easy'});
|
|
game = act(game, 'h', {type: 'setAutopilot', difficulty: 'Hard'});
|
|
assert.equal(game.revivalOrder?.remaining[0], 'a');
|
|
return game;
|
|
}
|
|
|
|
void test('Advanced group release is recipient-specific across consecutive automated Revival turns', () => {
|
|
let game = fixture();
|
|
const original = structuredClone(game.revivalOrder);
|
|
game = runBots(game, 12);
|
|
assert.deepEqual(game.revivalOrder, original);
|
|
assert.equal(seat(game, 'a').revived, 0); assert.equal(seat(game, 'h').revived, 0);
|
|
game = act(game, 'f', {type: 'releaseFremenRevivalWait', target: 'a'});
|
|
const projection = viewGame(game, 'f').fremenGroupRevivalGrant;
|
|
assert.equal(projection?.find(row => row.recipient === 'a')?.waitReleased, true);
|
|
assert.ok(!projection?.find(row => row.recipient === 'h')?.waitReleased);
|
|
game = runBots(game, 30);
|
|
assert.ok(seat(game, 'a').revived > 0);
|
|
assert.ok(game.revivalOrder?.completed.includes('a'));
|
|
assert.equal(game.revivalOrder?.remaining[0], 'h');
|
|
assert.equal(seat(game, 'h').revived, 0); assert.equal(seat(game, 'h').tanks, 4);
|
|
assert.equal(game.freeRevival.includes('a'), false); assert.equal(game.freeRevival.includes('h'), false);
|
|
});
|