Files
Dune/tests/ecaz-duke-ally-engine.test.ts

150 lines
8.9 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { viewGame, type Action, type Game } from '../game/engine';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import {
actEcazDukeAlly, advanceEcazDukeAlly, battleEcazDukeAlly, createEcazDukeAllyFixture,
ecazDukeAllyClean, ecazDukeAllyInventory, ecazDukeAllyPhysical,
ecazDukeAllyPlayer as player, finishEcazDukeAlly, grantEcazDukeAlly,
} from './fixture-ecaz-duke-ally';
function rejected(game: Game, actor: string, action: Action) {
const before = structuredClone(game);
assert.throws(() => actEcazDukeAlly(game, actor, action));
assert.deepEqual(game, before);
}
for (const advanced of [false, true]) {
void test(`${advanced ? 'Advanced' : 'Basic'} native already-allied Ecaz Ambassador transfers its held ordinary Duke to the ally`, () => {
const f = createEcazDukeAllyFixture({ advanced });
assert.equal(f.game.status, 'playing');
assert.equal(f.game.turn, 2);
assert.equal(f.game.phase, 5);
assert.equal(f.game.active, f.entrant);
assert.equal(f.game.pendingAmbassador!.stage, 'offer');
assert.equal(f.game.pendingAmbassador!.owner, f.owner);
assert.equal(f.game.pendingAmbassador!.entrant, f.entrant);
assert.equal(f.game.pendingAmbassador!.token, f.token);
assert.equal(f.game.pendingAmbassador!.territory, 'carthag');
assert.equal(f.game.response ?? null, null);
assert.equal(f.game.phaseOpening ?? null, null);
assert.equal(f.game.battle, null);
assert.equal(player(f.game, f.owner).ally, f.ally);
assert.equal(player(f.game, f.ally).ally, f.owner);
assert.equal(player(f.game, f.entrant).ally, null);
assert.deepEqual([f.afterSelf.dukeVidal!.controller, f.afterSelf.dukeVidal!.source, f.afterSelf.dukeVidal!.acquiredTurn],
[f.owner, 'ecaz', 1]);
assert.equal(f.afterMentat.dukeVidal!.controller, f.owner);
assert.deepEqual([f.game.dukeVidal!.controller, f.game.dukeVidal!.source, f.game.dukeVidal!.acquiredTurn],
[f.owner, 'ecaz', 1]);
assert.equal(f.placements.length, 2);
assert.ok(f.placements.every(placement => placement.action.token === f.token));
assert.equal(f.shipments.length, 3);
assert.equal(f.shipments[0].step.action.territory, 'habbanya_ridge_sietch');
assert.equal(f.shipments[1].step.actor, f.ally);
assert.equal(f.shipments[2].step.actor, f.entrant);
assert.ok(f.alliance.actions.some(step => step.actor === f.owner && step.action.type === 'alliance'));
assert.ok(f.alliance.actions.some(step => step.actor === f.ally && step.action.type === 'alliance'));
const offer = viewGame(f.game, f.owner).ambassadorEntry;
assert.ok(offer);
assert.ok(offer.beneficiaries!.find(target => target.player === f.owner)!.blocked);
assert.equal(offer.beneficiaries!.find(target => target.player === f.ally)!.blocked, null);
assert.equal(player(f.game, f.ally).forces['carthag:11'], 1);
assert.equal(player(f.game, f.entrant).forces['carthag:11'], 1);
assert.equal(f.sourceActionCount, f.actions.length);
assert.deepEqual(ecazDukeAllyInventory(f.game), f.originalInventory);
let game = grantEcazDukeAlly(f);
game = finishEcazDukeAlly(f, game);
assert.deepEqual([game.dukeVidal!.controller, game.dukeVidal!.source, game.dukeVidal!.acquiredTurn],
[f.ally, 'ecaz', 2]);
assert.deepEqual(game.dukeVidal!.leader, f.originalDuke.leader);
assert.ok(game.log.filter(entry => entry.text.includes('acquired Duke Prad Vidal')).at(-1)!.text
.startsWith(`${player(game, f.ally).name} acquired Duke Prad Vidal`));
assert.deepEqual(ecazDukeAllyPhysical(game), f.originalPhysical,
'Only the existing shared disc controller changes after the already-paid native entry');
assert.equal(game.pendingAmbassador ?? null, null);
assert.equal(game.ecazAmbassadors!.tokens.find(token => token.id === f.token)!.zone, 'supply');
assert.deepEqual(game.ecazAmbassadors!.cohort, f.cohort);
assert.deepEqual(ecazDukeAllyInventory(game), f.originalInventory);
});
void test(`${advanced ? 'Advanced' : 'Basic'} native existing-ally ordinary Duke fights once and releases the same living shared disc`, () => {
const f = createEcazDukeAllyFixture({ advanced });
const granted = finishEcazDukeAlly(f, grantEcazDukeAlly(f));
const beforeRosters = granted.players.map(seat => ({ id: seat.id, leaders: seat.leaders.map(leader => leader.id) }));
const fought = battleEcazDukeAlly(f, granted);
assert.equal(fought.beforePlans.dukeVidal!.controller, f.ally);
assert.equal(fought.beforePlans.dukeVidal!.source, 'ecaz');
assert.equal(fought.plans[0].actor, f.ally);
assert.equal(fought.plans[0].action.leader, f.duke);
assert.equal(fought.sealed.battle!.plans[f.ally].leader, f.duke);
assert.equal(fought.game.battle, null);
assert.equal(fought.game.lastBattleContext!.winner, f.ally);
assert.equal(fought.game.lastBattleContext!.territory, 'carthag');
assert.deepEqual([fought.game.dukeVidal!.controller, fought.game.dukeVidal!.source, fought.game.dukeVidal!.acquiredTurn],
[null, null, null]);
assert.equal(fought.game.dukeVidal!.leader.id, f.duke);
assert.equal(fought.game.dukeVidal!.leader.dead, false);
assert.equal(fought.game.dukeVidal!.leader.deaths, f.originalDuke.leader.deaths);
assert.equal(player(fought.game, f.ally).forces['carthag:11'], 1);
assert.equal(player(fought.game, f.opponent).forces['carthag:11'] ?? 0, 0);
assert.deepEqual(fought.game.players.map(seat => ({ id: seat.id, leaders: seat.leaders.map(leader => leader.id) })), beforeRosters);
assert.deepEqual(ecazDukeAllyInventory(fought.game), f.originalInventory);
});
void test(`${advanced ? 'Advanced' : 'Basic'} unused existing-ally direct acquisition persists through a real native battle and next Mentat`, () => {
const f = createEcazDukeAllyFixture({ advanced });
const granted = finishEcazDukeAlly(f, grantEcazDukeAlly(f));
const fought = battleEcazDukeAlly(f, granted, [], { useDuke: false });
assert.notEqual(fought.plans[0].action.leader, f.duke);
assert.deepEqual(fought.game.dukeVidal, granted.dukeVidal);
const nextTurn = advanceEcazDukeAlly(fought.game, state => state.turn === 3 && state.phase === 1 && ecazDukeAllyClean(state));
assert.equal(nextTurn.turn, 3);
assert.deepEqual([nextTurn.dukeVidal!.controller, nextTurn.dukeVidal!.source, nextTurn.dukeVidal!.acquiredTurn],
[f.ally, 'ecaz', 2]);
assert.deepEqual(nextTurn.dukeVidal!.leader, granted.dukeVidal!.leader);
assert.equal(player(nextTurn, f.owner).ally, f.ally);
assert.equal(player(nextTurn, f.ally).ally, f.owner);
assert.deepEqual(ecazDukeAllyInventory(nextTurn), f.originalInventory);
});
void test(`${advanced ? 'Advanced' : 'Basic'} live Ecaz Duke entry rejects foreign, stale, self-held and nonally beneficiaries before consuming its token`, () => {
const f = createEcazDukeAllyFixture({ advanced });
rejected(f.game, f.ally, f.action);
rejected(f.game, f.entrant, f.action);
rejected(f.game, f.owner, { ...f.action, event: 'stale' });
rejected(f.game, f.owner, { ...f.action, beneficiary: f.owner });
rejected(f.game, f.owner, { ...f.action, beneficiary: f.entrant });
rejected(f.game, f.owner, { ...f.action, beneficiary: 'guild' });
assert.equal(player(f.beforeSelf, f.owner).ally, null);
assert.equal(player(f.beforeSelf, f.ally).ally, null);
rejected(f.beforeSelf, f.owner, { ...f.action, event: f.beforeSelf.pendingAmbassador!.event });
assert.deepEqual(f.game.dukeVidal, f.originalDuke);
assert.deepEqual(f.game.pendingAmbassador, f.originalParent);
assert.deepEqual(ecazDukeAllyPhysical(f.game), f.originalPhysical);
assert.equal(f.game.ecazAmbassadors!.tokens.find(token => token.id === f.token)!.zone, 'placed');
const granted = finishEcazDukeAlly(f, grantEcazDukeAlly(f));
rejected(granted, f.owner, f.action);
assert.deepEqual(ecazDukeAllyInventory(granted), f.originalInventory);
});
}
void test('four minimal owner profiles choose the actual existing ally when Ecaz already holds the shared Duke', () => {
for (const advanced of [false, true]) {
const f = createEcazDukeAllyFixture({ advanced });
for (const difficulty of DIFFICULTIES) {
const view = viewGame(f.game, f.owner);
view.players.find(seat => seat.id === f.owner)!.bot = difficulty;
const action = botActions(view)[0];
assert.deepEqual(action, f.action, `${advanced}/${difficulty}`);
const game = finishEcazDukeAlly(f, actEcazDukeAlly(structuredClone(f.game), f.owner, action));
assert.deepEqual([game.dukeVidal!.controller, game.dukeVidal!.source, game.dukeVidal!.acquiredTurn],
[f.ally, 'ecaz', 2]);
assert.deepEqual(game.dukeVidal!.leader, f.originalDuke.leader);
assert.deepEqual(ecazDukeAllyPhysical(game), f.originalPhysical);
assert.deepEqual(ecazDukeAllyInventory(game), f.originalInventory);
}
}
});