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.
go-elo/strategy_test.go

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)
}
}