import assert from 'node:assert/strict'; import test from 'node:test'; import { applyAction, viewGame } from '../game/engine'; import { botActions } from '../game/bots'; import { DIFFICULTIES } from '../game/bot-profiles'; import { treacheryDeck } from '../game/cards'; import { DISCOVERY_CARD_PLACEMENTS, validateDiscoveryState } from '../game/discoveries'; import { validateLeaderSkills } from '../game/leader-skills'; import { selectedDiscoverySkillsLobby, selectedDiscoverySkillsStart, advanceDiscoverySkillsBlow } from './selected-discovery-skills-fixture'; for (const advanced of [false, true]) for (const factions of [ ['atreides', 'guild', 'emperor'], ['ixians', 'ecaz', 'guild'], ['moritani', 'choam', 'guild'], ['ecaz', 'moritani', 'guild'], ] as const) void test( `public ${advanced ? 'Advanced' : 'Basic'} mixed Discovery/Skills: ${factions.join('/')}`, () => { const fixture = selectedDiscoverySkillsStart({advanced, factions}); const game = fixture.game; assert.deepEqual(game.expansions, ['ecaz', 'ix', 'choam']); assert.equal(game.discoveryEnabled, true); assert.ok(game.discoveries); assert.equal(game.discoveries.tokens.length, 8); assert.ok(!game.nexusCards && !game.ecazTreachery); assert.equal(game.leaderSkills!.assignments.length, factions.length); assert.equal(fixture.actions.filter(step => step.action.type === 'leaderSkill').length, factions.length); assert.ok(fixture.actions.some(step => step.action.type === 'moduleOption' && step.action.module === 'discoveries')); validateLeaderSkills(game.leaderSkills!, game.players); validateDiscoveryState(game.discoveries!); assert.deepEqual([...game.deck, ...game.discard, ...game.players.flatMap(player => player.hand)] .map(card => card.id).sort(), treacheryDeck(game.expansions).map(card => card.id).sort()); if (game.players.some(player => player.faction === 'ecaz')) { assert.equal(game.ecazOccupyPreview, true); assert.ok(game.ecazAmbassadors); } if (game.players.some(player => player.faction === 'moritani')) { assert.ok(game.moritaniTerror); if (advanced) assert.ok(game.moritaniAssassinate); } if (game.players.some(player => player.faction === 'ixians')) assert.ok(game.mobileStronghold); }); for (const advanced of [false, true]) void test( `${advanced ? 'Advanced' : 'Basic'} mixed Discovery/Skills reaches a real Discovery Spice Blow`, () => { const fixture = selectedDiscoverySkillsStart({advanced, factions:['ixians', 'ecaz', 'guild']}); const game = advanceDiscoverySkillsBlow(structuredClone(fixture.game), fixture.actions); assert.ok(game.spiceDiscard.flat().some(card => 'territory' in card && card.discovery === 'discovery-hagga-basin')); const placed = game.discoveries!.tokens.filter(token => token.status === 'placed'); assert.equal(placed.length, 1); const source = DISCOVERY_CARD_PLACEMENTS['discovery-hagga-basin']; assert.deepEqual([placed[0].type, placed[0].territory, placed[0].sector], [source.type, source.territory, source.sector]); validateDiscoveryState(game.discoveries!); validateLeaderSkills(game.leaderSkills!, game.players); assert.ok(!game.nexusCards && !game.ecazTreachery); assert.ok(game.active); for (const difficulty of DIFFICULTIES) { const view = viewGame(game, game.active); view.players.find(player => player.id === game.active)!.bot = difficulty; const action = botActions(view)[0]; assert.ok(action, difficulty); assert.doesNotThrow(() => applyAction(game, game.active!, action), difficulty); } }); for (const faction of ['ecaz', 'moritani'] as const) void test( `Advanced mixed Discovery/Skills with Harkonnen and ${faction} stays guarded`, () => { const fixture = selectedDiscoverySkillsLobby({advanced:true, factions:[faction, 'harkonnen', 'ixians']}); const before = structuredClone(fixture.game); assert.throws(() => applyAction(fixture.game, fixture.host, {type:'start', advancedPreview:true})); assert.deepEqual(fixture.game, before); });