Files
Dune/tests/fixture-selected-independent-nexus-entry.ts
T
Quintine 8422cf1f2d Compose selected E1/E2 independent cards with Nexus rules
Retain selected decks and original optional modules through normal setup, distinguish native Ixian Betrayal from absent-faction replacement, route explicit CHOAM fuel before printed effects, and preserve native No-Field invoices and pair continuation. Connect canonical native cancellation stock and existing human/minimal AI consumers.

Validation: 194 affected tests across 19 files, types/lint/build, bounded native invoice review and seven finite original-action/rendered-control programmes. Runtime ec5ad6c18996d8d958ec0d01132ef3e3ea6d23d36188902eca9a63d4db2efbf8; documentation-only proof overlay a5892004937434429916496497c07a3838a324b4a5d64d4a0eb804ab5212d5bd. Other envelopes, full combinations, comprehensive assurance and deployed acceptance remain open. No saved-room/provider changes.
2026-10-18 10:46:21 +11:00

50 lines
2.4 KiB
TypeScript

import assert from 'node:assert/strict';
import { applyAction, createGame, joinGame, newPlayer, viewGame, type Action, type Game } from '../game/engine';
import { botActions } from '../game/bots';
import type { FactionId } from '../game/catalog';
export type SelectedIndependentEntryOptions = {
expansions: Game['expansions'];
factions: readonly FactionId[];
advanced?: boolean;
skills?: boolean;
homeworlds?: boolean;
tech?: boolean;
strongholds?: boolean;
};
/** Ordinary host controls and original private choices; no dealt inventory edits. */
export function createSelectedIndependentEntry(options: SelectedIndependentEntryOptions) {
const [host, ...others] = options.factions;
let game = createGame('SELECTEDINDEPENDENTNEXUS', newPlayer(host, host, host), options.advanced ?? false, options.expansions);
for (const faction of others) joinGame(game, newPlayer(faction, faction, faction));
const actions: {actor: string; action: Action}[] = [];
const act = (actor: string, action: Action) => {
actions.push(structuredClone({actor, action}));
game = applyAction(game, actor, action);
};
act(host, {type:'moduleOption', module:'ecazTreachery', enabled:true});
act(host, {type:'moduleOption', module:'nexusCards', enabled:true});
if (options.skills) act(host, {type:'moduleOption', module:'leaderSkills', enabled:true});
if (options.homeworlds) act(host, {type:'homeworlds', enabled:true});
if (options.tech) act(host, {type:'techTokens', enabled:true});
if (options.strongholds) act(host, {type:'strongholdCards', enabled:true});
for (const player of game.players) act(player.id, {type:'ready'});
const lobby = structuredClone(game);
act(host, options.advanced ? {type:'start', advancedPreview:true} : {type:'start'});
const setup = structuredClone(game);
for (let count = 0; game.status === 'setup' && count < 100; count++) {
let next: {actor:string; action:Action} | undefined;
for (const player of game.players) {
const view = viewGame(game, player.id);
view.players.find(seat => seat.id === player.id)!.bot = 'Easy';
const action = botActions(view)[0];
if (action) { next = {actor:player.id, action}; break; }
}
assert.ok(next, `Original ${game.setupStage} setup must have an owned legal choice`);
act(next.actor, next.action);
}
assert.equal(game.status, 'playing');
return {lobby, setup, game, actions};
}