101 lines
5.4 KiB
TypeScript
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);
|
|
}
|
|
});
|