From 1f532681df524134e0b21f92491a34526ef08ae4 Mon Sep 17 00:00:00 2001 From: Dorian Date: Mon, 9 Mar 2026 08:58:59 +0000 Subject: [PATCH] test: add fight length and KO rate distribution test Simulates 1000 full fights with HP tracking (200 HP start, 7-10 rounds) to verify average fight length (5-10 rounds) and KO rate (20-80%). Validates the balance between damage output and HP pool. Co-Authored-By: Claude Opus 4.6 --- server/src/engine/lifecycle.test.ts | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/server/src/engine/lifecycle.test.ts b/server/src/engine/lifecycle.test.ts index 8bc76ed..1f810ef 100644 --- a/server/src/engine/lifecycle.test.ts +++ b/server/src/engine/lifecycle.test.ts @@ -420,6 +420,63 @@ describe('fight lifecycle', () => { } }) + it('average fight lasts 5-10 rounds with ~30-70% KO rate', () => { + const STARTING_HP = 200 + let totalRounds = 0 + let totalKOs = 0 + const totalFights = 1000 + + for (let f = 0; f < totalFights; f++) { + const eloA = 1000 + Math.floor(Math.random() * 800) + const eloB = 1000 + Math.floor(Math.random() * 800) + const botA = { id: 'fl-a', name: 'A' } + const botB = { id: 'fl-b', name: 'B' } + const usedTypes = new Set() + let hpA = STARTING_HP + let hpB = STARTING_HP + let comboA = 0 + let comboB = 0 + const maxRounds = 7 + Math.floor(Math.random() * 4) // 7-10 rounds like mock.ts + let rounds = 0 + + for (let r = 0; r < maxRounds; r++) { + const challenge = pickChallenge(usedTypes, null, undefined, r + 1) + usedTypes.add(challenge.type) + const respA = mockResponse(challenge, 'confident', eloA) + const respB = mockResponse(challenge, 'confident', eloB) + const result = scoreRound( + challenge, botA, botB, + { answer: respA.answer, timeMs: respA.timeMs, timedOut: respA.timedOut, error: respA.error }, + { answer: respB.answer, timeMs: respB.timeMs, timedOut: respB.timedOut, error: respB.error }, + null, comboA, comboB, + ) + + hpB = Math.max(0, hpB - result.botADamage) + hpA = Math.max(0, hpA - result.botBDamage) + if (result.winnerId === botA.id) { comboA++; comboB = 0 } + else if (result.winnerId === botB.id) { comboB++; comboA = 0 } + rounds++ + + if (hpA <= 0 || hpB <= 0) { + totalKOs++ + break + } + } + totalRounds += rounds + } + + const avgRounds = totalRounds / totalFights + const koRate = totalKOs / totalFights + + // Average fight should be 5-10 rounds + expect(avgRounds).toBeGreaterThan(4) + expect(avgRounds).toBeLessThan(10) + + // KO rate should be reasonable (30-70% of fights end in KO) + expect(koRate).toBeGreaterThan(0.2) + expect(koRate).toBeLessThan(0.8) + }) + it('Elo difference correlates with win rate', () => { const scenarios = [ { eloA: 1400, eloB: 1400, expectedAWinMin: 0.40, expectedAWinMax: 0.60 },