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, []); });