import test from 'node:test'; import assert from 'node:assert/strict'; import { createGame, newPlayer, viewGame, type GameView } from '../game/engine'; import { botActions } from '../game/bots'; import { DIFFICULTIES } from '../game/bot-profiles'; import { nexusTleilaxuBotActions } from '../game/nexus-tleilaxu-options'; /** Synthetic authorized offer: this tests shared bot policy, not acquisition * or source-effect execution, which belong to the engine suite. */ function fixture(): GameView { const g = createGame('DANCERBOTS', newPlayer('t', 'Tleilaxu', 'tleilaxu')); g.players.push( newPlayer('a', 'Atreides', 'atreides'), newPlayer('h', 'Harkonnen', 'harkonnen'), ); Object.assign(g, { status: 'playing', phase: 6, turn: 2, active: 'a', order: ['t', 'a', 'h'], }); const v = viewGame(g, 't'); v.nexusCards = { card: 'tleilaxu', deckCount: 11, discardCount: 0, held: { t: true, a: false, h: false }, turn: null, choices: [], waiting: [], }; v.nexusTleilaxu = { cunning: { event: 'dancer-event', count: 1, blocked: null }, secretAlly: null, }; return v; } void test('all bot profiles preserve Truth, closing draw, automatic and preceding exchange priorities', () => { for (const difficulty of DIFFICULTIES) { const v = fixture(); v.players[0].bot = difficulty; v.truthtrance = { stage: 'priority', queue: [], passed: [], question: null, }; assert.deepEqual(botActions(v), [{ type: 'truthPass' }]); v.truthtrance = null; v.automaticContinuationPending = true; assert.deepEqual(botActions(v), []); v.automaticContinuationPending = false; v.nexusCards!.waiting = ['a']; assert.deepEqual(botActions(v), []); v.nexusCards!.waiting = []; v.nexusTraitors = { offer: null, pending: { event: 'exchange', owner: 'h', mode: 'cunning', count: 1, choices: [], }, }; assert.deepEqual(botActions(v), []); v.nexusTraitors = null; v.nexusTleilaxu!.cunning!.blocked = 'Cunning is unavailable.'; assert.deepEqual(nexusTleilaxuBotActions(v), []); v.nexusTleilaxu = null; assert.deepEqual(nexusTleilaxuBotActions(v), []); } }); void test('replacement policy never reads any private hand, traitor, dancer or spice field', () => { const v = fixture(); v.players[0].bot = 'Brutal'; for (const player of v.players) for (const field of ['hand', 'traitors', 'faceDancers', 'spice']) Object.defineProperty(player, field, { get() { throw new Error(`Read private ${field}`); }, }); assert.deepEqual(botActions(v), [ { type: 'nexusFaceDancers', event: 'dancer-event' }, ]); v.me = 'a'; assert.deepEqual(nexusTleilaxuBotActions(v), []); });