Tab/space alignment consistency from running gofmt on all packages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
55 lines
1.5 KiB
Go
55 lines
1.5 KiB
Go
package arena
|
||
|
||
import "math"
|
||
|
||
// WinRateResult holds the observed win rate and its 95% Wilson score confidence interval.
|
||
type WinRateResult struct {
|
||
Wins int
|
||
Total int // non-error matches only
|
||
Rate float64 // observed win rate (0–1)
|
||
Lower float64 // 95% CI lower bound
|
||
Upper float64 // 95% CI upper bound
|
||
}
|
||
|
||
// WinRate computes the win rate and Wilson score 95% confidence interval
|
||
// for wins out of total valid matches. When total == 0, all values are 0.5.
|
||
//
|
||
// Wilson score interval:
|
||
//
|
||
// center = (p̂ + z²/2n) / (1 + z²/n)
|
||
// margin = z * sqrt(p̂(1-p̂)/n + z²/4n²) / (1 + z²/n)
|
||
// CI = [center − margin, center + margin]
|
||
//
|
||
// Using z = 1.96 (95% two-tailed confidence).
|
||
func WinRate(wins, total int) WinRateResult {
|
||
if total == 0 {
|
||
return WinRateResult{Rate: 0.5, Lower: 0.0, Upper: 1.0}
|
||
}
|
||
|
||
const z = 1.96 // 95% CI
|
||
p := float64(wins) / float64(total)
|
||
n := float64(total)
|
||
z2 := z * z
|
||
|
||
center := (p + z2/(2*n)) / (1 + z2/n)
|
||
margin := z * math.Sqrt(p*(1-p)/n+z2/(4*n*n)) / (1 + z2/n)
|
||
|
||
lower := math.Max(0, center-margin)
|
||
upper := math.Min(1, center+margin)
|
||
|
||
return WinRateResult{
|
||
Wins: wins,
|
||
Total: total,
|
||
Rate: p,
|
||
Lower: lower,
|
||
Upper: upper,
|
||
}
|
||
}
|
||
|
||
// ComputeFromResult builds a WinRateResult from a tournament Result.
|
||
// Only non-error matches are counted; draws count as 0.5 wins.
|
||
func ComputeFromResult(r *Result) WinRateResult {
|
||
total := r.Wins + r.Losses + r.Draws
|
||
// Count draws as half-wins for the rate; wins/total integers use integer wins.
|
||
return WinRate(r.Wins, total)
|
||
}
|