test: add simultaneous KO and negative ELO edge cases

Adds tests for both-bots-at-0-HP tiebreaker asymmetry (botA penalized
first) and verifies ELO can go negative when 0-rated bot loses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-13 10:10:49 +00:00
co-authored by Claude Opus 4.6
parent ffad432e5b
commit 2dd09fb954
+43
View File
@@ -243,6 +243,49 @@ describe('challenge type rotation', () => {
})
})
describe('simultaneous KO — both bots reach 0 HP same round', () => {
it('when both bots KO same round, botA is penalized first (asymmetric tiebreaker)', () => {
// Simulate the orchestrator's HP application logic:
// hpB = Math.max(0, hpB - result.botADamage)
// hpA = Math.max(0, hpA - result.botBDamage)
// Then: if (hpA <= 0 || hpB <= 0) winnerId = hpA <= 0 ? botB.id : botA.id
const KO_THRESHOLD = 0
let hpA = 25 // low enough to die this round
let hpB = 25
// One correct one wrong → winner gets ~30 damage from baseDamage 20 + margin
const ch = makeChallenge(['Bitcoin'])
// Both correct, A faster → A deals more damage to B, but B also deals damage to A
const result = scoreRound(ch, botA, botB, resp('Bitcoin', 200), resp('Bitcoin', 8000), null, 0, 0)
// Apply damage (mimicking orchestrator logic)
hpB = Math.max(KO_THRESHOLD, hpB - result.botADamage)
hpA = Math.max(KO_THRESHOLD, hpA - result.botBDamage)
// If both at or below 0, the orchestrator checks hpA first
if (hpA <= KO_THRESHOLD && hpB <= KO_THRESHOLD) {
// Orchestrator: winnerId = hpA <= 0 ? botB.id : botA.id
// When BOTH are at 0, hpA <= 0 is checked first → botB wins
const winnerId = hpA <= KO_THRESHOLD ? 'b1' : 'a1'
expect(winnerId).toBe('b1')
} else if (hpA <= KO_THRESHOLD) {
expect(true).toBe(true) // botB wins, expected
} else if (hpB <= KO_THRESHOLD) {
expect(true).toBe(true) // botA wins, expected
}
// Regardless of who dies, both HP should be at or below starting value
expect(hpA).toBeLessThanOrEqual(25)
expect(hpB).toBeLessThanOrEqual(25)
})
it('ELO can go negative when 0-rated bot loses', () => {
const result = calculateElo(0, 0)
// Loser at 0 should go negative (no clamping in the formula)
expect(result.newLoserElo).toBeLessThan(0)
})
})
describe('10-round double timeout simulation', () => {
it('both bots timeout every round: fight ends as draw, equal HP, no infinite loop', () => {
const STARTING_HP = 200