import { test } from 'node:test'; import assert from 'node:assert/strict'; import { isLegalAction, resolveTurn } from '../src/shared/rules.js'; const actions = ['charge', 'blast', 'guard', 'beam', 'dodge'] as const; const winners = [ [null, 1, null, 1, null], [0, null, null, 1, null], [null, null, null, 1, null], [0, 0, 0, null, null], [null, null, null, null, null], ]; for (let a = 0; a < 5; a++) for (let b = 0; b < 5; b++) { test(`${actions[a]} / ${actions[b]} follows the agreed outcome table`, () => { const result = resolveTurn([3, 3], [actions[a], actions[b]]) as { winnerIndex: number | null }; assert.equal(result.winnerIndex, winners[a][b]); }); } test('first turn allows only charge and guard; unknown actions are rejected', () => { assert.equal(isLegalAction('charge', 0), true); assert.equal(isLegalAction('guard', 0), true); for (const action of ['blast', 'beam', 'dodge', 'fake']) assert.equal(isLegalAction(action, 0), false); }); test('energy is spent on evasion and clashing attacks, and a hit cancels charging', () => { assert.deepEqual(resolveTurn([3, 1], ['beam', 'dodge']), { energy: [0, 0], winnerIndex: null, hitIndex: null }); assert.deepEqual(resolveTurn([1, 0], ['blast', 'charge']), { energy: [0, 0], winnerIndex: 0, hitIndex: 1 }); assert.deepEqual(resolveTurn([1, 1], ['blast', 'blast']), { energy: [0, 0], winnerIndex: null, hitIndex: null }); assert.deepEqual(resolveTurn([0, 0], ['charge', 'charge']), { energy: [1, 1], winnerIndex: null, hitIndex: null }); assert.deepEqual(resolveTurn([0, 1], ['guard', 'dodge']), { energy: [0, 0], winnerIndex: null, hitIndex: null }); }); test('illegal actions cannot spend energy or mutate the input', () => { const energy: [number, number] = [0, 0]; assert.throws(() => resolveTurn(energy, ['blast', 'charge'])); assert.deepEqual(energy, [0, 0]); });