mirror of https://github.com/gabehf/go-elo.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
397 lines
7.6 KiB
397 lines
7.6 KiB
package elo_test
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/gabehf/go-elo"
|
|
)
|
|
|
|
// Equal precision to Go's default precision when printing with %f.
|
|
const float64EqualityThreshold = 1e-6
|
|
|
|
func almostEqual(a, b float64) bool {
|
|
return math.Abs(a-b) <= float64EqualityThreshold
|
|
}
|
|
|
|
type player struct {
|
|
elo float64
|
|
}
|
|
|
|
func (p *player) GetElo() float64 {
|
|
return p.elo
|
|
}
|
|
func (p *player) SetElo(e float64) {
|
|
p.elo = e
|
|
}
|
|
|
|
func TestDefaultCalculator(t *testing.T) {
|
|
c := elo.NewCalculatorBuilder().Build()
|
|
|
|
p1 := new(player)
|
|
p1.elo = 1600
|
|
p2 := new(player)
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.SetStrategy(elo.StrategyDefault)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomePlayerTwoWin,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1592.311901653) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1592.311901653, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1807.688098347) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1807.688098347, p2.elo)
|
|
}
|
|
|
|
p1.elo = 800
|
|
p2.elo = 1300
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomePlayerOneWin,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 830.296313114) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 830.296313114, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1269.703686886) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1269.703686886, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestScoredStrategy(t *testing.T) {
|
|
c := elo.NewCalculatorBuilder().
|
|
WithStrategy(elo.StrategyScored).
|
|
Build()
|
|
|
|
p1 := new(player)
|
|
p2 := new(player)
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 150,
|
|
PlayerTwoScore: 160,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1596.031949) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1596.031949, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1803.968051) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1803.968051, p2.elo)
|
|
}
|
|
|
|
p1.elo = 800
|
|
p2.elo = 1300
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 15,
|
|
PlayerTwoScore: 20,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 799.026465) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 799.026465, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1300.973535) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1300.973535, p2.elo)
|
|
}
|
|
|
|
p1.elo = 900
|
|
p2.elo = 700
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 6,
|
|
PlayerTwoScore: 0,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 907.688098) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 907.688098, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 692.311902) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 692.311902, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestWithKValue(t *testing.T) {
|
|
c := elo.NewCalculatorBuilder().WithKValue(55).Build()
|
|
|
|
p1 := new(player)
|
|
p1.elo = 1600
|
|
p2 := new(player)
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomePlayerTwoWin,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1586.786081) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1586.786081, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1813.213919) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1813.213919, p2.elo)
|
|
}
|
|
|
|
p1.elo = 800
|
|
p2.elo = 1300
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomePlayerOneWin,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 852.071788) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 852.071788, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1247.928212) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1247.928212, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestScoredWithKValue(t *testing.T) {
|
|
c := elo.NewCalculatorBuilder().
|
|
WithStrategy(elo.StrategyScored).
|
|
WithKValue(60).
|
|
Build()
|
|
|
|
p1 := new(player)
|
|
p2 := new(player)
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 230,
|
|
PlayerTwoScore: 160,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1626.883353) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1626.883353, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1773.116647) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1773.116647, p2.elo)
|
|
}
|
|
|
|
p1.elo = 800
|
|
p2.elo = 1300
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 0,
|
|
PlayerTwoScore: 4,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 796.805587) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 796.805587, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1303.194413) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1303.194413, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestScoredWithCloseScore(t *testing.T) {
|
|
c := elo.NewCalculatorBuilder().
|
|
WithStrategy(elo.StrategyScored).
|
|
Build()
|
|
|
|
p1 := new(player)
|
|
p2 := new(player)
|
|
p1.elo = 1200
|
|
p2.elo = 1100
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 501,
|
|
PlayerTwoScore: 500,
|
|
})
|
|
|
|
if p1.elo < 1200 {
|
|
t.Fail()
|
|
t.Log("Winning player's elo must always be higher than starting elo.")
|
|
}
|
|
if p2.elo > 1100 {
|
|
t.Fail()
|
|
t.Log("Losing player's elo must always be lower than starting elo.")
|
|
}
|
|
|
|
if !almostEqual(p1.elo, 1205.764713) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1205.764713, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1094.235287) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1094.235287, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestWithDeviation(t *testing.T) {
|
|
c := elo.NewCalculatorBuilder().
|
|
Build()
|
|
|
|
p1 := new(player)
|
|
p2 := new(player)
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomePlayerTwoWin,
|
|
})
|
|
|
|
o1 := p1.elo
|
|
o2 := p2.elo
|
|
|
|
c = elo.NewCalculatorBuilder().
|
|
WithDeviation(200).
|
|
Build()
|
|
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomePlayerTwoWin,
|
|
})
|
|
|
|
if o1 > p1.elo || o2 < p2.elo {
|
|
t.Fail()
|
|
t.Log("")
|
|
t.Logf("Tighter deviation should not result in more change to elo."+
|
|
"\nD=400 P1: %f, D=200 P1: %f\n"+
|
|
"D=400 P2: %f, D=200 P2: %f", o1, p1.elo, o2, p2.elo)
|
|
}
|
|
if !almostEqual(p1.elo, 1597.090909) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1597.090909, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1802.909091) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1802.909091, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestDraw(t *testing.T) {
|
|
|
|
// w/l
|
|
|
|
c := elo.NewCalculatorBuilder().
|
|
Build()
|
|
|
|
p1 := new(player)
|
|
p2 := new(player)
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomeDraw,
|
|
})
|
|
if !almostEqual(p1.elo, 1608.311902) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1608.311902, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1791.688098) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1791.688098, p2.elo)
|
|
}
|
|
|
|
// scored
|
|
|
|
c = elo.NewCalculatorBuilder().
|
|
WithStrategy(elo.StrategyScored).
|
|
Build()
|
|
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 500,
|
|
PlayerTwoScore: 500,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1608.311902) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1608.311902, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1791.688098) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1791.688098, p2.elo)
|
|
}
|
|
}
|
|
|
|
func TestIgnoreDraw(t *testing.T) {
|
|
|
|
// w/l
|
|
c := elo.NewCalculatorBuilder().
|
|
WithIgnoreDraws().
|
|
Build()
|
|
|
|
p1 := new(player)
|
|
p2 := new(player)
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m := c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
Outcome: elo.OutcomeDraw,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1600) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1600.0, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1800) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1600.0, p2.elo)
|
|
}
|
|
|
|
// scored
|
|
c = elo.NewCalculatorBuilder().
|
|
WithIgnoreDraws().
|
|
WithStrategy(elo.StrategyScored).
|
|
Build()
|
|
|
|
p1.elo = 1600
|
|
p2.elo = 1800
|
|
|
|
m = c.NewMatch(p1, p2)
|
|
m.Play(&elo.MatchResult{
|
|
PlayerOneScore: 500,
|
|
PlayerTwoScore: 500,
|
|
})
|
|
|
|
if !almostEqual(p1.elo, 1600) {
|
|
t.Fail()
|
|
t.Logf("Expected P1 Elo %f, got %f\n", 1600.0, p1.elo)
|
|
}
|
|
if !almostEqual(p2.elo, 1800) {
|
|
t.Fail()
|
|
t.Logf("Expected P2 Elo %f, got %f\n", 1600.0, p2.elo)
|
|
}
|
|
}
|