Files
Dune/tests/ecaz-homeworld-marker-victory.test.ts

92 lines
4.6 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { ecazHomeworldVictoryProgress } from '../game/ecaz-homeworld-victory';
import { homeworldContext, homeworldGameIntegrity } from '../game/homeworld-game';
import { homeworldForceGroups } from '../game/homeworld-custody';
import { homeworldOccupationMarker, observeHomeworldOccupation } from '../game/homeworld-occupation-history';
import { deployRicheseNoField, recallRicheseNoField, type NoFieldValue } from '../game/richese-no-field';
import type { Game } from '../game/engine';
import { ecazHomeworldFixture, ecazSeat } from './fixture-ecaz-homeworld-victory';
const world = 'homeworld:emperor';
/** The original fixture has native setup and a deliberately staged, conserved
* victory position. Exchange its one physical visitor for a real hidden token;
* no token denomination is added to the Homeworld force ledger. */
function replaceVisitorWithMarker(game: Game, value: NoFieldValue) {
const owner = ecazSeat(game, 'al');
const marker = owner.noField!.tokens.find(token => token.value === value)!;
assert.deepEqual(game.homeworlds!.custody!.visitors[world].al, { normal: 1, elite: 0 });
delete game.homeworlds!.custody!.visitors[world].al;
delete game.homeworlds!.custody!.visitors[world];
owner.reserves++;
owner.noField = deployRicheseNoField(owner.noField!, {
tokenId: marker.id, controller: owner.id, location: { homeworld: world },
});
observe(game, `controlled-marker-${value}`);
return marker;
}
function observe(game: Game, event: string) {
homeworldGameIntegrity(game);
game.homeworldOccupationHistory = observeHomeworldOccupation(game.homeworldOccupationHistory!,
homeworldContext(game), game.homeworlds!.custody!, game.turn, 'change', event,
homeworldOccupationMarker(game.players));
homeworldGameIntegrity(game);
}
function physicalInventory(game: Game) {
const worlds = homeworldForceGroups(homeworldContext(game), game.homeworlds!.custody!);
return Object.fromEntries(game.players.map(player => [player.id, player.reserves + player.tanks +
Object.values(player.forces).reduce((sum, n) => sum + n, 0) + worlds
.filter(home => home.native !== player.id)
.reduce((sum, home) => sum + (home.forces[player.id]?.normal ?? 0) + (home.forces[player.id]?.elite ?? 0), 0)]));
}
for (const advanced of [false, true]) {
void test(`${advanced ? 'Advanced' : 'Basic'} High Ecaz victory counts an ally's concealed Homeworld marker without its value`, () => {
const base = ecazHomeworldFixture(advanced, 'richese').game;
const before = ecazHomeworldVictoryProgress(base)!;
const inventory = physicalInventory(base);
assert.equal(before.qualifies, true);
const outcomes = ([0, 3, 5] as const).map(value => {
const game = structuredClone(base);
const marker = replaceVisitorWithMarker(game, value);
const row = ecazHomeworldVictoryProgress(game)!;
assert.equal(row.qualifies, true);
assert.deepEqual(row.foreignHomeworlds, before.foreignHomeworlds);
assert.equal(game.homeworlds!.custody!.visitors[world], undefined);
assert.deepEqual(physicalInventory(game), inventory);
assert.equal(JSON.stringify(row).includes(marker.id), false);
return row;
});
assert.deepEqual(outcomes[0], outcomes[1]);
assert.deepEqual(outcomes[1], outcomes[2]);
});
void test(`${advanced ? 'Advanced' : 'Basic'} High Ecaz requires a currently present marker even after prior occupation`, () => {
const game = ecazHomeworldFixture(advanced, 'richese').game;
const marker = replaceVisitorWithMarker(game, 3);
assert.equal(ecazHomeworldVictoryProgress(game)!.qualifies, true);
const owner = ecazSeat(game, 'al');
owner.noField = recallRicheseNoField(owner.noField!, marker.id);
observe(game, 'controlled-marker-departure');
const row = ecazHomeworldVictoryProgress(game)!;
assert.equal(row.qualifies, false);
assert.deepEqual(row.distinctNativeFactions, ['atreides']);
});
void test(`${advanced ? 'Advanced' : 'Basic'} an opposing World visitor contests a marker-only ally holding under native occupation rules`, () => {
const game = ecazHomeworldFixture(advanced, 'richese').game;
replaceVisitorWithMarker(game, 5);
const observer = ecazSeat(game, 'observer');
assert.ok(observer.reserves > 0);
observer.reserves--;
game.homeworlds!.custody!.visitors[world] = { observer: { normal: 1, elite: 0 } };
observe(game, 'controlled-marker-contest');
const row = ecazHomeworldVictoryProgress(game)!;
assert.equal(row.qualifies, advanced, 'Advanced keeps its original occupation while Basic requires current sole occupation.');
assert.equal(row.blocked, null);
});
}