78 lines
3.8 KiB
TypeScript
78 lines
3.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { applyAction, createGame, newPlayer, viewGame, type Game } from '../game/engine';
|
|
import { richeseRevealInterruptSupported } from '../game/richese-reveal-interruption';
|
|
import { createRicheseNoField, deployRicheseNoField } from '../game/richese-no-field';
|
|
import {
|
|
bindTruthMixedBribe,
|
|
createTruthMixedBribeFixture,
|
|
truthMixedBribeFact,
|
|
truthMixedBribeGroup,
|
|
truthMixedBribePayment,
|
|
} from './fixture-truth-mixed-bribe';
|
|
|
|
// The public question and residual are produced by the native mixed-question
|
|
// fixture. The pending auction parent, transfer of its records to a Richese
|
|
// table, marker, and alternate no-residual ledger are explicitly staged for
|
|
// this availability comparison; they are not a replay of the original game.
|
|
function stagedPair() {
|
|
const fixture = createTruthMixedBribeFixture();
|
|
const mixed = truthMixedBribeGroup('or', truthMixedBribeFact(fixture.hajrFact),
|
|
truthMixedBribePayment(fixture.asker));
|
|
const answered = bindTruthMixedBribe(fixture, 'yes', mixed).game;
|
|
const record = answered.truthHistory?.at(-1);
|
|
const residuals = answered.bribeExpressionPromises;
|
|
assert.ok(record && residuals?.length);
|
|
assert.equal(record.answer, 'yes');
|
|
assert.ok(residuals.some(row => !row.fulfilled && !row.released));
|
|
|
|
const base = createGame('REVEALPRIVACY', newPlayer('r', 'Richese', 'richese'), false, ['choam']);
|
|
base.players.push(...answered.players.map(player => newPlayer(player.id, player.name, player.faction)));
|
|
const richese = base.players[0];
|
|
richese.noField = deployRicheseNoField(createRicheseNoField(['zero', 'three', 'five']), {
|
|
tokenId: 'three', controller: 'r', location: { territory: 'polar_sink', sector: 0 },
|
|
});
|
|
richese.noFieldEvent = 'marker-event';
|
|
base.status = 'playing';
|
|
base.phase = 3;
|
|
base.turn = record.turn;
|
|
base.response = null;
|
|
base.phaseOpening = null;
|
|
base.decision = { kind: 'auctionPayment', player: fixture.target };
|
|
base.auction = { cards: [], index: 0, bid: 3, bidder: fixture.target, active: fixture.target, passed: [], opener: 0 };
|
|
base.truthHistory = [structuredClone(record)];
|
|
const withResidual = structuredClone(base);
|
|
withResidual.bribeExpressionPromises = structuredClone(residuals);
|
|
const withoutResidual = structuredClone(base);
|
|
withoutResidual.bribeExpressionPromises = [];
|
|
return { withResidual, withoutResidual, record };
|
|
}
|
|
|
|
void test('a public current-turn mixed answer uniformly hides private residual-dependent interruption availability', () => {
|
|
const { withResidual, withoutResidual, record } = stagedPair();
|
|
const baseline = structuredClone(withoutResidual);
|
|
baseline.truthHistory = [];
|
|
assert.equal(richeseRevealInterruptSupported(baseline), true, 'the staged economic parent is otherwise admitted');
|
|
for (const game of [withResidual, withoutResidual]) {
|
|
const before = structuredClone(game);
|
|
assert.deepEqual(viewGame(game, 'r').truthHistory, [record]);
|
|
assert.equal(richeseRevealInterruptSupported(game), false);
|
|
const info = viewGame(game, 'r').richeseNoField;
|
|
assert.ok(info?.private?.deployed);
|
|
assert.equal(info.canReveal, false);
|
|
assert.throws(() => applyAction(game, 'r', { type: 'revealNoField',
|
|
event: info.event, token: info.private!.deployed!.tokenId }));
|
|
assert.deepEqual(game, before);
|
|
}
|
|
assert.equal(viewGame(withResidual, 'r').richeseNoField!.revealBlock,
|
|
viewGame(withoutResidual, 'r').richeseNoField!.revealBlock);
|
|
});
|
|
|
|
void test('the public history fence applies only to the current turn', () => {
|
|
const { withoutResidual } = stagedPair();
|
|
const game: Game = structuredClone(withoutResidual);
|
|
game.turn++;
|
|
assert.equal(richeseRevealInterruptSupported(game), true);
|
|
assert.equal(viewGame(game, 'r').richeseNoField?.canReveal, true);
|
|
});
|