Files
Dune/tests/fixture-bureaucrat-qualifying-split.ts

75 lines
3.7 KiB
TypeScript

import assert from 'node:assert/strict';
import { applyAction, createGame, joinGame, newPlayer, type Action, type Game } from '../game/engine';
import { initializeClassicNexusSkillsPaymentsSetup, nextClassicNexusSkillsPaymentsNativeStep } from './fixture-classic-nexus-skills-payments';
export type BureaucratQualifyingSplitStep = { actor: string; action: Action };
const clean = (game: Game) => !game.phaseOpening && !game.response && !game.decision;
/** Keep every original army and wallet. A real three-spice bribe collects into
* Fremen's shield at the first Mentat; the actual second-turn worm forms H/F. */
export function createBureaucratQualifyingSplitSetup(advanced = false) {
const lobby = createGame('BUREAUCRATQUALIFY', newPlayer('b', 'Bureaucrat', 'atreides'), advanced);
for (const [id, faction] of [['p', 'harkonnen'], ['f', 'fremen'], ['e', 'emperor'], ['g', 'guild']] as const)
joinGame(lobby, newPlayer(id, faction, faction));
let game = initializeClassicNexusSkillsPaymentsSetup({ initial: lobby, skill: 'bureaucrat' });
const actions: BureaucratQualifyingSplitStep[] = [];
const staging: string[] = [];
const act = (actor: string, action: Action) => {
actions.push({ actor, action });
game = applyAction(game, actor, action);
};
const advance = (until: (state: Game) => boolean) => {
for (let n = 0; !until(game) && n < 2400; n++) {
assert.notEqual(game.status, 'finished');
const next = nextClassicNexusSkillsPaymentsNativeStep(game);
assert.ok(next, 'Original skill and phase controls must reach the payment boundary');
act(next.actor, next.action);
}
assert.ok(until(game));
};
const orderSpice = (kinds: ('land' | 'worm')[]) => {
for (const [position, kind] of kinds.entries()) {
const index = game.spiceDeck.findIndex((card, i) => i >= position &&
(kind === 'land' ? 'territory' in card : 'worm' in card));
assert.ok(index >= position);
game.spiceDeck.splice(position, 0, game.spiceDeck.splice(index, 1)[0]);
}
staging.push('Reorder only original still-unplayed Spice Cards.');
};
advance(state => state.status === 'playing');
const setup = structuredClone(game);
assert.ok(game.leaderSkills!.assignments.some(a => a.owner === 'b' && a.skill === 'bureaucrat'));
orderSpice(['land', 'land']);
advance(state => state.phase === 5 && state.active === 'p' && clean(state));
const beforeBribe = structuredClone(game);
act('p', { type: 'bribe', target: 'f', amount: 3 });
const bribe = structuredClone(game);
assert.equal(game.players.find(p => p.id === 'f')!.bribes, 3);
advance(state => state.turn === 2 && state.phase === 1 && clean(state));
const afterFirstMentat = structuredClone(game);
assert.equal(game.players.find(p => p.id === 'f')!.spice, 6);
orderSpice(['worm', 'land', 'land']);
advance(state => state.nexus && !state.spiceWindow && !state.spiceResolution && clean(state));
const worm = structuredClone(game);
act('p', { type: 'alliance', target: 'f' });
act('f', { type: 'alliance', target: 'p' });
const alliance = structuredClone(game);
advance(state => state.phase === 3 && !!state.auction && clean(state));
return { game, setup, beforeBribe, bribe, afterFirstMentat, worm, alliance,
payer: 'p', donor: 'f', trainer: 'b', guild: 'g', emperor: 'e', actions, staging };
}
export function advanceBureaucratQualifyingSplit(state: Game, until: (game: Game) => boolean,
actions?: BureaucratQualifyingSplitStep[]): Game {
let game = state;
for (let n = 0; !until(game) && n < 1600; n++) {
assert.notEqual(game.status, 'finished');
const next = nextClassicNexusSkillsPaymentsNativeStep(game);
assert.ok(next);
actions?.push(next);
game = applyAction(game, next.actor, next.action);
}
assert.ok(until(game));
return game;
}