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 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 08:58:59 +00:00
co-authored by Claude Opus 4.6
parent 57af4e6a25
commit 1f532681df
+57
View File
@@ -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<string>()
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 },