Files
Dune/tests/truthtrance-cache-lot-advanced.test.ts
T
Quintine 8ae275f598
Verify and publish checkpoint container / container (push) Successful in 10m38s
Interpret bounded Truthtrance fact questions
2026-10-29 04:31:15 +11:00

101 lines
5.4 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import { newPlayer, viewGame } from '../game/engine';
import { TRUTH_CARD_NAMES } from '../game/truthtrance-card-names';
import { knowledgeFactAnswer, truthKnowledgeOf } from '../game/truthtrance-knowledge';
import {
actTruthtranceRichesePromises as act,
advanceTruthtranceRichesePromises as advance,
askTruthtranceRichesePromises as ask,
createTruthtranceRichesePromisesFixture as fixture,
passTruthRicheseFreshDiscard,
truthtranceRichesePromisesClean as clean,
truthtranceRichesePromisesInventory as inventory,
} from './fixture-truthtrance-richese-promises';
/** Public Advanced Richese setup and actual cache auction; the shared fixture
* orders unopened deal and Spice cards but does not forge the cache lot. */
function liveCache() {
const f = fixture({ advanced: true });
const game = advance(f.earlySource, state => clean(state) && state.phase === 3 &&
state.richeseAuction?.source === 'cache' && !state.richeseAuction.outcome);
const card = game.richeseCache!.find(item => item.id === game.richeseAuction!.cardId);
assert.ok(card);
assert.equal(game.advanced, true);
assert.deepEqual(inventory(game), f.originalCards);
return { f, game, card };
}
for (const expected of ['yes', 'no'] as const) {
void test(`Advanced public cache question answers ${expected} and all bots continue`, () => {
const { f, game, card } = liveCache();
for (const seat of game.players) assert.equal(viewGame(game, seat.id).richeseAuction?.card?.id, card.id);
const name = expected === 'yes' ? card.name : TRUTH_CARD_NAMES.find(face => face !== card.name);
assert.ok(name);
const lot = structuredClone(game.richeseAuction);
const asked = ask(structuredClone(game), f, {kind:'fact', target:f.richese,
fact:{kind:'cacheLot', name}});
assert.equal(viewGame(asked, f.richese).truthAnswer, expected);
assert.equal(viewGame(asked, f.asker).truthAnswer, null);
const before = structuredClone(asked);
assert.throws(() => act(asked, f.richese, {type:'truthAnswer', answer:expected === 'yes' ? 'no' : 'yes'}));
assert.deepEqual(asked, before);
for (const difficulty of DIFFICULTIES) {
const view = viewGame(asked, f.richese);
view.players.find(player => player.id === f.richese)!.bot = difficulty;
assert.deepEqual(botActions(view), [{type:'truthAnswer', answer:expected}]);
const done = passTruthRicheseFreshDiscard(act(structuredClone(asked), f.richese,
{type:'truthAnswer', answer:expected}));
assert.equal(done.truthtrance, null);
assert.equal(done.truthHistory!.at(-1)!.answer, expected);
assert.deepEqual(done.richeseAuction, lot);
assert.deepEqual(inventory(done), f.originalCards);
}
});
}
void test('Advanced cache question is unknown outside the active public lot or for an outsider', () => {
const {game, card} = liveCache();
const fact = {kind:'cacheLot', name:card.name} as const;
const target = game.players.find(player => player.id === 'richese')!;
assert.equal(knowledgeFactAnswer(fact, truthKnowledgeOf(game, target)), 'yes');
assert.equal(knowledgeFactAnswer(fact, truthKnowledgeOf(game, newPlayer('outside','Outside','guild'))), 'unknown');
for (const change of [
(state: typeof game) => { state.phase = 4; },
(state: typeof game) => { state.richeseAuction = null; },
(state: typeof game) => { state.richeseAuction!.outcome = {kind:'unbid'}; },
(state: typeof game) => { state.richeseCache = state.richeseCache!.filter(item => item.id !== card.id); },
]) {
const staged = structuredClone(game); change(staged);
assert.equal(knowledgeFactAnswer(fact, truthKnowledgeOf(staged, target)), 'unknown');
}
});
void test('actual Advanced Black Market remains unknown to the public cache question', () => {
const f = fixture({advanced:true});
let game = advance(f.earlySource, state => state.decision?.kind === 'richeseBlackMarket');
const card = game.players.find(player => player.id === f.richese)!.hand[0];
assert.ok(card);
game = act(game, f.richese, {type:'decision', event:game.richeseBidding!.event, card:card.id, method:'silent'});
for (let count = 0; game.response && count < game.players.length; count++) {
const respondent = game.players.find(player => !game.response!.passed.includes(player.id));
assert.ok(respondent); game = act(game, respondent.id, {type:'passResponse'});
}
assert.equal(game.richeseAuction?.source, 'blackMarket');
assert.equal(knowledgeFactAnswer({kind:'cacheLot',name:card.name},
truthKnowledgeOf(game, game.players.find(player => player.id === f.richese)!)), 'unknown');
assert.deepEqual(inventory(game), f.originalCards);
const asked = ask(game, f, {kind:'fact', target:f.richese, fact:{kind:'cacheLot', name:card.name}});
assert.equal(viewGame(asked, f.richese).truthAnswer, 'unknown');
for (const difficulty of DIFFICULTIES) {
const view = viewGame(asked, f.richese);
view.players.find(player => player.id === f.richese)!.bot = difficulty;
assert.deepEqual(botActions(view), [{type:'truthAnswer', answer:'unknown'}]);
const answered = act(structuredClone(asked), f.richese, {type:'truthAnswer', answer:'unknown'});
assert.equal(answered.truthtrance?.stage, 'unknown');
assert.deepEqual(inventory(answered), f.originalCards);
}
});