Files
Dune/tests/diplomat-context-journey.test.ts
T

131 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, viewGame } from '../game/engine';
import { weaponKills } from '../game/battle-cards';
import {
applyDiplomatContextStep, assertDiplomatContextCustody, copyDiplomatContextJourney,
createDiplomatContextJourney, diplomatContextPlayer, finishDiplomatContextJourney,
revealDiplomatContextJourney, settleDiplomatContextJourney,
} from './fixture-diplomat-context-journey';
void test('original native World vote closes, invader uses actual late Portable, and Diplomat copies only its poison role before typed custody settles', () => {
const fixture = createDiplomatContextJourney();
const { game, owner, opponent, trainer, enemyLeader, source, worthless, territory } = fixture;
assert.ok(source);
const shipment = fixture.shipments[0];
assert.equal(diplomatContextPlayer(shipment.before, opponent).spice -
diplomatContextPlayer(shipment.after, opponent).spice, 2);
assert.equal(diplomatContextPlayer(shipment.before, opponent).reserves -
diplomatContextPlayer(shipment.after, opponent).reserves, 3);
const worlds = viewGame(game, owner).homeworlds!.worlds!;
const nativeWorld = worlds.find(world => world.id === territory)!;
const salusa = worlds.find(world => world.id === 'homeworld:emperor:salusa')!;
assert.equal(nativeWorld.card, 'kaitain');
assert.deepEqual(nativeWorld.forces[owner], { normal: 15, elite: 0 });
assert.deepEqual(nativeWorld.forces[opponent], { normal: 3, elite: 0 });
assert.deepEqual(salusa.forces[owner], { normal: 0, elite: 5 });
const revealed = revealDiplomatContextJourney(fixture);
assert.equal(revealed.battle!.diplomatDefense, undefined);
assert.deepEqual(viewGame(revealed, owner).battle!.traitorVoters, [owner]);
assert.equal(revealed.battle!.plans[opponent].defense, null);
const nativeVoted = applyAction(revealed, owner, { type: 'traitorCall', call: false });
assert.equal(nativeVoted.decision?.kind, 'homeworldDefense');
assert.equal(nativeVoted.decision?.player, opponent);
assert.equal(nativeVoted.battle!.traitorCalls[owner], false);
assert.equal(nativeVoted.battle!.traitorCalls[opponent], undefined);
assert.ok(nativeVoted.decision?.kind === 'homeworldDefense');
const used = applyDiplomatContextStep(nativeVoted, { actor: opponent,
action: { type: 'decision', event: nativeVoted.decision.event, use: true } });
assert.equal(used.decision?.kind, 'diplomatDefense');
assert.equal(used.battle!.lateDefense![opponent], source);
assert.deepEqual(used.battle!.homeworldDefensePassed, [opponent]);
assert.equal(used.battle!.traitorCalls[owner], false);
assert.deepEqual(used.battle!.diplomatDefense!.cards, [worthless]);
assert.equal(used.battle!.diplomatDefense!.source, source);
assert.equal(used.battle!.diplomatDefense!.kind, 'snooper');
assert.deepEqual(used.battle!.plans, revealed.battle!.plans);
const copied = copyDiplomatContextJourney(used, worthless);
assert.throws(() => applyAction(copied, owner, { type: 'traitorCall', call: false }),
'The submitted native-only vote cannot be reopened after the copied defense.');
const settled = settleDiplomatContextJourney(copied);
assert.equal(settled.lastBattleContext!.winner, owner);
assert.equal(diplomatContextPlayer(settled, owner).leaders.find(l => l.id === trainer)!.dead, false);
assert.equal(diplomatContextPlayer(settled, opponent).leaders.find(l => l.id === enemyLeader)!.dead, false);
const finalWorlds = viewGame(settled, owner).homeworlds!.worlds!;
assert.deepEqual(finalWorlds.find(world => world.id === territory)!.forces[owner], nativeWorld.forces[owner]);
assert.deepEqual(finalWorlds.find(world => world.id === 'homeworld:emperor:salusa')!.forces[owner],
{ normal: 0, elite: 5 }, 'Copying a defense never spends or relabels Sardaukar.');
assert.deepEqual(finalWorlds.find(world => world.id === territory)!.forces[opponent] ?? { normal: 0, elite: 0 },
{ normal: 0, elite: 0 });
assert.equal(diplomatContextPlayer(settled, opponent).tanks, 3);
assert.equal(diplomatContextPlayer(settled, owner).elites!.tanks, 0);
assert.equal(settled.discard.filter(card => card.id === worthless).length, 1);
assert.equal(settled.discard.filter(card => card.id === source).length, 1);
assert.equal(diplomatContextPlayer(settled, owner).hand.some(card => card.id === worthless), false);
assertDiplomatContextCustody(settled);
assert.equal(finishDiplomatContextJourney(settled).phase, 7);
});
void test('an actually earned Carthag layers its own Snooper onto a copied printed Shield while preserving physical custody', () => {
const fixture = createDiplomatContextJourney({ kind: 'held-carthag' });
const { owner, opponent, trainer, enemyLeader, worthless, source, firstMentat } = fixture;
assert.ok(firstMentat && source);
assert.equal(firstMentat.before.strongholdCards!.owners.carthag, null);
assert.equal(firstMentat.before.players.find(p => p.id === owner)!.forces['carthag:11'], 1);
assert.equal(firstMentat.after.strongholdCards!.owners.carthag, owner);
assert.equal(fixture.game.strongholdCards!.owners.carthag, owner);
assert.equal(fixture.game.players.find(p => p.id === opponent)!.forces['carthag:11'], 1);
for (const shipment of fixture.shipments) {
assert.equal(diplomatContextPlayer(shipment.before, shipment.step.actor).reserves -
diplomatContextPlayer(shipment.after, shipment.step.actor).reserves, 1);
assert.equal(diplomatContextPlayer(shipment.before, shipment.step.actor).spice -
diplomatContextPlayer(shipment.after, shipment.step.actor).spice, 1);
}
assert.equal(weaponKills(
diplomatContextPlayer(fixture.game, opponent).hand.find(card => card.id === fixture.enemyWeapon),
diplomatContextPlayer(fixture.game, opponent).hand.find(card => card.id === source),
), true, 'The exact copied printed Shield by itself does not protect against the exact poison weapon.');
const revealed = revealDiplomatContextJourney(fixture);
assert.equal(revealed.battle!.diplomatDefense!.source, source);
assert.equal(revealed.battle!.diplomatDefense!.kind, 'shield');
const settled = settleDiplomatContextJourney(copyDiplomatContextJourney(revealed, worthless));
assert.equal(settled.lastBattleContext!.winner, owner);
assert.equal(diplomatContextPlayer(settled, owner).leaders.find(l => l.id === trainer)!.dead, false,
'Shield alone cannot stop poison: the holder’s original Carthag independently supplies Snooper.');
assert.equal(diplomatContextPlayer(settled, opponent).leaders.find(l => l.id === enemyLeader)!.dead, false);
assert.equal(diplomatContextPlayer(settled, owner).forces['carthag:11'], 1);
assert.equal(diplomatContextPlayer(settled, owner).tanks, 0);
assert.equal(diplomatContextPlayer(settled, owner).elites!.tanks, 0);
assert.equal(diplomatContextPlayer(settled, opponent).tanks, 1);
assert.equal(settled.strongholdCards!.owners.carthag, owner);
assert.equal(settled.discard.filter(card => card.id === worthless).length, 1);
assert.equal(settled.discard.filter(card => card.id === source).length, 1);
assert.equal(diplomatContextPlayer(settled, owner).hand.some(card => card.id === source), false);
assertDiplomatContextCustody(settled);
assert.equal(finishDiplomatContextJourney(settled).phase, 7);
});
void test('an earned Carthag with only a physical Worthless and no opposing actual defense supplies neither a copy source nor poison protection', () => {
const fixture = createDiplomatContextJourney({ kind: 'empty-carthag' });
const { owner, opponent, trainer, worthless } = fixture;
assert.equal(fixture.firstMentat!.after.strongholdCards!.owners.carthag, owner);
const revealed = revealDiplomatContextJourney(fixture);
assert.equal(revealed.battle!.plans[owner].weapon, worthless);
assert.equal(revealed.battle!.plans[owner].defense, null);
assert.equal(revealed.battle!.plans[opponent].defense, null);
assert.equal(revealed.battle!.diplomatDefense, undefined);
assert.notEqual(revealed.decision?.kind, 'diplomatDefense');
const settled = settleDiplomatContextJourney(revealed);
assert.equal(diplomatContextPlayer(settled, owner).leaders.find(l => l.id === trainer)!.dead, true);
assert.equal(settled.lastBattleContext!.winner, opponent);
assert.equal(settled.strongholdCards!.owners.carthag, owner,
'Battle occupancy does not replace the original END Mentat card owner.');
assert.equal(diplomatContextPlayer(settled, owner).forces['carthag:11'] ?? 0, 0);
assert.equal(diplomatContextPlayer(settled, owner).tanks, 1);
assert.equal(diplomatContextPlayer(settled, owner).elites!.tanks, 0);
assert.equal(settled.discard.filter(card => card.id === worthless).length, 1);
assertDiplomatContextCustody(settled);
});