74 lines
3.8 KiB
TypeScript
74 lines
3.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
createRicheseNoField, deployRicheseNoField, isBoardNoFieldLocation,
|
|
isHomeworldNoFieldLocation, moveRicheseNoField, projectRicheseNoField,
|
|
recallRicheseNoField, revealRicheseNoField, validateRicheseNoField,
|
|
type NoFieldLocation, type RicheseNoField,
|
|
} from '../game/richese-no-field';
|
|
|
|
const ids = ['zero', 'three', 'five'];
|
|
const home = { homeworld: 'homeworld:atreides' } as const;
|
|
const planet = { territory: 'polar_sink', sector: 0 } as const;
|
|
function placed(location: NoFieldLocation = home, tokenId = 'three') {
|
|
return deployRicheseNoField(createRicheseNoField(ids), { tokenId, controller: 'richese', location });
|
|
}
|
|
|
|
void test('Homeworld marker preserves single physical token and hides its face in public projection', () => {
|
|
const state = placed();
|
|
assert.equal(state.lastShipped, 'three');
|
|
assert.deepEqual(projectRicheseNoField(state, false), {
|
|
deployed: { controller: 'richese', location: home, effectiveForces: 1 }, lastUsed: null,
|
|
});
|
|
assert.deepEqual(projectRicheseNoField(state, true).private, state);
|
|
assert.throws(() => deployRicheseNoField(state, { tokenId: 'five', controller: 'richese', location: planet }));
|
|
assert.equal(isHomeworldNoFieldLocation(state.deployed!.location), true);
|
|
assert.equal(isBoardNoFieldLocation(state.deployed!.location), false);
|
|
});
|
|
|
|
void test('Homeworld reveal returns only existing reserves and keeps the prior shipment history', () => {
|
|
for (const [tokenId, reserves, forces] of [['zero', 5, 0], ['three', 1, 1], ['five', 3, 3]] as const) {
|
|
const before = placed(home, tokenId);
|
|
const snapshot = structuredClone(before);
|
|
const result = revealRicheseNoField(before, { tokenId, reserves, cause: 'battle' });
|
|
assert.equal(result.forces, forces);
|
|
assert.deepEqual(result.location, home);
|
|
assert.equal(result.state.deployed, null);
|
|
assert.equal(result.state.lastShipped, tokenId);
|
|
assert.deepEqual(before, snapshot);
|
|
assert.throws(() => deployRicheseNoField(result.state, { tokenId, controller: 'richese', location: planet }));
|
|
}
|
|
});
|
|
|
|
void test('board and Homeworld location custody survives JSON and a face-down recall', () => {
|
|
const board = placed(planet);
|
|
assert.equal(isBoardNoFieldLocation(board.deployed!.location), true);
|
|
assert.equal(isHomeworldNoFieldLocation(board.deployed!.location), false);
|
|
assert.deepEqual(JSON.parse(JSON.stringify(board)), board);
|
|
validateRicheseNoField(JSON.parse(JSON.stringify(board)) as RicheseNoField);
|
|
const moved = moveRicheseNoField(board, 'three', home);
|
|
assert.deepEqual(board.deployed!.location, planet);
|
|
assert.deepEqual(moved.deployed!.location, home);
|
|
const loaded = JSON.parse(JSON.stringify(moved)) as RicheseNoField;
|
|
validateRicheseNoField(loaded);
|
|
const returned = recallRicheseNoField(loaded, 'three');
|
|
assert.equal(returned.deployed, null);
|
|
assert.equal(returned.hiddenReturn, true);
|
|
assert.equal(projectRicheseNoField(returned, false).lastUsed, null);
|
|
assert.throws(() => deployRicheseNoField(returned, { tokenId: 'three', controller: 'richese', location: home }));
|
|
});
|
|
|
|
void test('Homeworld locations require a canonical identity and cannot mix with territory shape', () => {
|
|
const fresh = createRicheseNoField(ids);
|
|
for (const location of [
|
|
{ homeworld: 'homeworld:emperor:salusa' }, { homeworld: 'homeworld:choam' },
|
|
]) assert.doesNotThrow(() => deployRicheseNoField(fresh, { tokenId: 'zero', controller: 'richese', location }));
|
|
for (const location of [
|
|
{ homeworld: 'homeworld:unknown' }, { homeworld: '' },
|
|
{ homeworld: 'homeworld:atreides', territory: 'polar_sink', sector: 0 },
|
|
{ territory: 'homeworld:atreides', sector: 0 },
|
|
]) assert.throws(() => deployRicheseNoField(fresh, {
|
|
tokenId: 'zero', controller: 'richese', location: location as NoFieldLocation,
|
|
}));
|
|
});
|