80 lines
4.9 KiB
TypeScript
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);
|
|
});
|