SvelteKit 5 single-player version against a heuristic bot. The engine resolves all forty spells simultaneously as the rules describe, the ledger of gestures is the interface, and the duel is saved in the browser. Tester feedback rounds added per-hand planning, threat evidence, a result strip with one-time rule explanations, a phone layout and a structured rules page. Deploys as a static site behind Caddy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
43 lines
1.3 KiB
TypeScript
43 lines
1.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { chooseBotTurn } from './bot';
|
|
import { resolveTurn } from './resolve';
|
|
import { createGame } from './state';
|
|
|
|
function lcg(seed: number) {
|
|
let s = seed >>> 0;
|
|
return () => {
|
|
s = (s * 1664525 + 1013904223) >>> 0;
|
|
return s / 4294967296;
|
|
};
|
|
}
|
|
|
|
describe('bot versus bot', () => {
|
|
it('sixty duels end without errors and by death, not surrender', () => {
|
|
let finished = 0;
|
|
let byDeath = 0;
|
|
let totalTurns = 0;
|
|
for (let seed = 1; seed <= 60; seed++) {
|
|
let state = createGame({ A: 'Black', B: 'White' }, seed);
|
|
const rngA = lcg(seed * 7 + 1);
|
|
const rngB = lcg(seed * 13 + 5);
|
|
for (let turn = 0; turn < 80 && !state.over; turn++) {
|
|
const a = chooseBotTurn(state, 'A', rngA);
|
|
const b = chooseBotTurn(state, 'B', rngB);
|
|
state = resolveTurn(state, { A: a, B: b });
|
|
}
|
|
if (state.over) {
|
|
finished++;
|
|
totalTurns += state.turn;
|
|
if (!state.wizards.A.alive || !state.wizards.B.alive) byDeath++;
|
|
}
|
|
for (const w of Object.values(state.wizards)) {
|
|
expect(w.hp).toBeGreaterThanOrEqual(0);
|
|
expect(w.hp).toBeLessThanOrEqual(15);
|
|
}
|
|
}
|
|
expect(finished).toBeGreaterThan(45);
|
|
expect(byDeath).toBe(finished);
|
|
expect(totalTurns / finished).toBeLessThan(40);
|
|
}, 60000);
|
|
});
|