Files
Dune/tests/fixture-truth-future-battle-leader.ts
Quintine 02695aea7c
Verify and publish checkpoint container / container (push) Failing after 11m11s
Prototype conditional future battle Truthtrance
2026-10-28 21:17:44 +11:00

76 lines
4.7 KiB
TypeScript

import assert from 'node:assert/strict';
import { viewGame, type Action, type Game } from '../game/engine';
import {
actTruthTraitorDecision, advanceTruthTraitorDecision, createTruthTraitorDecisionFixture,
truthTraitorDecisionClean, truthTraitorDecisionInventory, truthTraitorDecisionPlayer,
type TruthTraitorDecisionStep,
} from './fixture-truth-traitor-decision';
export const actFutureBattleLeader = actTruthTraitorDecision;
export const advanceFutureBattleLeader = advanceTruthTraitorDecision;
export const futureBattleLeaderClean = truthTraitorDecisionClean;
export const futureBattleLeaderInventory = truthTraitorDecisionInventory;
export const futureBattleLeaderPlayer = truthTraitorDecisionPlayer;
export type FutureBattleLeaderStep = TruthTraitorDecisionStep;
export type FutureBattleLeaderRecord = {
turn: number; asker: string; player: string; leader: string; answer: boolean;
battle?: string; status: 'waiting' | 'bound' | 'settled' | 'expired' | 'released';
};
export function futureBattleLeaderRecords(game: Game): FutureBattleLeaderRecord[] {
return (game as Game & { futureBattleLeaderPromises?: FutureBattleLeaderRecord[] }).futureBattleLeaderPromises ?? [];
}
/** Reuse the source's genuinely earned Basic Movement boundary. Only its
* original unopened card/spice order and existing scalar lotteries were
* selected; no battle, private plan or answer is installed. */
export function createFutureBattleLeaderFixture() {
const base = createTruthTraitorDecisionFixture();
const game = structuredClone(base.beforeShipment);
assert.equal(game.turn, 1); assert.equal(game.phase, 5); assert.equal(game.active, base.target);
assert.equal(game.advanced, false); assert.deepEqual(game.expansions, []);
assert.ok(!game.battle && futureBattleLeaderClean(game));
assert.ok(futureBattleLeaderPlayer(game, base.asker).hand.some(card => card.id === base.truth));
const leader = base.leaders.emperor;
const otherLeader = futureBattleLeaderPlayer(game, base.target).leaders.find(item => item.id !== leader && !item.dead && !item.usedAt);
assert.ok(otherLeader);
assert.deepEqual(futureBattleLeaderInventory(game), base.originalCards);
const question = { kind: 'futureBattleLeader', target: base.target, turn: game.turn, leader };
const ask = { type: 'truthAsk', question } as unknown as Action;
return { base, game, source: structuredClone(game), question, ask, leader, otherLeader: otherLeader.id,
asker: base.asker, target: base.target, truth: base.truth, originalCards: base.originalCards,
targetPlan: base.targetPlan, ownPlan: base.ownPlan, territory: base.territory,
shipmentStep: base.shipmentStep };
}
export type FutureBattleLeaderFixture = ReturnType<typeof createFutureBattleLeaderFixture>;
export function declareFutureBattleLeader(fixture: FutureBattleLeaderFixture): Game {
let game = actFutureBattleLeader(fixture.game, fixture.asker, { type: 'card', card: fixture.truth });
for (let count = 0; game.truthtrance?.stage === 'priority' && count < game.players.length; count++) {
const player = game.players.find(seat => !game.truthtrance!.passed.includes(seat.id)); assert.ok(player);
game = actFutureBattleLeader(game, player.id, { type: 'truthPass' });
}
assert.equal(game.truthtrance?.stage, 'ask');
return actFutureBattleLeader(game, fixture.asker, fixture.ask);
}
export function answerFutureBattleLeader(game: Game, fixture: FutureBattleLeaderFixture, answer: 'yes' | 'no'): Game {
return actFutureBattleLeader(game, fixture.target, { type: 'truthAnswer', answer });
}
/** Use the source's actual paid shipment, then the native choice and
* preparation controls; the caller alone seals plans and traitor votes. */
export function reachFutureBattle(game: Game, fixture: FutureBattleLeaderFixture): Game {
game = actFutureBattleLeader(game, fixture.shipmentStep.actor, fixture.shipmentStep.action);
game = advanceFutureBattleLeader(game, futureBattleLeaderClean);
game = actFutureBattleLeader(game, fixture.target, { type: 'endMovement' });
game = advanceFutureBattleLeader(game, state => state.phase === 6 && futureBattleLeaderClean(state) && !!state.active && !state.battle);
const chooser = game.active!;
const choice = viewGame(game, chooser).battleChoices.find(row => row.territory === fixture.territory &&
[row.attacker, row.defender].includes(fixture.target));
assert.ok(choice);
game = actFutureBattleLeader(game, choice.chooser, { type: 'chooseBattle', territory: choice.territory,
target: choice.attacker === choice.chooser ? choice.defender : choice.attacker });
assert.ok(game.battle?.event);
return advanceFutureBattleLeader(game, state => !!state.battle && futureBattleLeaderClean(state) &&
!state.battle.preparation && state.battle.preLeader?.closed !== false);
}