Files
Dune/tests/richese-native-marker-runtime.test.ts
T
Quintine 26d45ecf25
Verify and publish checkpoint container / container (push) Canceled after 0s
Allow fresh Advanced native No-Field placement
2026-10-29 20:09:52 +11:00

71 lines
4.0 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, viewGame } from '../game/engine';
import { homeworldShipmentActions, richeseHomeworldMarkerChoice } from '../game/homeworld-shipment-options';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import { freshNativeMarkerFixture, native, owner, pass, player } from './fixture-richese-native-marker';
for (const value of [0, 3, 5] as const) void test(
`Advanced fresh No-Field ${value} reaches native Richese without duplicating reserves`, () => {
const f = freshNativeMarkerFixture(true, value), before = structuredClone(f.game);
assert.equal(f.quote.blocked, null);
assert.equal(f.quote.amount, 1);
assert.equal(f.quote.cost, 1);
assert.ok(f.quote.action);
let game = applyAction(f.game, owner, f.quote.action);
assert.equal(game.response?.kind, 'richeseNoField');
assert.equal(player(game, owner).spice, player(before, owner).spice);
assert.equal(player(game, owner).reserves, player(before, owner).reserves);
game = pass(game);
assert.ok(player(game, owner).shipped);
assert.equal(player(game, owner).spice, player(before, owner).spice - 1);
assert.equal(player(game, owner).reserves, player(before, owner).reserves);
assert.deepEqual(game.homeworlds!.custody, before.homeworlds!.custody);
assert.deepEqual(player(game, owner).noField!.deployed!.location, {homeworld:native});
assert.equal(player(game, owner).noField!.lastShipped, f.token);
const publicMarker = viewGame(game, 'emperor').richeseNoField!;
assert.equal(publicMarker.private, null);
assert.equal(publicMarker.public.deployed?.effectiveForces, 1);
assert.equal(JSON.stringify(publicMarker.public).includes(f.token), false);
const own = player(game, owner);
game = pass(applyAction(game, owner, {type:'revealNoField', token:f.token, event:own.noFieldEvent}));
assert.equal(player(game, owner).noField!.deployed, null);
assert.equal(player(game, owner).reserves, player(before, owner).reserves);
assert.deepEqual(game.homeworlds!.custody, before.homeworlds!.custody);
assert.deepEqual(f.game, before, 'Applying the shipment leaves its source snapshot intact');
});
void test('Basic rejects fresh native placement while Advanced rejects same-source companions and stale events', () => {
const basic = freshNativeMarkerFixture(false);
const originalBasic = structuredClone(basic.game);
assert.equal(basic.quote.action, null);
assert.ok(basic.quote.blocked);
assert.deepEqual(basic.game, originalBasic);
const f = freshNativeMarkerFixture(true), before = structuredClone(f.game);
const mixed = richeseHomeworldMarkerChoice(f.view, native, f.token, 0, undefined, {normal:1, elite:0});
assert.equal(mixed.action, null, 'Native reserves cannot move from a World back to itself');
assert.ok(mixed.blocked);
assert.ok(f.quote.action);
assert.throws(() => applyAction(f.game, owner, {...f.quote.action!, noFieldEvent:'stale-marker'}));
assert.deepEqual(f.game, before);
});
void test('existing bot profiles retain a legal Homeworld action in the fresh native position', () => {
const f = freshNativeMarkerFixture(true);
for (const [index, difficulty] of DIFFICULTIES.entries()) {
const view = viewGame(f.game, owner);
view.players.find(row => row.id === owner)!.bot = difficulty;
const candidates = homeworldShipmentActions(view, index);
assert.ok(candidates.length, difficulty);
assert.doesNotThrow(() => applyAction(f.game, owner, candidates[0]), difficulty);
const nativeCandidate = candidates.find(action => action.type === 'homeworldShip' &&
action.destination === native && action.noField !== undefined);
assert.ok(nativeCandidate, `${difficulty} can select the native marker route`);
assert.doesNotThrow(() => applyAction(f.game, owner, nativeCandidate), difficulty);
const action = botActions(view)[0]; assert.ok(action, difficulty);
assert.doesNotThrow(() => applyAction(f.game, owner, action), difficulty);
}
});