import assert from 'node:assert/strict'; import test from 'node:test'; import { botActions } from '../game/bots'; import { DIFFICULTIES } from '../game/bot-profiles'; import { coalitionOf } from '../game/coalition-alliance'; import { applyAction, createGame, joinGame, newPlayer, viewGame, type Action, type Game } from '../game/engine'; import { advanceShortAlliance, type ShortAllianceStep } from './fixture-short-alliance-victory'; const apply = (game: Game, actor: string, action: Action) => applyAction(game, actor, action); /** Real setup and first turn; select only unopened Spice order to reach Nexus. */ function pairAtNexus(advanced: boolean): Game { let game = createGame('THREEGROUPBOT', newPlayer('at', 'Atreides', 'atreides')); joinGame(game, newPlayer('em', 'Emperor', 'emperor')); joinGame(game, newPlayer('ha', 'Harkonnen', 'harkonnen')); if (advanced) game = apply(game, 'at', { type: 'rules', advanced: true }); game = apply(game, 'at', { type: 'coalitionPreview', variant: 'three' }); for (const seat of game.players) game = apply(game, seat.id, { type: 'ready' }); game = apply(game, 'at', advanced ? { type: 'start', advancedPreview: true } : { type: 'start' }); const actions: ShortAllianceStep[] = []; game = advanceShortAlliance(game, state => state.status === 'playing' && state.turn === 2 && state.phase === 0, actions); const index = game.spiceDeck.findIndex(card => 'worm' in card && !card.greatMaker && !card.suppressed && !card.thumper); assert.ok(index >= 0); const before = game.spiceDeck.map(card => JSON.stringify(card)).sort(); game.spiceDeck.unshift(game.spiceDeck.splice(index, 1)[0]); assert.deepEqual(game.spiceDeck.map(card => JSON.stringify(card)).sort(), before); game = advanceShortAlliance(game, state => state.nexus && !state.spiceWindow && !state.spiceResolution && !state.response && !state.decision && !state.truthtrance && !state.phaseOpening, actions); game = apply(game, 'at', { type: 'alliance', target: 'em' }); return apply(game, 'em', { type: 'alliance', target: 'at' }); } void test('each bot can seek admission to the three-member four-stronghold preview in Basic and Advanced', () => { for (const advanced of [false, true]) { const initial = pairAtNexus(advanced); const pair = coalitionOf(initial.players, initial.coalitionState, 'at'); assert.ok(pair); assert.equal(initial.coalitionState!.variant, undefined); assert.equal(viewGame(initial, 'ha').coalitionPreview, 'three'); for (const difficulty of DIFFICULTIES) { const view = viewGame(initial, 'ha'); view.players.find(player => player.id === 'ha')!.bot = difficulty; const proposal = botActions(view).find(action => action.type === 'coalitionPropose'); assert.ok(proposal, `${advanced ? 'Advanced' : 'Basic'} ${difficulty}`); let game = apply(initial, 'ha', proposal); for (const actor of ['at', 'em']) { const offer = game.coalitionState!.proposal; assert.ok(offer); game = apply(game, actor, { type: 'coalitionAnswer', event: offer.event, nexus: offer.nexus, turn: offer.turn, accept: true, expectedRevision: game.coalitionState!.revision }); } assert.deepEqual(new Set(coalitionOf(game.players, game.coalitionState, 'at')!.members), new Set(['at', 'em', 'ha'])); assert.equal(viewGame(game, 'at').victoryProgress.find(row => row.player === 'at')!.target, 4); } } });