Files
Dune/tests/combat.test.ts
2026-09-08 07:37:52 +10:00

93 lines
2.8 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { casualtyOptions, type CombatForces } from '../game/combat';
void test('printed advanced battle example permits retaining either ordinary or elite forces', () => {
const options = casualtyOptions(
{ normal: 5, elite: 1, eliteStrength: 2, freeSupport: false },
3,
1,
);
assert.ok(
options.some((o) => o.normal === 2 && o.elite === 1 && o.paidElite === 1),
);
assert.ok(
options.some((o) => o.normal === 5 && o.elite === 0 && o.paidNormal === 1),
);
});
void test('unsupported strength, Fremen exemption, and Sardaukar versus Fremen remain distinct', () => {
assert.deepEqual(
casualtyOptions(
{ normal: 10, elite: 0, eliteStrength: 2, freeSupport: false },
4,
2,
),
[{ normal: 6, elite: 0, paidNormal: 2, paidElite: 0 }],
);
const fremen: CombatForces = {
normal: 4,
elite: 3,
eliteStrength: 2,
freeSupport: true,
};
assert.ok(
casualtyOptions(fremen, 4, 0).some((o) => o.elite === 2 && o.normal === 0),
);
assert.deepEqual(casualtyOptions(fremen, 3.5, 0), []);
assert.deepEqual(casualtyOptions(fremen, 4, 1), []);
assert.deepEqual(
casualtyOptions(
{ normal: 0, elite: 1, eliteStrength: 1, freeSupport: false },
2,
1,
),
[],
);
assert.equal(
casualtyOptions(
{ normal: 0, elite: 1, eliteStrength: 1, freeSupport: false },
1,
1,
).length,
1,
);
});
void test('casualty solver matches exhaustive independent token assignments', () => {
for (const eliteStrength of [1, 2] as const)
for (let support = 0; support <= 5; support++)
for (let halfDial = 0; halfDial <= 14; halfDial++) {
const brute = new Set<string>();
for (let code = 0; code < 3 ** 5; code++) {
let remaining = code,
paid = 0,
half = 0,
normal = 0,
elite = 0;
for (let token = 0; token < 5; token++) {
const state = remaining % 3;
remaining = Math.floor(remaining / 3);
if (!state) continue;
const value = token < 3 ? 1 : eliteStrength;
half += value * state;
if (state === 2) paid++;
if (token < 3) normal++;
else elite++;
}
if (paid === support && half === halfDial)
brute.add(`${normal}:${elite}`);
}
const actual = new Set(
casualtyOptions(
{ normal: 3, elite: 2, eliteStrength, freeSupport: false },
halfDial / 2,
support,
).map((o) => `${o.normal}:${o.elite}`),
);
assert.deepEqual(
actual,
brute,
`${eliteStrength}/${halfDial}/${support}`,
);
}
});