Files
Dune/tests/duke-public-death-uncertainty.test.ts
T
Quintine 59cb336c80
Verify and publish checkpoint container / container (push) Canceled after 0s
Resolve Duke uncertainty on public events
2026-10-29 19:30:00 +11:00

66 lines
3.8 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, createGame, newPlayer, type Game } from '../game/engine';
import { baseDeck } from '../game/cards';
import { acquireDuke, createDukeVidal, DUKE_VIDAL_ID } from '../game/duke-vidal';
import { richeseCards } from '../game/richese-cards';
import { moritaniTerrorAssassinationPool } from '../game/moritani-terror-assassination';
import { createMoritaniDukeCaptureFixture, revealMoritaniTerrorVictim } from './fixture-moritani-duke-capture';
function mark(game: Game, marker: 'uncertain' | 'legacy') {
if (marker === 'uncertain') game.dukeCaptureUncertain = true;
else delete game.dukeCaptureUncertain;
}
/** Staged ordinary pre-battle source based on the Residual Poison review
* fixture. One real card and the one shared Duke are placed in their physical
* zones; native leader deaths are controlled so the random pool is singleton. */
function residual(victim: 'duke' | 'native'): Game {
let game = createGame('DUKERESIDUAL', newPlayer('r', 'Richese', 'richese'), false, ['choam']);
game.players.push(newPlayer('g', 'Guild', 'guild'), newPlayer('e', 'Emperor', 'emperor'));
Object.assign(game, {status:'playing',phase:6,turn:2,active:'r',order:['r','g','e'],storm:18,
deck:baseDeck(),richeseCache:richeseCards()});
for (const player of game.players) { player.hand = []; player.spice = 10; player.forces = {}; player.reserves = 20; }
for (const player of game.players.slice(0, 2)) { player.forces = {'arrakeen:10':2}; player.reserves = 18; }
const cache = game.richeseCache!, index = cache.findIndex(card => card.effect === 'residualPoison');
assert.ok(index >= 0); game.players[0].hand.push(cache.splice(index, 1)[0]);
const guild = game.players[1];
for (const leader of guild.leaders) { leader.dead = true; leader.deaths = 1; }
if (victim === 'native') { guild.leaders[0].dead = false; guild.leaders[0].deaths = 0; }
game.dukeVidal = acquireDuke(createDukeVidal(), 'g', 2, 'moritani');
if (victim === 'native') game.dukeVidal.leader.usedAt = 'carthag';
game = applyAction(game, 'r', {type:'chooseBattle', target:'g', territory:'arrakeen'});
return game;
}
for (const state of ['uncertain', 'legacy'] as const) {
void test(`public Moritani Terror Duke death resolves ${state} uncertainty`, () => {
const f = createMoritaniDukeCaptureFixture({terror:true});
mark(f.game, state);
const {after} = revealMoritaniTerrorVictim(f, DUKE_VIDAL_ID);
assert.equal(after.dukeVidal!.leader.dead, true);
assert.equal(after.dukeCaptureUncertain, false);
assert.ok(after.log.some(entry => entry.text.includes('Duke Prad Vidal was assassinated')));
});
void test(`Moritani Terror killing another leader preserves ${state} uncertainty`, () => {
const f = createMoritaniDukeCaptureFixture({terror:true});
mark(f.game, state);
const pool = moritaniTerrorAssassinationPool(f.game, f.captor);
const other = pool.find(leader => leader.id !== DUKE_VIDAL_ID); assert.ok(other);
const {after} = revealMoritaniTerrorVictim(f, other.id);
assert.equal(after.dukeCaptureUncertain, state === 'uncertain' ? true : undefined);
assert.equal(after.dukeVidal!.leader.dead, false);
});
for (const victim of ['duke', 'native'] as const) {
void test(`public Residual Poison ${victim} death ${victim === 'duke' ? 'resolves' : 'preserves'} ${state} uncertainty`, () => {
const game = residual(victim); mark(game, state);
const after = applyAction(game, 'r', {type:'card',card:'richese-residual-poison',target:'g',event:game.battle!.event});
assert.equal(after.dukeCaptureUncertain, victim === 'duke' ? false : state === 'uncertain' ? true : undefined);
assert.equal(after.dukeVidal!.leader.dead, victim === 'duke');
assert.equal(after.log.some(entry => entry.text.includes('Duke Prad Vidal was randomly selected')), victim === 'duke');
});
}
}