Files
Dune/tests/discovery-native-e3-runtime.test.ts

59 lines
3.7 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { applyAction, createGame, initializeDiscoveryGameForAudit, joinGame, newPlayer } from '../game/engine';
import { createTechTokens } from '../game/tech-tokens';
import { nativeTypedClearBoard, nativeTypedPhase, nativeTypedPlace, nativeTypedPlayer, nativeTypedReload, nativeTypedStep } from './fixture-discovery-native-typed';
import { putDiscovery } from './fixture-discovery';
import { giveAssassinationTraitor } from './moritani-assassinate-fixture';
import { openSkillsTechBattle, nextSkillsTechBattleStep } from './fixture-skills-tech-battle';
void test('native Moritani assassination retains the actual revealed Discovery battle source and physical traitor cost', () => {
let game = createGame('DISCASSA', newPlayer('m', 'Moritani', 'moritani'), true, ['ecaz']);
joinGame(game, newPlayer('g', 'Guild', 'guild')); joinGame(game, newPlayer('f', 'Fremen', 'fremen'));
for (const player of game.players) player.ready = true;
game.discoveryEnabled = true; game.techTokens = createTechTokens();
game = initializeDiscoveryGameForAudit(game);
for (let n = 0; game.status === 'setup' && n < 100; n++) game = nativeTypedStep(game);
// Keep the conserved token supply available: the first real Spice Blow
// draws ordinary land, rather than randomly placing this later fixture token.
for (const position of [0, 1]) {
const index = game.spiceDeck.findIndex((card, i) => i >= position && 'territory' in card && !card.discovery);
assert.ok(index >= position);
game.spiceDeck.splice(position, 0, game.spiceDeck.splice(index, 1)[0]);
}
game = nativeTypedPhase(game, 5);
nativeTypedClearBoard(game);
// Conserved original supply token and reserve positions; inspect/reveal,
// clocks, battle, assassination and reward are produced by original controls.
const token = putDiscovery(game, 'cistern');
nativeTypedPlace(game, 'm', `${token.territory}:${token.sector}`, 2);
game = nativeTypedPhase(game, 7);
game = applyAction(game, 'm', { type: 'discovery', token: token.id, reveal: false });
game = applyAction(game, 'm', { type: 'discovery', token: token.id, reveal: true });
game = nativeTypedPhase(game, 8);
game = nativeTypedPhase(game, 5, 2);
nativeTypedClearBoard(game);
for (const player of game.players) { game.deck.push(...player.hand); player.hand = []; }
for (const id of ['m', 'g']) nativeTypedPlace(game, id, 'cistern:0', 3);
giveAssassinationTraitor(game, 'guild-1');
game = nativeTypedPhase(game, 6);
game = openSkillsTechBattle(game, 'm', 'g', 'cistern');
for (const id of [game.battle!.attacker, game.battle!.defender]) {
const leader = id === 'g' ? 'guild-0' : nativeTypedPlayer(game, 'm').leaders[4].id;
game = applyAction(game, id, { type: 'battlePlan', leader, dial: 0, support: 0 });
}
for (const id of ['m', 'g']) game = applyAction(game, id, { type: 'traitorCall', call: false });
for (let n = 0; game.decision?.kind !== 'moritaniAssassinate' && n < 100; n++) {
const next = nextSkillsTechBattleStep(game); game = applyAction(game, next.actor, next.action);
}
assert.ok(game.decision?.kind === 'moritaniAssassinate');
const spice = nativeTypedPlayer(game, 'm').spice;
const event = game.decision.event;
game = applyAction(nativeTypedReload(game), 'm', { type: 'decision', event, card: 'guild-1' });
assert.equal(nativeTypedPlayer(game, 'g').leaders.find(leader => leader.id === 'guild-1')!.dead, true);
assert.equal(nativeTypedPlayer(game, 'm').spice, spice + 3);
assert.equal(game.moritaniAssassinate!.opportunities[0].territory, 'cistern');
assert.equal(game.moritaniAssassinate!.opportunities[0].card, 'guild-1');
assert.equal(game.moritaniAssassinate!.opportunities[0].stage, 'revealed');
});