Skip to content

Commit

Permalink
add Glicko data type
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Nov 19, 2024
1 parent 3ff94c1 commit 18adc97
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 41 deletions.
33 changes: 25 additions & 8 deletions core/src/main/scala/glicko/glicko.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,28 @@ package glicko
import java.time.Instant
import scala.util.Try

case class Player(
case class Glicko(
rating: Double,
ratingDeviation: Double,
volatility: Double,
deviation: Double,
volatility: Double
):
def average(other: Glicko, weight: Float = 0.5f): Glicko =
if weight >= 1 then other
else if weight <= 0 then this
else
Glicko(
rating = rating * (1 - weight) + other.rating * weight,
deviation = deviation * (1 - weight) + other.deviation * weight,
volatility = volatility * (1 - weight) + other.volatility * weight
)
override def toString = f"${rating.toInt}/${deviation.toInt}/${volatility}%.3f"

case class Player(
glicko: Glicko,
numberOfResults: Int,
lastRatingPeriodEnd: Option[Instant] = None
)
):
export glicko.*

case class Game(players: ByColor[Player], outcome: Outcome)

Expand Down Expand Up @@ -70,16 +85,18 @@ final class GlickoCalculator(config: Config) extends GlickoCalculatorApi:

def toRating(player: Player) = impl.Rating(
rating = player.rating,
ratingDeviation = player.ratingDeviation,
ratingDeviation = player.deviation,
volatility = player.volatility,
numberOfResults = player.numberOfResults,
lastRatingPeriodEnd = player.lastRatingPeriodEnd
)

def toPlayer(rating: Rating) = Player(
rating = rating.rating,
ratingDeviation = rating.ratingDeviation,
volatility = rating.volatility,
glicko = Glicko(
rating = rating.rating,
deviation = rating.ratingDeviation,
volatility = rating.volatility
),
numberOfResults = rating.numberOfResults,
lastRatingPeriodEnd = rating.lastRatingPeriodEnd
)
56 changes: 23 additions & 33 deletions test-kit/src/test/scala/glicko/GlickoCalculator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,45 @@ class GlickoCalculatorTest extends ScalaCheckSuite with MunitExtensions:
{
val players = ByColor.fill:
Player(
rating = 1500d,
ratingDeviation = 500d,
volatility = 0.09d,
Glicko(rating = 1500d, deviation = 500d, volatility = 0.09d),
numberOfResults = 0,
lastRatingPeriodEnd = None
)
test("default deviation: white wins"):
val (w, b) = computeGame(players, Outcome.white)
assertCloseTo(w.rating, 1741d, 1d)
assertCloseTo(b.rating, 1258d, 1d)
assertCloseTo(w.ratingDeviation, 396d, 1d)
assertCloseTo(b.ratingDeviation, 396d, 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.ratingDeviation, 396d, 1d)
assertCloseTo(b.ratingDeviation, 396d, 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.ratingDeviation, 396d, 1d)
assertCloseTo(b.ratingDeviation, 396d, 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(
rating = 1400d,
ratingDeviation = 79d,
volatility = 0.06d,
Glicko(rating = 1400d, deviation = 79d, volatility = 0.06d),
numberOfResults = 0,
lastRatingPeriodEnd = None
),
Player(
rating = 1550d,
ratingDeviation = 110d,
volatility = 0.065d,
Glicko(rating = 1550d, deviation = 110d, volatility = 0.065d),
numberOfResults = 0,
lastRatingPeriodEnd = None
)
Expand All @@ -69,41 +63,37 @@ class GlickoCalculatorTest extends ScalaCheckSuite with MunitExtensions:
val (w, b) = computeGame(players, Outcome.white)
assertCloseTo(w.rating, 1422d, 1d)
assertCloseTo(b.rating, 1506d, 1d)
assertCloseTo(w.ratingDeviation, 77d, 1d)
assertCloseTo(b.ratingDeviation, 105d, 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.ratingDeviation, 78d, 1d)
assertCloseTo(b.ratingDeviation, 105d, 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.ratingDeviation, 78d, 1d)
assertCloseTo(b.ratingDeviation, 105.87d, 0.01d)
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)
}

{
val players = ByColor(
Player(
rating = 1200d,
ratingDeviation = 60d,
volatility = 0.053d,
Glicko(rating = 1200d, deviation = 60d, volatility = 0.053d),
numberOfResults = 0,
lastRatingPeriodEnd = None
),
Player(
rating = 1850d,
ratingDeviation = 200d,
volatility = 0.062d,
Glicko(rating = 1850d, deviation = 200d, volatility = 0.062d),
numberOfResults = 0,
lastRatingPeriodEnd = None
)
Expand All @@ -112,24 +102,24 @@ class GlickoCalculatorTest extends ScalaCheckSuite with MunitExtensions:
val (w, b) = computeGame(players, Outcome.white)
assertCloseTo(w.rating, 1216.7d, 0.1d)
assertCloseTo(b.rating, 1636d, 0.1d)
assertCloseTo(w.ratingDeviation, 59.9d, 0.1d)
assertCloseTo(b.ratingDeviation, 196.9d, 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.ratingDeviation, 59.9d, 0.1d)
assertCloseTo(b.ratingDeviation, 196.9d, 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.ratingDeviation, 59.90056, 0.1d)
assertCloseTo(b.ratingDeviation, 196.98729, 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 18adc97

Please sign in to comment.