Files
Dune/tests/truthtrance-freeform-v3-runtime.test.ts
T
Quintine 7a083ac9aa
Verify and publish checkpoint container / container (push) Failing after 10m53s
Extend Truthtrance facts and Basic Semuta Tech
2026-10-29 05:15:04 +11:00

67 lines
3.5 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, viewGame, type Game } from '../game/engine';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import { moveSemutaEmperorCard } from './fixture-semuta-emperor-share';
import { knowledgeGame, knowledgePhysical, rejectKnowledge } from './truthtrance-knowledge-fixture';
/** Actual Truthtrance actions after conserving the target's staged hand. */
function pending(hasWeapon: boolean, scope: 'fact' | 'currentTurn' = 'fact'): Game {
let game = knowledgeGame();
const target = game.players.find(seat => seat.id === 'b')!;
for (const card of target.hand.slice()) moveSemutaEmperorCard(game, card.id, 'f');
if (hasWeapon) {
const weapon = [...game.deck, ...game.discard, ...game.players.flatMap(seat => seat.hand)]
.find(card => card.kind === 'projectile');
assert.ok(weapon); moveSemutaEmperorCard(game, weapon.id, target.id);
}
const truth = game.players[0].hand.find(card => card.effect === 'truthtrance');
assert.ok(truth);
game = applyAction(game, 'a', { type: 'card', card: truth.id });
while (game.truthtrance?.stage === 'priority') {
const seat = game.players.find(row => !game.truthtrance!.passed.includes(row.id));
assert.ok(seat); game = applyAction(game, seat.id, { type: 'truthPass' });
}
return applyAction(game, 'a', { type: 'truthAsk', question: {
kind: 'freeform', target: 'b', scope, text: 'Do you have a weapon?',
} });
}
for (const hasWeapon of [true, false]) {
void test(`a category question checks the target's current primary weapon role: ${hasWeapon}`, () => {
const game = pending(hasWeapon), answer = hasWeapon ? 'yes' : 'no';
const question = game.truthtrance!.question;
assert.equal(question?.kind, 'freeform');
if (question?.kind !== 'freeform') throw Error('Expected freeform question');
assert.deepEqual(question.interpreted, { version: 3,
fact: { kind: 'handInventory', category: 'weapon', compare: 'gte', value: 1 } });
assert.equal(viewGame(game, 'b').truthAnswer, answer);
for (const observer of ['a', 'f', 'o']) assert.equal(viewGame(game, observer).truthAnswer, null);
rejectKnowledge(game, 'b', { type: 'truthAnswer', answer: hasWeapon ? 'no' : 'yes' });
const physical = knowledgePhysical(game);
for (const difficulty of DIFFICULTIES) {
const view = viewGame(game, 'b');
view.players.find(seat => seat.id === 'b')!.bot = difficulty;
assert.deepEqual(botActions(view), [{ type: 'truthAnswer', answer }]);
}
const restored = JSON.parse(JSON.stringify(game)) as Game;
const done = applyAction(restored, 'b', { type: 'truthAnswer', answer });
assert.equal(done.truthHistory!.at(-1)!.answer, answer);
assert.deepEqual(knowledgePhysical(done), physical);
});
}
void test('unstamped and current-turn category questions keep their prior manual semantics', () => {
const currentTurn = pending(true, 'currentTurn');
assert.equal(currentTurn.truthtrance?.question?.kind, 'freeform');
assert.equal(currentTurn.truthtrance?.question?.kind === 'freeform' && currentTurn.truthtrance.question.interpreted, undefined);
const legacy = pending(true);
const question = legacy.truthtrance!.question;
assert.equal(question?.kind, 'freeform');
if (question?.kind !== 'freeform') throw Error('Expected freeform question');
delete question.interpreted;
assert.equal(viewGame(legacy, 'b').truthAnswer, null);
assert.equal(applyAction(legacy, 'b', { type: 'truthAnswer', answer: 'no' }).truthHistory!.at(-1)!.answer, 'no');
});