97 lines
6.0 KiB
TypeScript
97 lines
6.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import type { Game } from '../game/engine';
|
|
import {
|
|
actTruthBribe, advanceTruthBribe, answerTruthBribe, askTruthBribe, createTruthBribeFixture,
|
|
declareTruthBribe, finishTruthBribe, nextTruthBribe, settleTruthBribe, truthBribeBalances,
|
|
truthBribeClean, truthBribeDeferred, truthBribeInventory, truthBribePlayer,
|
|
type TruthBribeStep, type TruthBribeTransition,
|
|
} from './fixture-truth-bribe';
|
|
|
|
export type MentatBribeStep = TruthBribeStep;
|
|
export type MentatBribeTransition = TruthBribeTransition;
|
|
export const actMentatBribe = actTruthBribe;
|
|
export const nextMentatBribe = nextTruthBribe;
|
|
export const advanceMentatBribe = advanceTruthBribe;
|
|
export const settleMentatBribe = settleTruthBribe;
|
|
export const finishMentatBribe = finishTruthBribe;
|
|
export const mentatBribeClean = truthBribeClean;
|
|
export const mentatBribeInventory = truthBribeInventory;
|
|
export const mentatBribePlayer = truthBribePlayer;
|
|
export const mentatBribeBalances = truthBribeBalances;
|
|
export const mentatBribeDeferred = truthBribeDeferred;
|
|
export const declareMentatBribe = declareTruthBribe;
|
|
export const askMentatBribe = askTruthBribe;
|
|
export const answerMentatBribe = answerTruthBribe;
|
|
|
|
/** Reach the current turn's actual Mentat from earlier phases, or the next
|
|
* actual opening when this turn's collection has already happened. */
|
|
export function nextMentatBribeOpening(game: Game, actions: MentatBribeStep[] = []): Game {
|
|
const turn = game.turn + Number(game.phase === 8);
|
|
return advanceMentatBribe(game, state => state.turn === turn && state.phase === 8 && mentatBribeClean(state), actions);
|
|
}
|
|
|
|
/** The inherited original H/A/E base33 source earns Collection and Mentat by
|
|
* real native actions. H still owns both original, unplayed Truthtrances and E
|
|
* its original Shield and ten spendable spice. No played state is installed. */
|
|
export function createMentatBribeFixture(options: { advanced?: boolean } = {}) {
|
|
const base = createTruthBribeFixture({ advanced: options.advanced, phase: 7 });
|
|
const actions = structuredClone(base.sourceActions), before = structuredClone(base.game), from = actions.length;
|
|
const game = nextMentatBribeOpening(base.game, actions);
|
|
const phase7: MentatBribeTransition = {
|
|
before: structuredClone(base.base.game),
|
|
steps: structuredClone(base.sourceActions.slice(base.base.sourceActionCount)),
|
|
after: structuredClone(base.game),
|
|
};
|
|
const mentat: MentatBribeTransition = {
|
|
before, steps: structuredClone(actions.slice(from)), after: structuredClone(game),
|
|
};
|
|
assert.equal(game.phase, 8); assert.equal(game.turn, 1); assert.ok(mentatBribeClean(game));
|
|
assert.equal(mentatBribePlayer(game, base.payer).spice, 10);
|
|
assert.ok(game.players.every(player => player.bribes === 0 && player.ally === null));
|
|
assert.deepEqual(mentatBribePlayer(game, base.asker).hand.map(card => card.id), base.truths);
|
|
assert.deepEqual(mentatBribeInventory(game), base.originalCards);
|
|
return { ...base, game, sourceGame: structuredClone(game), base, phase: 8 as const, phase7, mentat,
|
|
originalBalances: mentatBribeBalances(game), originalDeferred: mentatBribeDeferred(game),
|
|
actions, sourceActions: structuredClone(actions), sourceActionCount: actions.length,
|
|
staging: [] as string[],
|
|
sourceDisclosure: 'Inherited genuine public original H/A/E base33 setup, ordinary auction and native Collection/Mentat actions. H retains both original unplayed Truthtrances; E retains its original Shield and10spice. Only inherited unopened deal/lot/Spice positions and scalar lotteries are selected. The current Mentat opening already collected its receipts. A new payment spends the payer now and waits in the exact recipient bribes until the next actual Mentat opening. No played clock, purse, hand, receipt or promise is installed.' };
|
|
}
|
|
export type MentatBribeFixture = ReturnType<typeof createMentatBribeFixture>;
|
|
|
|
export function bindMentatBribe(f: MentatBribeFixture, answer: 'yes' | 'no' = 'yes', minimum = f.minimum,
|
|
recipient = f.recipient, state = f.game) {
|
|
const declared = declareMentatBribe(f, state), actions = [...declared.actions];
|
|
const asked = askMentatBribe(declared.game, f.asker, f.target, recipient, minimum, actions);
|
|
const game = answerMentatBribe(asked, f.target, answer, actions);
|
|
return { game, declared, asked, actions, actionCount: actions.length };
|
|
}
|
|
|
|
/** Keep next Storm and ordinary Collection visible before the next real
|
|
* Mentat opening. Incoming escrow cannot masquerade as cash along this path. */
|
|
export function completeMentatBribe(f: MentatBribeFixture, state = f.game) {
|
|
assert.equal(state.phase, 8);
|
|
let game = state; const turn = game.turn, actions: MentatBribeStep[] = [];
|
|
const transition = (until: (state: Game) => boolean): MentatBribeTransition => {
|
|
const before = structuredClone(game), from = actions.length;
|
|
game = advanceMentatBribe(game, until, actions);
|
|
return { before, steps: structuredClone(actions.slice(from)), after: structuredClone(game) };
|
|
};
|
|
const nextTurn = transition(state => state.turn === turn + 1 && state.phase === 0 && mentatBribeClean(state));
|
|
const collection = transition(state => state.turn === turn + 1 && state.phase === 7 && mentatBribeClean(state));
|
|
const mentat = transition(state => state.turn === turn + 1 && state.phase === 8 && mentatBribeClean(state));
|
|
assert.deepEqual(mentatBribeInventory(game), f.originalCards);
|
|
return { game, nextTurn, collection, mentat, actions, actionCount: actions.length };
|
|
}
|
|
|
|
/** Move Collection readiness explicitly so an unpaid Yes is carried into
|
|
* Mentat instead of automatically consuming the adapter's suggested payment. */
|
|
export function enterMentatBribeWithUnpaidPromise(game: Game, actions: MentatBribeStep[] = []): Game {
|
|
assert.equal(game.phase, 7); assert.ok(mentatBribeClean(game));
|
|
const turn = game.turn;
|
|
for (const player of game.players) {
|
|
if (game.phase === 7 && !game.ready.includes(player.id))
|
|
game = actMentatBribe(game, player.id, { type: 'ready' }, actions);
|
|
}
|
|
return advanceMentatBribe(game, state => state.turn === turn && state.phase === 8 && mentatBribeClean(state), actions);
|
|
}
|