67 lines
3.5 KiB
TypeScript
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');
|
|
});
|