Skip to content

Commit

Permalink
Rating color advantage test WIP (lichess-org#598)
Browse files Browse the repository at this point in the history
  • Loading branch information
ddugovic committed Nov 22, 2024
1 parent 06c1662 commit 8d95d64
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 50 deletions.
9 changes: 6 additions & 3 deletions test-kit/src/test/scala/rating/glicko/GlickoCalculator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import chess.{ ByColor, Outcome }
import munit.ScalaCheckSuite

class GlickoCalculatorTest extends ScalaCheckSuite with chess.MunitExtensions:
// Validate results with reference implementations
// http://www.glicko.net/glicko/glicko2.pdf
val R: Double = 1500d
val RD: Double = 350d
val V: Double = 0.06d

val calc = GlickoCalculator(
ratingPeriodsPerDay = RatingPeriodsPerDay(0.21436d)
Expand All @@ -15,9 +20,7 @@ class GlickoCalculatorTest extends ScalaCheckSuite with chess.MunitExtensions:
{
val players = ByColor.fill:
Player(
Glicko(rating = 1500d, deviation = 500d, volatility = 0.09d),
numberOfResults = 0,
lastRatingPeriodEnd = None
Glicko(rating = R, deviation = RD, volatility = V)
)
test("default deviation: white wins"):
val (w, b) = computeGame(players, Outcome.white)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,129 @@
package chess
package rating.glicko
package chess.rating.glicko

import cats.syntax.all.*
import chess.{ ByColor, Outcome }
import munit.ScalaCheckSuite

class RatingCalculatorWithColorAdvantageTest extends ScalaCheckSuite with chess.MunitExtensions:

val smallAdvantage = GlickoCalculator(
Tau.default,
RatingPeriodsPerDay.default,
ColorAdvantage(7d)
)

// test that the rating calculator correctly applies the color advantage
import Outcome.*
class GlickoCalculatorTest extends ScalaCheckSuite with chess.MunitExtensions:
// Validate results with reference implementations
// http://www.glicko.net/glicko/glicko2.pdf
// If the player is unrated, set the rating to 1500
// and the RD to 350. Set the player’s volatility to
// 0.06 (this value depends on the particular application)
val R: Double = 1500d
val RD: Double = 350d
val V: Double = 0.06d

private def ratingDiff(r: Int, opRating: Int, outcome: Outcome, expected: Int)(using
munit.Location
) =
val player = Player(Glicko(r, RD, V))
val opponent = Player(Glicko(opRating, RD, V))
val game = Game(ByColor(player, opponent), outcome)
// TODO: calculator with color advantage
val calculator = GlickoCalculator()
calculator.computeGame(game)
val calc = GlickoCalculator(
ratingPeriodsPerDay = RatingPeriodsPerDay(0.21436d),
colorAdvantage = ColorAdvantage.standard
One
)

def computeGame(players: ByColor[Player], outcome: Outcome) =
calc.computeGame(Game(players, outcome), skipDeviationIncrease = true).get.toPair

{
val players = ByColor.fill:
Player(
Glicko(rating = R, deviation = RD, volatility = V)
)
test("default deviation: white wins"):
val (w, b) = computeGame(players, Outcome.white)
assertCloseTo(w.rating, 1741d, 1d)
assertCloseTo(b.rating, 1258d, 1d)
assertCloseTo(w.deviation, 396d, 1d)
assertCloseTo(b.deviation, 396d, 1d)
assertCloseTo(w.volatility, 0.0899983, 0.00000001d)
assertCloseTo(b.volatility, 0.0899983, 0.0000001d)
test("default deviation: black wins"):
val (w, b) = computeGame(players, Outcome.black)
assertCloseTo(w.rating, 1258d, 1d)
assertCloseTo(b.rating, 1741d, 1d)
assertCloseTo(w.deviation, 396d, 1d)
assertCloseTo(b.deviation, 396d, 1d)
assertCloseTo(w.volatility, 0.0899983, 0.00000001d)
assertCloseTo(b.volatility, 0.0899983, 0.0000001d)
test("default deviation: draw"):
val (w, b) = computeGame(players, Outcome.draw)
assertCloseTo(w.rating, 1500d, 1d)
assertCloseTo(b.rating, 1500d, 1d)
assertCloseTo(w.deviation, 396d, 1d)
assertCloseTo(b.deviation, 396d, 1d)
assertCloseTo(w.volatility, 0.0899954, 0.0000001d)
assertCloseTo(b.volatility, 0.0899954, 0.0000001d)
}

{
val players = ByColor(
Player(
Glicko(rating = 1400d, deviation = 79d, volatility = 0.06d),
numberOfResults = 0,
lastRatingPeriodEnd = None
),
Player(
Glicko(rating = 1550d, deviation = 110d, volatility = 0.065d),
numberOfResults = 0,
lastRatingPeriodEnd = None
)
)
test("mixed ratings and deviations: white wins"):
val (w, b) = computeGame(players, Outcome.white)
assertCloseTo(w.rating, 1422d, 1d)
assertCloseTo(b.rating, 1506d, 1d)
assertCloseTo(w.deviation, 77d, 1d)
assertCloseTo(b.deviation, 105d, 1d)
assertCloseTo(w.volatility, 0.06, 0.00001d)
assertCloseTo(b.volatility, 0.065, 0.00001d)
test("mixed ratings and deviations: black wins"):
val (w, b) = computeGame(players, Outcome.black)
assertCloseTo(w.rating, 1389d, 1d)
assertCloseTo(b.rating, 1568d, 1d)
assertCloseTo(w.deviation, 78d, 1d)
assertCloseTo(b.deviation, 105d, 1d)
assertCloseTo(w.volatility, 0.06, 0.00001d)
assertCloseTo(b.volatility, 0.065, 0.00001d)
test("mixed ratings and deviations: draw"):
val (w, b) = computeGame(players, Outcome.draw)
assertCloseTo(w.rating, 1406d, 1d)
assertCloseTo(b.rating, 1537d, 1d)
assertCloseTo(w.deviation, 78d, 1d)
assertCloseTo(b.deviation, 105.87d, 0.01d)
assertCloseTo(w.volatility, 0.06, 0.00001d)
assertCloseTo(b.volatility, 0.065, 0.00001d)
}

test("new rating calculation over one game"):
ratingDiff(1500, 1500, white, 20)
ratingDiff(1500, 1500, black, -20)
ratingDiff(1500, 1500, draw, 0)
ratingDiff(1500, 1900, white, 37)
ratingDiff(1500, 1900, black, -3)
ratingDiff(1500, 1900, draw, 17)
ratingDiff(1500, 2900, white, 37)
ratingDiff(1500, 2900, black, -3)
ratingDiff(1500, 2900, draw, 17)
ratingDiff(1500, 1600, white, 26)
ratingDiff(1500, 1600, black, -14)
ratingDiff(1500, 1600, draw, 6)
ratingDiff(2000, 1600, white, 3)
ratingDiff(2000, 1600, black, -37)
ratingDiff(2000, 1600, draw, -17)
ratingDiff(2000, 1000, white, 3)
ratingDiff(2000, 1000, black, -37)
ratingDiff(2000, 1000, draw, -17)
ratingDiff(2000, 1900, white, 14)
ratingDiff(2000, 1900, black, -26)
ratingDiff(2000, 1900, draw, -6)
{
val players = ByColor(
Player(
Glicko(rating = 1200d, deviation = 60d, volatility = 0.053d),
numberOfResults = 0,
lastRatingPeriodEnd = None
),
Player(
Glicko(rating = 1850d, deviation = 200d, volatility = 0.062d),
numberOfResults = 0,
lastRatingPeriodEnd = None
)
)
test("more mixed ratings and deviations: white wins"):
val (w, b) = computeGame(players, Outcome.white)
assertCloseTo(w.rating, 1216.7d, 0.1d)
assertCloseTo(b.rating, 1636d, 0.1d)
assertCloseTo(w.deviation, 59.9d, 0.1d)
assertCloseTo(b.deviation, 196.9d, 0.1d)
assertCloseTo(w.volatility, 0.053013, 0.000001d)
assertCloseTo(b.volatility, 0.062028, 0.000001d)
test("more mixed ratings and deviations: black wins"):
val (w, b) = computeGame(players, Outcome.black)
assertCloseTo(w.rating, 1199.3d, 0.1d)
assertCloseTo(b.rating, 1855.4d, 0.1d)
assertCloseTo(w.deviation, 59.9d, 0.1d)
assertCloseTo(b.deviation, 196.9d, 0.1d)
assertCloseTo(w.volatility, 0.052999, 0.000001d)
assertCloseTo(b.volatility, 0.061999, 0.000001d)
test("more mixed ratings and deviations: draw"):
val (w, b) = computeGame(players, Outcome.draw)
assertCloseTo(w.rating, 1208.0, 0.1d)
assertCloseTo(b.rating, 1745.7, 0.1d)
assertCloseTo(w.deviation, 59.90056, 0.1d)
assertCloseTo(b.deviation, 196.98729, 0.1d)
assertCloseTo(w.volatility, 0.053002, 0.000001d)
assertCloseTo(b.volatility, 0.062006, 0.000001d)
}

0 comments on commit 8d95d64

Please sign in to comment.