Files
Dune/tests/ecaz-duke-capture-policy.test.ts
T
Quintine 57e7611468
Verify and publish checkpoint container / container (push) Successful in 11m21s
Guard Duke grants using public capture uncertainty
2026-10-29 19:22:46 +11:00

70 lines
3.7 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createGame, newPlayer, type Game } from '../game/engine';
import { acquireDuke, captureDuke, createDukeVidal } from '../game/duke-vidal';
import { ecazDukeAcquisitionBlock, quoteEcazDukeAcquisition } from '../game/ecaz-duke-acquisition';
import { DUKE_CAPTURE_UNCERTAIN_BLOCK, dukeCaptureUncertaintyBlock, plainAdvancedEcazDukeProfile } from '../game/ecaz-duke-capture-policy';
function table(): Game {
const game = createGame('DUKEPOLICY', newPlayer('e', 'Ecaz', 'ecaz'), true, ['ecaz']);
game.players.push(newPlayer('h', 'Harkonnen', 'harkonnen'), newPlayer('g', 'Guild', 'guild'));
game.status = 'playing'; game.turn = 2; game.dukeVidal = createDukeVidal();
game.dukeCaptureUncertain = false;
return game;
}
void test('a known-safe plain Advanced table can acquire the original living Duke', () => {
const game = table(), before = structuredClone(game);
assert.equal(plainAdvancedEcazDukeProfile(game), true);
assert.equal(dukeCaptureUncertaintyBlock(game), null);
const next = quoteEcazDukeAcquisition(game, 'e');
assert.equal(next.controller, 'e'); assert.equal(next.leader.id, game.dukeVidal!.leader.id);
assert.deepEqual(game, before, 'A quote does not change custody.');
const ecaz = game.players.find(player => player.id === 'e')!;
const guild = game.players.find(player => player.id === 'g')!;
ecaz.ally = 'g'; guild.ally = 'e';
assert.equal(quoteEcazDukeAcquisition(game, 'e', 'g').controller, 'g');
});
void test('uncertain and legacy Advanced tables block all hidden custody identically', () => {
const free = table();
free.dukeVidal = acquireDuke(free.dukeVidal!, 'g', free.turn, 'ecaz');
const captured = structuredClone(free);
captured.dukeVidal = captureDuke(captured.dukeVidal!, 'h', 'g', captured.turn, 'arrakeen');
for (const uncertain of [true, undefined] as const) {
for (const game of [free, captured]) {
if (uncertain === undefined) delete game.dukeCaptureUncertain;
else game.dukeCaptureUncertain = uncertain;
const before = structuredClone(game);
assert.equal(ecazDukeAcquisitionBlock(game, 'e'), DUKE_CAPTURE_UNCERTAIN_BLOCK);
assert.throws(() => quoteEcazDukeAcquisition(game, 'e'));
assert.deepEqual(game, before);
}
}
});
void test('uncertainty does not change Basic or tables without Harkonnen', () => {
const basic = table(); basic.advanced = false; delete basic.dukeCaptureUncertain;
assert.equal(dukeCaptureUncertaintyBlock(basic), null);
assert.equal(quoteEcazDukeAcquisition(basic, 'e').controller, 'e');
const noHarkonnen = table(); noHarkonnen.players = noHarkonnen.players.filter(player => player.id !== 'h');
delete noHarkonnen.dukeCaptureUncertain;
assert.equal(dukeCaptureUncertaintyBlock(noHarkonnen), null);
assert.equal(quoteEcazDukeAcquisition(noHarkonnen, 'e').controller, 'e');
});
void test('the plain profile keeps optional modules separate and validates the selected roster', () => {
const game = table();
assert.equal(plainAdvancedEcazDukeProfile(game), true);
const mixed = structuredClone(game); mixed.expansions.push('ix');
mixed.players.push(newPlayer('i', 'Ixians', 'ixians'));
assert.equal(plainAdvancedEcazDukeProfile(mixed), true);
mixed.sandtrout = true;
assert.equal(plainAdvancedEcazDukeProfile(mixed), true, 'Active native Sandtrout is not an optional selection.');
mixed.leaderSkills = {} as NonNullable<Game['leaderSkills']>;
assert.equal(plainAdvancedEcazDukeProfile(mixed), false);
assert.match(ecazDukeAcquisitionBlock(mixed, 'e')!, /Advanced Harkonnen/);
delete mixed.leaderSkills; mixed.expansions = ['ecaz'];
assert.equal(plainAdvancedEcazDukeProfile(mixed), false, 'An unselected expansion cannot seat Ixians.');
});