Files
Dune/tests/short-alliance-victory-runtime.test.ts
T
Quintine e896570f07
Verify and publish checkpoint container / container (push) Successful in 11m2s
Add optional short alliance victory
2026-10-28 21:40:24 +11:00

77 lines
4.5 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, createGame, joinControlledFaction, joinGame, newPlayer, viewGame, type Game } from '../game/engine';
import { quoteVictory } from '../game/victory-quote';
import { advanceShortAlliance, genuineShortAlliance, shortAllianceCollection, shortAllianceLobby,
type ShortAllianceStep } from './fixture-short-alliance-victory';
void test('only the host changes the optional target, clearing readiness and preserving the saved selection', () => {
let game = shortAllianceLobby();
assert.equal(viewGame(game, 'at').shortAllianceVictory, false);
game = applyAction(game, 'at', { type: 'ready' });
game = applyAction(game, 'em', { type: 'ready' });
const before = structuredClone(game);
assert.throws(() => applyAction(game, 'em', { type: 'shortAllianceVictory', enabled: true }));
assert.throws(() => applyAction(game, 'at', { type: 'shortAllianceVictory', enabled: 'true' }));
assert.deepEqual(game, before);
game = applyAction(game, 'at', { type: 'shortAllianceVictory', enabled: true });
assert.equal(game.shortAllianceVictory, true);
assert.ok(game.players.every(player => !player.ready));
for (const player of game.players) assert.equal(viewGame(game, player.id).shortAllianceVictory, true);
const restored = JSON.parse(JSON.stringify(game)) as Game;
assert.equal(viewGame(restored, 'em').shortAllianceVictory, true);
game = applyAction(restored, 'at', { type: 'shortAllianceVictory', enabled: false });
assert.equal(viewGame(game, 'at').shortAllianceVictory, false);
assert.equal(game.shortAllianceVictory, undefined);
});
for (const enabled of [false, true]) {
void test(`Basic genuine Nexus alliance ${enabled ? 'wins' : 'does not win'} three strongholds under ${enabled ? 'short' : 'default'} rules`, () => {
const native = genuineShortAlliance(enabled);
assert.ok(native.actions.some(step => step.action.type === 'alliance' && step.actor === 'at'));
assert.ok(native.actions.some(step => step.action.type === 'alliance' && step.actor === 'em'));
const game = shortAllianceCollection(native.game);
const before = structuredClone(game);
const progress = viewGame(game, 'at').victoryProgress.find(row => row.player === 'at');
assert.ok(progress);
assert.deepEqual(new Set(progress.members), new Set(['at', 'em']));
assert.equal(progress.strongholds.length, 3);
assert.equal(progress.target, enabled ? 3 : 4);
assert.equal(progress.qualifies, enabled);
const quote = quoteVictory(game);
assert.deepEqual(new Set(quote.winner), new Set(enabled ? ['at', 'em'] : []));
let current = game;
for (const player of game.players) {
if (current.status === 'finished') break;
current = applyAction(current, player.id, { type: 'ready' });
}
assert.deepEqual(game, before, 'Checking and resolving must not mutate the staged source.');
assert.equal(current.status, enabled ? 'finished' : 'playing');
assert.deepEqual(new Set(current.winner), new Set(enabled ? ['at', 'em'] : []));
assert.equal(viewGame(JSON.parse(JSON.stringify(current)), 'at').shortAllianceVictory, enabled);
});
}
void test('two real controllers with four faction seats retain the four-stronghold target', () => {
let game = createGame('TWOALLIANCE', newPlayer('at', 'Atreides', 'atreides'));
game = applyAction(game, 'at', { type: 'twoPlayerMode', enabled: true });
joinControlledFaction(game, newPlayer('em', 'Emperor', 'emperor'), 'at');
joinGame(game, newPlayer('gu', 'Guild', 'guild'));
joinControlledFaction(game, newPlayer('fr', 'Fremen', 'fremen'), 'gu');
game = applyAction(game, 'at', { type: 'shortAllianceVictory', enabled: true });
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
game = applyAction(game, 'at', { type: 'start' });
const actions: ShortAllianceStep[] = [];
game = advanceShortAlliance(game, state => state.status === 'playing', actions);
// This controlled two-person boundary stages reciprocal alliance and board
// positions; the other cases above exercise an actual Nexus alliance.
game.players.find(player => player.id === 'at')!.ally = 'em';
game.players.find(player => player.id === 'em')!.ally = 'at';
game = shortAllianceCollection(game);
const progress = viewGame(game, 'at').victoryProgress.find(row => row.player === 'at');
assert.ok(progress);
assert.equal(progress.target, 4);
assert.equal(progress.qualifies, false);
assert.deepEqual(quoteVictory(game).winner, []);
});