Files
Dune/tests/homeworld-ecaz-duke-bots.test.ts
T

80 lines
4.9 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, viewGame, type Game } from '../game/engine';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import {
createHomeworldEcazDukeFixture as fixture,
homeworldEcazDukePlayer as player,
homeworldEcazDukeInventory as inventory,
homeworldEcazDukePhysical as physical,
} from './fixture-homeworld-ecaz-duke';
const cards = (game: Game) => ({
deck: game.deck, discard: game.discard, auction: game.auction,
hands: game.players.map(p => ({ id: p.id, hand: p.hand })),
});
for (const advanced of [false, true]) void test(
`all four minimal profiles natively grant the ${advanced ? 'Advanced' : 'Basic'} occupied Ecaz Duke to an offered recipient`, () => {
const f = fixture({ advanced, allied: true }), before = structuredClone(f.game);
assert.ok(f.ally); assert.equal(player(f.game, f.occupier).ally, f.ally);
assert.equal(player(f.game, f.ally).ally, f.occupier);
assert.equal(f.game.dukeVidal?.controller, null); assert.equal(f.game.dukeVidal?.leader.dead, false);
assert.equal(f.game.dukeVidal?.leader.id, f.dukeId);
assert.deepEqual(f.game.battle?.plans, {}); assert.equal(f.game.battle?.revealed, false);
assert.deepEqual(inventory(f.game), f.originalInventory);
const originalPhysical = structuredClone(physical(f.game)), originalCards = structuredClone(cards(f.game));
for (const profile of DIFFICULTIES) {
const own = viewGame(f.game, f.occupier), offer = own.homeworldEcazDuke;
assert.ok(offer && offer.event); assert.equal(offer.blocked, null); assert.equal(offer.occupier, f.occupier);
assert.deepEqual(offer.recipients.map(recipient => recipient.id).sort(), [f.occupier, f.ally].sort());
own.players.find(p => p.id === f.occupier)!.bot = profile;
const projected = structuredClone(own), proposals = botActions(own);
assert.deepEqual(own, projected, profile); assert.equal(proposals.length, 1, profile);
const action = proposals[0];
assert.equal(action.type, 'homeworldDuke'); assert.equal(action.event, offer.event);
assert.equal(typeof action.recipient, 'string');
assert.ok(offer.recipients.some(recipient => recipient.id === action.recipient), profile);
assert.deepEqual(action, { type: 'homeworldDuke', event: offer.event, recipient: action.recipient });
const granted = applyAction(f.game, f.occupier, action);
assert.deepEqual(f.game, before, 'Bot metadata and native application leave the original genuine source unchanged');
assert.equal(granted.dukeVidal?.leader.id, f.dukeId);
assert.deepEqual(granted.dukeVidal?.leader, before.dukeVidal!.leader);
assert.equal(granted.dukeVidal?.controller, action.recipient);
assert.equal(granted.dukeVidal?.source, 'homeworldEcaz');
assert.equal(granted.dukeVidal?.acquiredTurn, f.game.turn);
assert.deepEqual(granted.homeworldEcazDukeGrants, [{
turn: f.game.turn, qualification: f.qualification.event,
occupier: f.occupier, recipient: action.recipient,
}]);
assert.deepEqual(physical(granted), originalPhysical); assert.deepEqual(cards(granted), originalCards);
assert.deepEqual(granted.battle, before.battle); assert.deepEqual(inventory(granted), f.originalInventory);
const unavailable = viewGame(granted, f.occupier).homeworldEcazDuke;
assert.ok(unavailable?.blocked); assert.equal(unavailable.event, null);
}
},
);
void test('minimal profiles propose no occupied Ecaz Duke grant from blocked missing-event or foreign projections', () => {
const f = fixture({ allied: true }), before = structuredClone(f.game);
for (const profile of DIFFICULTIES) {
// Deliberately controlled projection-only priority states. Native custody,
// occupation, battle and the original two-choice source remain untouched.
for (const unavailable of ['blocked', 'event', 'absent'] as const) {
const own = viewGame(f.game, f.occupier); assert.ok(own.homeworldEcazDuke);
own.players.find(p => p.id === f.occupier)!.bot = profile;
if (unavailable === 'blocked') own.homeworldEcazDuke.blocked = 'Finish the current interaction before granting Duke Vidal.';
if (unavailable === 'event') own.homeworldEcazDuke.event = null;
if (unavailable === 'absent') own.homeworldEcazDuke = null;
const projected = structuredClone(own);
assert.ok(botActions(own).every(action => action.type !== 'homeworldDuke'), `${profile}: ${unavailable}`);
assert.deepEqual(own, projected);
}
const foreign = viewGame(f.game, f.ally!); assert.equal(foreign.homeworldEcazDuke, null);
foreign.players.find(p => p.id === f.ally)!.bot = profile;
assert.ok(botActions(foreign).every(action => action.type !== 'homeworldDuke'), profile);
}
assert.deepEqual(f.game, before); assert.deepEqual(inventory(f.game), f.originalInventory);
});