Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rate games accounting for first move advantage (fix #6818) #16281

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d9d4b2a
Rate games with first move advantage (fix #6818)
ddugovic Oct 27, 2024
aa180ce
Code cleanup (reorder parameters)
ddugovic Oct 27, 2024
6194f07
Allow advantage even for BinaryResult
ddugovic Oct 27, 2024
1f2aff7
Code cleanup
ddugovic Oct 28, 2024
31ce96c
Code cleanup
ddugovic Oct 28, 2024
d22d638
Reduce first move advantage to 7.786 Elo
ddugovic Oct 30, 2024
8b5182f
Merge branch 'master' into glicko-first-move
ornicar Oct 30, 2024
759d6d4
Merge branch 'master' into glicko-first-move
ornicar Oct 30, 2024
e3ee697
Merge branch 'master' into glicko-first-move
ornicar Oct 30, 2024
3f6bbb6
sbt scalafmtAll
ornicar Oct 30, 2024
09692bf
Remove function getParticipants
ddugovic Oct 31, 2024
697a23b
sbt scalafmtAll
ddugovic Oct 31, 2024
421560c
Merge branch 'master' into glicko-first-move
ornicar Oct 31, 2024
da2b525
scala tweaks while reviewing
ornicar Oct 31, 2024
ef02272
type safety for glicko2.ColorAdvantage
ornicar Oct 31, 2024
d31bc4a
Rename GameResult to DuelResult
ddugovic Oct 31, 2024
300d191
Merge branch 'master' into glicko-first-move
ornicar Nov 1, 2024
2742c8c
Define crazyhouse first move advantage as 15.171 Elo
ddugovic Nov 2, 2024
887fea5
Merge branch 'master' into glicko-first-move
ornicar Nov 2, 2024
3e254fd
fix round perf updater doesn't use color advantages
ornicar Nov 2, 2024
8e89323
tweak scala imports
ornicar Nov 2, 2024
657c1e6
move color advantage constants
ornicar Nov 2, 2024
aaf28c4
Merge branch 'master' into glicko-first-move
ornicar Nov 3, 2024
e4195d4
Merge branch 'master' into glicko-first-move
ornicar Nov 4, 2024
49a6d9e
Revert tutor changes (WIP)
ddugovic Nov 17, 2024
69b6e34
Reintroduce FloatingResult
ddugovic Nov 17, 2024
41a260b
Fix compile errors
ddugovic Nov 17, 2024
9e31c15
Add code comment (lack of unit interval type)
ddugovic Nov 17, 2024
9ea827f
Merge branch 'master' into glicko-first-move
ornicar Nov 17, 2024
3e92964
unused code and scala syntax
ornicar Nov 17, 2024
f6e8bb4
better recover from erased users
ornicar Nov 17, 2024
850f2f8
rename Glicko.calculator
ornicar Nov 17, 2024
2774b03
use chess.Outcome for glicko Result
ornicar Nov 17, 2024
685e069
Delete bug (WIP)
ddugovic Nov 17, 2024
2f0db66
Delete bug (WIP)
ddugovic Nov 17, 2024
fad133d
Delete bug (WIP)
ddugovic Nov 17, 2024
6270982
Delete bug
ddugovic Nov 17, 2024
3083ac3
Attempt to fix type errors
ddugovic Nov 17, 2024
82a7c74
Attempt to fix type errors
ddugovic Nov 17, 2024
31543bd
Attempt to fix type errors
ddugovic Nov 17, 2024
32e91cf
Attempt to fix type errors
ddugovic Nov 17, 2024
3f71c58
Attempt to fix type errors
ddugovic Nov 18, 2024
85ecdac
Attempt to fix type errors
ddugovic Nov 18, 2024
f9e34be
Attempt to fix type errors
ddugovic Nov 18, 2024
d10fc58
Attempt to fix type errors
ddugovic Nov 18, 2024
0b4948e
scalafmtAll
ddugovic Nov 18, 2024
1bc6e04
Attempt to fix type errors
ddugovic Nov 18, 2024
aa374d3
Strongly type result color
ddugovic Nov 18, 2024
22a850a
Make RatingPeriodResults immutable
ddugovic Nov 18, 2024
e366158
Revert "Make RatingPeriodResults immutable"
ddugovic Nov 18, 2024
485d459
Make Rating immutable
ddugovic Nov 18, 2024
9aecdde
Rating facade WIP
ddugovic Nov 18, 2024
8b752af
Rating facade
ddugovic Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions modules/puzzle/src/main/PuzzleFinisher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -199,18 +199,16 @@ final private[puzzle] class PuzzleFinisher(
if player.clueless then glicko._1
else glicko._1.average(glicko._2, weightOf(angle, win))

private val VOLATILITY = lila.rating.Glicko.default.volatility
private val TAU = 0.75d
private val calculator = glicko2.RatingCalculator(VOLATILITY, TAU)
private val calculator = lila.rating.Glicko.system
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what the fudge... there was a horrible bug here.

private val calculator = glicko2.RatingCalculator(VOLATILITY, TAU)

was completely wrong here, as the constructor goes RatingCalculator(tau: Double = 0.75d, ratingPeriodsPerDay: Double = 0).

And that's what happens when we use weak types like Double. Arguments get swapped during some code change and no-one notices.

Gonna change these to proper opaque type so that it doesn't happen again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goodness, that is quite a mess and I feel somewhat bad for having introduced it years ago.

Parameter tau being near-zero may have caused solvers' volatility to increase slower when on a hot streak:

Smaller values of τ prevent the volatility measures from changing by large amounts, which in turn prevent enormous changes in ratings based on very improbable results.
http://www.glicko.net/glicko/glicko2.pdf

Parameter ratingPeriodsPerDay being 0;75d instead of 0.21436d may have caused solvers' RD to increase faster.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I wasn't blaming you for the bug. In fact I didn't check who introduced it and assumed it was me.
Thanks for fixing it tho.


def incPuzzlePlays(puzzleId: PuzzleId): Funit =
colls.puzzle.map(_.incFieldUnchecked($id(puzzleId), Puzzle.BSONFields.plays))

private def updateRatings(u1: glicko2.Rating, u2: glicko2.Rating, win: PuzzleWin): Unit =
val results = glicko2.GameRatingPeriodResults(
val results = glicko2.BinaryRatingPeriodResults(
List(
if win.yes then glicko2.GameResult(u1, u2, false)
else glicko2.GameResult(u2, u1, false)
if win.yes then glicko2.BinaryResult(u1, u2, false)
else glicko2.BinaryResult(u2, u1, false)
ornicar marked this conversation as resolved.
Show resolved Hide resolved
)
)
try calculator.updateRatings(results)
Expand Down
5 changes: 3 additions & 2 deletions modules/rating/src/main/Glicko.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ object Glicko:
// rating that can be lost or gained with a single game
val maxRatingDelta = 700

val tau = 0.75d
val system = glicko2.RatingCalculator(tau, ratingPeriodsPerDay)
val tau = 0.75d
val system = glicko2.RatingCalculator(tau, ratingPeriodsPerDay)
def calculator(advantage: Double) = glicko2.RatingCalculator(advantage, tau, ratingPeriodsPerDay)

def liveDeviation(p: Perf, reverse: Boolean): Double = {
system.previewDeviation(p.toRating, nowInstant, reverse)
Expand Down
4 changes: 4 additions & 0 deletions modules/rating/src/main/glicko2/Rating.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ final class Rating(
*/
def getGlicko2Rating: Double = convertRatingToGlicko2Scale(this.rating)

def getGlicko2RatingWithAdvantage(advantage: Double): Double = convertRatingToGlicko2Scale(
this.rating + advantage
)

/** Set the average skill value, taking in a value in Glicko2 scale.
*/
def setGlicko2Rating(r: Double) =
Expand Down
20 changes: 10 additions & 10 deletions modules/rating/src/main/glicko2/RatingCalculator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object RatingCalculator:
(ratingDeviation / MULTIPLIER)

final class RatingCalculator(
advantage: Double = 0.0d,
tau: Double = 0.75d,
ratingPeriodsPerDay: Double = 0
):
Expand All @@ -33,10 +34,9 @@ final class RatingCalculator(

val ratingPeriodsPerMilli: Double = ratingPeriodsPerDay * DAYS_PER_MILLI

/** <p>Run through all players within a resultset and calculate their new ratings.</p> <p>Players within the
* resultset who did not compete during the rating period will have see their deviation increase (in line
* with Prof Glickman's paper).</p> <p>Note that this method will clear the results held in the association
* resultset.</p>
/** Run through all players within a resultset and calculate their new ratings. Players within the resultset
* who did not compete during the rating period will have see their deviation increase (in line with Prof
* Glickman's paper). Note that this method will clear the results held in the association resultset.
*
* @param results
*/
Expand Down Expand Up @@ -166,13 +166,13 @@ final class RatingCalculator(
for result <- results do
v = v + ((Math.pow(g(result.getOpponent(player).getGlicko2RatingDeviation), 2))
* E(
player.getGlicko2Rating,
result.getOpponent(player).getGlicko2Rating,
player.getGlicko2RatingWithAdvantage(result.getAdvantage(advantage, player)),
result.getOpponent(player).getGlicko2RatingWithAdvantage(-result.getAdvantage(advantage, player)),
result.getOpponent(player).getGlicko2RatingDeviation
)
* (1.0 - E(
player.getGlicko2Rating,
result.getOpponent(player).getGlicko2Rating,
player.getGlicko2RatingWithAdvantage(result.getAdvantage(advantage, player)),
result.getOpponent(player).getGlicko2RatingWithAdvantage(-result.getAdvantage(advantage, player)),
result.getOpponent(player).getGlicko2RatingDeviation
)))
1 / v
Expand All @@ -193,8 +193,8 @@ final class RatingCalculator(
outcomeBasedRating = outcomeBasedRating
+ (g(result.getOpponent(player).getGlicko2RatingDeviation)
* (result.getScore(player) - E(
player.getGlicko2Rating,
result.getOpponent(player).getGlicko2Rating,
player.getGlicko2RatingWithAdvantage(result.getAdvantage(advantage, player)),
result.getOpponent(player).getGlicko2RatingWithAdvantage(-result.getAdvantage(advantage, player)),
result.getOpponent(player).getGlicko2RatingDeviation
)))
outcomeBasedRating
Expand Down
3 changes: 1 addition & 2 deletions modules/rating/src/main/glicko2/RatingPeriodResults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ trait RatingPeriodResults[R <: Result]():

class GameRatingPeriodResults(val results: List[GameResult]) extends RatingPeriodResults[GameResult]

class FloatingRatingPeriodResults(val results: List[FloatingResult])
extends RatingPeriodResults[FloatingResult]
class BinaryRatingPeriodResults(val results: List[BinaryResult]) extends RatingPeriodResults[BinaryResult]
48 changes: 28 additions & 20 deletions modules/rating/src/main/glicko2/Result.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package lila.rating.glicko2

trait Result:

def getAdvantage(advantage: Double, player: Rating): Double

def getScore(player: Rating): Double

def getOpponent(player: Rating): Rating
Expand All @@ -10,42 +12,48 @@ trait Result:

def players: List[Rating]

// score from 0 (opponent wins) to 1 (player wins)
class FloatingResult(player: Rating, opponent: Rating, score: Float) extends Result:
class BinaryResult(first: Rating, second: Rating, score: Boolean) extends Result:
private val POINTS_FOR_WIN = 1.0d
private val POINTS_FOR_LOSS = 0.0d

def getAdvantage(advantage: Double, p: Rating) = 0.0d

def getScore(p: Rating) = if p == player then score else 1 - score
def getScore(p: Rating) = if p == first then if score then POINTS_FOR_WIN else POINTS_FOR_LOSS
else if score then POINTS_FOR_LOSS
else POINTS_FOR_WIN

def getOpponent(p: Rating) = if p == player then opponent else player
def getOpponent(p: Rating) = if p == first then second else first

def participated(p: Rating) = p == player || p == opponent
def participated(p: Rating) = p == first || p == second

def players = List(player, opponent)
def players = List(first, second)

final class GameResult(winner: Rating, loser: Rating, isDraw: Boolean) extends Result:
final class GameResult(first: Rating, second: Rating, outcome: Option[Boolean]) extends Result:
private val POINTS_FOR_WIN = 1.0d
private val POINTS_FOR_LOSS = 0.0d
private val POINTS_FOR_DRAW = 0.5d
private val POINTS_FOR_LOSS = 0.0d

def players = List(winner, loser)
def players = List(first, second)

def participated(player: Rating) = player == winner || player == loser
def participated(player: Rating) = player == first || player == second

def getAdvantage(advantage: Double, player: Rating) =
if player == first then advantage / 2.0d else -advantage / 2.0d

/** Returns the "score" for a match.
*
* @param player
* @return
* 1 for a win, 0.5 for a draw and 0 for a loss
* 1 for a first player win, 0.5 for a draw and 0 for a first player loss
* @throws IllegalArgumentException
*/
def getScore(player: Rating): Double =
if isDraw then POINTS_FOR_DRAW
else if winner == player then POINTS_FOR_WIN
else if loser == player then POINTS_FOR_LOSS
else throw new IllegalArgumentException("Player did not participate in match");
def getScore(player: Rating): Double = outcome match
case Some(true) => if player == first then POINTS_FOR_WIN else POINTS_FOR_LOSS
case Some(false) => if player == first then POINTS_FOR_LOSS else POINTS_FOR_WIN
case _ => POINTS_FOR_DRAW

def getOpponent(player: Rating) =
if winner == player then loser
else if loser == player then winner
if first == player then second
else if second == player then first
else throw new IllegalArgumentException("Player did not participate in match");

override def toString = s"$winner vs $loser = $isDraw"
override def toString = s"$first vs $second = $outcome"
109 changes: 54 additions & 55 deletions modules/rating/src/test/RatingCalculatorTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import lila.rating.PerfExt.*
import glicko2.*

class RatingCalculatorTest extends lila.common.LilaTest:

def updateRatings(wRating: Rating, bRating: Rating, winner: Option[Color]) =
val result = winner match
case Some(chess.White) => Glicko.Result.Win
Expand All @@ -17,43 +16,43 @@ class RatingCalculatorTest extends lila.common.LilaTest:
val results = GameRatingPeriodResults(
List(
result match
case Glicko.Result.Draw => GameResult(wRating, bRating, true)
case Glicko.Result.Win => GameResult(wRating, bRating, false)
case Glicko.Result.Loss => GameResult(bRating, wRating, false)
case Glicko.Result.Win => GameResult(wRating, bRating, Some(true))
case Glicko.Result.Loss => GameResult(wRating, bRating, Some(false))
case Glicko.Result.Draw => GameResult(wRating, bRating, None)
)
)
Glicko.system.updateRatings(results, true)
Glicko.calculator(35.0d).updateRatings(results, true)

test("default deviation: white wins") {
val wr = default.toRating
val br = default.toRating
updateRatings(wr, br, White.some)
assertCloseTo(wr.rating, 1741d, 1d)
assertCloseTo(br.rating, 1258d, 1d)
assertCloseTo(wr.ratingDeviation, 396d, 1d)
assertCloseTo(br.ratingDeviation, 396d, 1d)
assertCloseTo(wr.volatility, 0.0899983, 0.00000001d)
assertCloseTo(br.volatility, 0.0899983, 0.0000001d)
assertCloseTo(wr.rating, 1729d, 1d)
assertCloseTo(br.rating, 1271d, 1d)
assertCloseTo(wr.ratingDeviation, 396.9015d, 0.0001d)
assertCloseTo(br.ratingDeviation, 396.9015d, 0.0001d)
assertCloseTo(wr.volatility, 0.0899980, 0.0000001d)
assertCloseTo(br.volatility, 0.0899980, 0.0000001d)
}
test("default deviation: black wins") {
val wr = default.toRating
val br = default.toRating
updateRatings(wr, br, Black.some)
assertCloseTo(wr.rating, 1258d, 1d)
assertCloseTo(br.rating, 1741d, 1d)
assertCloseTo(wr.ratingDeviation, 396d, 1d)
assertCloseTo(br.ratingDeviation, 396d, 1d)
assertCloseTo(wr.volatility, 0.0899983, 0.00000001d)
assertCloseTo(br.volatility, 0.0899983, 0.0000001d)
assertCloseTo(wr.rating, 1245d, 1d)
assertCloseTo(br.rating, 1755d, 1d)
assertCloseTo(wr.ratingDeviation, 396.9015d, 0.0001d)
assertCloseTo(br.ratingDeviation, 396.9015d, 0.0001d)
assertCloseTo(wr.volatility, 0.0899986, 0.0000001d)
assertCloseTo(br.volatility, 0.0899986, 0.0000001d)
}
test("default deviation: draw") {
val wr = default.toRating
val br = default.toRating
updateRatings(wr, br, None)
assertCloseTo(wr.rating, 1500d, 1d)
assertCloseTo(br.rating, 1500d, 1d)
assertCloseTo(wr.ratingDeviation, 396d, 1d)
assertCloseTo(br.ratingDeviation, 396d, 1d)
assertCloseTo(wr.rating, 1487d, 1d)
assertCloseTo(br.rating, 1513d, 1d)
assertCloseTo(wr.ratingDeviation, 396.9015d, 0.0001d)
assertCloseTo(br.ratingDeviation, 396.9015d, 0.0001d)
assertCloseTo(wr.volatility, 0.0899954, 0.0000001d)
assertCloseTo(br.volatility, 0.0899954, 0.0000001d)
}
Expand All @@ -67,32 +66,32 @@ class RatingCalculatorTest extends lila.common.LilaTest:
val wr = perf.toRating
val br = perf.toRating
updateRatings(wr, br, White.some)
assertCloseTo(wr.rating, 1517d, 1d)
assertCloseTo(br.rating, 1482d, 1d)
assertCloseTo(wr.ratingDeviation, 78d, 1d)
assertCloseTo(br.ratingDeviation, 78d, 1d)
assertCloseTo(wr.rating, 1515d, 1d)
assertCloseTo(br.rating, 1485d, 1d)
assertCloseTo(wr.ratingDeviation, 78.0967d, 0.0001d)
assertCloseTo(br.ratingDeviation, 78.0967d, 0.0001d)
assertCloseTo(wr.volatility, 0.06, 0.00001d)
assertCloseTo(br.volatility, 0.06, 0.00001d)
}
test("low deviation: black wins") {
val wr = perf.toRating
val br = perf.toRating
updateRatings(wr, br, Black.some)
assertCloseTo(wr.rating, 1482d, 1d)
assertCloseTo(br.rating, 1517d, 1d)
assertCloseTo(wr.ratingDeviation, 78d, 1d)
assertCloseTo(br.ratingDeviation, 78d, 1d)
assertCloseTo(wr.rating, 1481d, 1d)
assertCloseTo(br.rating, 1519d, 1d)
assertCloseTo(wr.ratingDeviation, 78.0967d, 0.0001d)
assertCloseTo(br.ratingDeviation, 78.0967d, 0.0001d)
assertCloseTo(wr.volatility, 0.06, 0.00001d)
assertCloseTo(br.volatility, 0.06, 0.00001d)
}
test("low deviation: draw") {
val wr = perf.toRating
val br = perf.toRating
updateRatings(wr, br, None)
assertCloseTo(wr.rating, 1500d, 1d)
assertCloseTo(br.rating, 1500d, 1d)
assertCloseTo(wr.ratingDeviation, 78d, 1d)
assertCloseTo(br.ratingDeviation, 78d, 1d)
assertCloseTo(wr.rating, 1498d, 1d)
assertCloseTo(br.rating, 1502d, 1d)
assertCloseTo(wr.ratingDeviation, 78.0967d, 0.0001d)
assertCloseTo(br.ratingDeviation, 78.0967d, 0.0001d)
assertCloseTo(wr.volatility, 0.06, 0.00001d)
assertCloseTo(br.volatility, 0.06, 0.00001d)
}
Expand All @@ -116,9 +115,9 @@ class RatingCalculatorTest extends lila.common.LilaTest:
val br = bP.toRating
updateRatings(wr, br, White.some)
assertCloseTo(wr.rating, 1422d, 1d)
assertCloseTo(br.rating, 1506d, 1d)
assertCloseTo(wr.ratingDeviation, 77d, 1d)
assertCloseTo(br.ratingDeviation, 105d, 1d)
assertCloseTo(br.rating, 1509d, 1d)
assertCloseTo(wr.ratingDeviation, 77.3966, 0.0001d)
assertCloseTo(br.ratingDeviation, 105.5926, 0.0001d)
assertCloseTo(wr.volatility, 0.06, 0.00001d)
assertCloseTo(br.volatility, 0.065, 0.00001d)
}
Expand All @@ -127,20 +126,20 @@ class RatingCalculatorTest extends lila.common.LilaTest:
val br = bP.toRating
updateRatings(wr, br, Black.some)
assertCloseTo(wr.rating, 1389d, 1d)
assertCloseTo(br.rating, 1568d, 1d)
assertCloseTo(wr.ratingDeviation, 78d, 1d)
assertCloseTo(br.ratingDeviation, 105d, 1d)
assertCloseTo(br.rating, 1571d, 1d)
assertCloseTo(wr.ratingDeviation, 77.3966, 0.0001d)
assertCloseTo(br.ratingDeviation, 105.5926, 0.0001d)
assertCloseTo(wr.volatility, 0.06, 0.00001d)
assertCloseTo(br.volatility, 0.065, 0.00001d)
}
test("mixed ratings and deviations: draw") {
val wr = wP.toRating
val br = bP.toRating
updateRatings(wr, br, None)
assertCloseTo(wr.rating, 1406d, 1d)
assertCloseTo(br.rating, 1537d, 1d)
assertCloseTo(wr.ratingDeviation, 78d, 1d)
assertCloseTo(br.ratingDeviation, 105.87d, 0.01d)
assertCloseTo(wr.rating, 1405d, 1d)
assertCloseTo(br.rating, 1540d, 1d)
assertCloseTo(wr.ratingDeviation, 77.3966, 0.0001d)
assertCloseTo(br.ratingDeviation, 105.5926, 0.0001d)
assertCloseTo(wr.volatility, 0.06, 0.00001d)
assertCloseTo(br.volatility, 0.065, 0.00001d)
}
Expand All @@ -164,32 +163,32 @@ class RatingCalculatorTest extends lila.common.LilaTest:
val wr = wP.toRating
val br = bP.toRating
updateRatings(wr, br, White.some)
assertCloseTo(wr.rating, 1216.7d, 0.1d)
assertCloseTo(br.rating, 1636d, 0.1d)
assertCloseTo(wr.ratingDeviation, 59.9d, 0.1d)
assertCloseTo(br.ratingDeviation, 196.9d, 0.1d)
assertCloseTo(wr.rating, 1216.6d, 0.1d)
assertCloseTo(br.rating, 1638.4d, 0.1d)
assertCloseTo(wr.ratingDeviation, 59.8839d, 0.0001d)
assertCloseTo(br.ratingDeviation, 196.3840, 0.0001d)
assertCloseTo(wr.volatility, 0.053013, 0.000001d)
assertCloseTo(br.volatility, 0.062028, 0.000001d)
}
test("more mixed ratings and deviations: black wins") {
val wr = wP.toRating
val br = bP.toRating
updateRatings(wr, br, Black.some)
assertCloseTo(wr.rating, 1199.3d, 0.1d)
assertCloseTo(br.rating, 1855.4d, 0.1d)
assertCloseTo(wr.ratingDeviation, 59.9d, 0.1d)
assertCloseTo(br.ratingDeviation, 196.9d, 0.1d)
assertCloseTo(wr.rating, 1199.2d, 0.1d)
assertCloseTo(br.rating, 1856.5d, 0.1d)
assertCloseTo(wr.ratingDeviation, 59.8839d, 0.0001d)
assertCloseTo(br.ratingDeviation, 196.3840, 0.0001d)
assertCloseTo(wr.volatility, 0.052999, 0.000001d)
assertCloseTo(br.volatility, 0.061999, 0.000001d)
}
test("more mixed ratings and deviations: draw") {
val wr = wP.toRating
val br = bP.toRating
updateRatings(wr, br, None)
assertCloseTo(wr.rating, 1208.0, 0.1d)
assertCloseTo(br.rating, 1745.7, 0.1d)
assertCloseTo(wr.ratingDeviation, 59.90056, 0.1d)
assertCloseTo(br.ratingDeviation, 196.98729, 0.1d)
assertCloseTo(wr.rating, 1207.9, 0.1d)
assertCloseTo(br.rating, 1747.5, 0.1d)
assertCloseTo(wr.ratingDeviation, 59.8839, 0.0001d)
assertCloseTo(br.ratingDeviation, 196.3840, 0.0001d)
assertCloseTo(wr.volatility, 0.053002, 0.000001d)
assertCloseTo(br.volatility, 0.062006, 0.000001d)
}
Expand Down
Loading