55 lines
3.5 KiB
TypeScript
55 lines
3.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { movementShipmentPreparations } from '../game/movement-shipment-preparation';
|
|
import type { Game } from '../game/engine';
|
|
|
|
/** Structural enumeration fixture only, not a claimed natural gameplay source. */
|
|
function source(): Game {
|
|
return { players: [{ id: 'owner', faction: 'ixians', forces: {}, reserves: 3, shipped: false,
|
|
elites: { reserves: 1 }, ally: 'ally' }], aid: { ally: { recipient: 'owner', amount: 3 } },
|
|
expansions: [], advanced: false } as unknown as Game;
|
|
}
|
|
void test('preparation enumeration retains larger optional pledged shares and distinct physical special-force allocations', () => {
|
|
const game = source(), before = structuredClone(game);
|
|
const choices = [...movementShipmentPreparations(game, 'owner', (state, _actor, action) =>
|
|
action.territory === 'imperial_basin' && action.sector === 9 && action.amount === 2 && action.allyPayment === 3
|
|
? structuredClone(state) : null)];
|
|
assert.equal(choices.length, 2);
|
|
assert.deepEqual(choices.map(choice => choice.actions[0].elite), [0, 1]);
|
|
assert.deepEqual(choices.map(choice => choice.actions[0].allyPayment), [3, 3]);
|
|
assert.deepEqual(game, before);
|
|
});
|
|
void test('spent shipment and paid Box decisions cannot start another preparation enumeration', () => {
|
|
const game = source(); game.players[0].shipped = true;
|
|
assert.deepEqual([...movementShipmentPreparations(game, 'owner', () => assert.fail('Shipment is spent'))], []);
|
|
game.players[0].shipped = false; game.pendingNullentropy = { player: 'owner' } as Game['pendingNullentropy'];
|
|
assert.deepEqual([...movementShipmentPreparations(game, 'owner', () => assert.fail('Finish the paid decision'))], []);
|
|
});
|
|
|
|
void test('Fremen reserve preparation retains Guild transport variants only with a reciprocal native ally', () => {
|
|
const game = source(), owner = game.players[0]; owner.faction = 'fremen';
|
|
const guild: Game['players'][number] = { ...owner, id: 'ally', faction: 'guild', ally: owner.id };
|
|
game.players.push(guild);
|
|
const matching = (action: Record<string, unknown>) => action.type === 'guildShip' && action.from === 'reserves' &&
|
|
action.territory === 'carthag' && action.sector === 11 && action.amount === 2 && action.allyPayment === 1;
|
|
const choices = [...movementShipmentPreparations(game, owner.id, (state, _actor, action) =>
|
|
matching(action) ? structuredClone(state) : null)];
|
|
assert.equal(choices.length, 2);
|
|
assert.deepEqual(choices.map(choice => choice.actions[0].elite), [0, 1]);
|
|
guild.ally = null;
|
|
assert.deepEqual([...movementShipmentPreparations(game, owner.id, (_state, _actor, action) => {
|
|
assert.notEqual(action.type, 'guildShip'); return null;
|
|
})], []);
|
|
});
|
|
|
|
void test('owned No-Field shipment candidates preserve marker identity, one-force tariff shares and last-use restrictions', () => {
|
|
const game = source(), owner = game.players[0]; owner.faction = 'richese';
|
|
owner.noField = { tokens: [{ id: 'zero', value: 0 }, { id: 'three', value: 3 }, { id: 'five', value: 5 }],
|
|
lastShipped: 'three', deployed: null }; owner.noFieldEvent = 'current';
|
|
const choices = [...movementShipmentPreparations(game, owner.id, (state, _actor, action) =>
|
|
action.noField && action.territory === 'imperial_basin' && action.sector === 9 && action.allyPayment === 2
|
|
? structuredClone(state) : null)].map(choice => choice.actions[0]);
|
|
assert.deepEqual(choices.map(choice => choice.noField), ['zero', 'five']);
|
|
assert.ok(choices.every(choice => choice.amount === 1 && choice.elite === 0 && choice.event === 'current'));
|
|
});
|