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); } });