77 lines
4.5 KiB
TypeScript
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, []);
|
|
});
|