From 5746f15b8cf508a54c2ecc5aec87b2453196558d Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 20 Nov 2024 12:22:56 +0100 Subject: [PATCH] foundations for rating color advantage Before changing the implementation to use the color advantage, the implementer should start with adding tests to test-kit/src/test/scala/rating/glicko/impl/RatingCalculatorWithColorAdvantageTest.scala ```sh sbt project testKit ~testQuick chess.rating.glicko.* ``` --- .../src/main/scala/glicko/GlickoCalculator.scala | 5 +++-- rating/src/main/scala/glicko/impl/Rating.scala | 9 ++++++--- .../main/scala/glicko/impl/RatingCalculator.scala | 3 ++- rating/src/main/scala/glicko/model.scala | 7 +++++++ .../GlickoCalculatorWithColorAdvantageTest.scala | 14 ++++++++++++++ 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 test-kit/src/test/scala/rating/glicko/GlickoCalculatorWithColorAdvantageTest.scala diff --git a/rating/src/main/scala/glicko/GlickoCalculator.scala b/rating/src/main/scala/glicko/GlickoCalculator.scala index d7075683b..4ae48ee81 100644 --- a/rating/src/main/scala/glicko/GlickoCalculator.scala +++ b/rating/src/main/scala/glicko/GlickoCalculator.scala @@ -9,10 +9,11 @@ import scala.util.Try /* Purely functional interface hiding the mutable implementation */ final class GlickoCalculator( tau: Tau = Tau.default, - ratingPeriodsPerDay: RatingPeriodsPerDay = RatingPeriodsPerDay.default + ratingPeriodsPerDay: RatingPeriodsPerDay = RatingPeriodsPerDay.default, + colorAdvantage: ColorAdvantage = ColorAdvantage.zero ): - private val calculator = new impl.RatingCalculator(tau, ratingPeriodsPerDay) + private val calculator = new impl.RatingCalculator(tau, ratingPeriodsPerDay, colorAdvantage) // Simpler use case: a single game def computeGame(game: Game, skipDeviationIncrease: Boolean = false): Try[ByColor[Player]] = diff --git a/rating/src/main/scala/glicko/impl/Rating.scala b/rating/src/main/scala/glicko/impl/Rating.scala index 66c82e826..a8eecf190 100644 --- a/rating/src/main/scala/glicko/impl/Rating.scala +++ b/rating/src/main/scala/glicko/impl/Rating.scala @@ -16,11 +16,14 @@ final private[glicko] class Rating( private[impl] var workingRatingDeviation: Double = scala.compiletime.uninitialized private[impl] var workingVolatility: Double = scala.compiletime.uninitialized - /** Return the average skill value of the player scaled down to the scale used by the algorithm's internal - * workings. - */ + /** Return the average skill value of the player + * scaled down to the scale used by the algorithm's internal workings. + */ private[impl] def getGlicko2Rating: Double = convertRatingToGlicko2Scale(this.rating) + private[impl] def getGlicko2RatingWithAdvantage(advantage: ColorAdvantage): Double = + convertRatingToGlicko2Scale(this.rating + advantage.value) + /** Set the average skill value, taking in a value in Glicko2 scale. */ private[impl] def setGlicko2Rating(r: Double) = diff --git a/rating/src/main/scala/glicko/impl/RatingCalculator.scala b/rating/src/main/scala/glicko/impl/RatingCalculator.scala index 331cfb494..18c43b4bb 100644 --- a/rating/src/main/scala/glicko/impl/RatingCalculator.scala +++ b/rating/src/main/scala/glicko/impl/RatingCalculator.scala @@ -24,7 +24,8 @@ private object RatingCalculator: final private[glicko] class RatingCalculator( tau: Tau = Tau.default, - ratingPeriodsPerDay: RatingPeriodsPerDay = RatingPeriodsPerDay.default + ratingPeriodsPerDay: RatingPeriodsPerDay = RatingPeriodsPerDay.default, + colorAdvantage: ColorAdvantage = ColorAdvantage.zero ): import RatingCalculator.* diff --git a/rating/src/main/scala/glicko/model.scala b/rating/src/main/scala/glicko/model.scala index 154942154..387e25b1e 100644 --- a/rating/src/main/scala/glicko/model.scala +++ b/rating/src/main/scala/glicko/model.scala @@ -48,3 +48,10 @@ object Tau extends OpaqueDouble[Tau]: opaque type RatingPeriodsPerDay = Double object RatingPeriodsPerDay extends OpaqueDouble[RatingPeriodsPerDay]: val default: RatingPeriodsPerDay = 0d + +opaque type ColorAdvantage = Double +object ColorAdvantage extends OpaqueDouble[ColorAdvantage]: + val zero: ColorAdvantage = 0d + val standard: ColorAdvantage = 7.786d + val crazyhouse: ColorAdvantage = 15.171d + extension (c: ColorAdvantage) def negate: ColorAdvantage = -c diff --git a/test-kit/src/test/scala/rating/glicko/GlickoCalculatorWithColorAdvantageTest.scala b/test-kit/src/test/scala/rating/glicko/GlickoCalculatorWithColorAdvantageTest.scala new file mode 100644 index 000000000..568641f46 --- /dev/null +++ b/test-kit/src/test/scala/rating/glicko/GlickoCalculatorWithColorAdvantageTest.scala @@ -0,0 +1,14 @@ +package chess.rating.glicko + +import cats.syntax.all.* +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