|
1 {- |
|
2 Glicko2, as described in http://www.glicko.net/glicko/glicko2.pdf |
|
3 -} |
|
4 |
|
5 module OfficialServer.Glicko2 where |
|
6 |
|
7 data RatingData = RatingData { |
|
8 ratingValue |
|
9 , rD |
|
10 , volatility :: Double |
|
11 } |
|
12 data GameData = GameData { |
|
13 opponentRating :: RatingData, |
|
14 gameScore :: Double |
|
15 } |
|
16 |
|
17 τ, ε :: Double |
|
18 τ = 0.2 |
|
19 ε = 0.000001 |
|
20 |
|
21 g_φ :: Double -> Double |
|
22 g_φ φ = 1 / sqrt (1 + 3 * φ^2 / pi^2) |
|
23 |
|
24 calcE :: RatingData -> GameData -> (Double, Double, Double) |
|
25 calcE oldRating (GameData oppRating s) = ( |
|
26 1 / (1 + exp (g_φᵢ * (μᵢ - μ))) |
|
27 , g_φᵢ |
|
28 , s |
|
29 ) |
|
30 where |
|
31 μ = (ratingValue oldRating - 1500) / 173.7178 |
|
32 φ = rD oldRating / 173.7178 |
|
33 μᵢ = (ratingValue oppRating - 1500) / 173.7178 |
|
34 φᵢ = rD oppRating / 173.7178 |
|
35 g_φᵢ = g_φ φᵢ |
|
36 |
|
37 |
|
38 calcNewRating :: RatingData -> [GameData] -> (Int, RatingData) |
|
39 calcNewRating oldRating [] = (0, RatingData (ratingValue oldRating) (173.7178 * sqrt (φ ^ 2 + σ ^ 2)) σ) |
|
40 where |
|
41 φ = rD oldRating / 173.7178 |
|
42 σ = volatility oldRating |
|
43 |
|
44 calcNewRating oldRating games = (length games, RatingData (173.7178 * μ' + 1500) (173.7178 * sqrt φ'sqr) σ') |
|
45 where |
|
46 _Es = map (calcE oldRating) games |
|
47 υ = 1 / sum (map υ_p _Es) |
|
48 υ_p (_Eᵢ, g_φᵢ, _) = g_φᵢ ^ 2 * _Eᵢ * (1 - _Eᵢ) |
|
49 _Δ = υ * part1 |
|
50 part1 = sum (map _Δ_p _Es) |
|
51 _Δ_p (_Eᵢ, g_φᵢ, sᵢ) = g_φᵢ * (sᵢ - _Eᵢ) |
|
52 |
|
53 μ = (ratingValue oldRating - 1500) / 173.7178 |
|
54 φ = rD oldRating / 173.7178 |
|
55 σ = volatility oldRating |
|
56 |
|
57 a = log (σ ^ 2) |
|
58 f :: Double -> Double |
|
59 f x = exp x * (_Δ ^ 2 - φ ^ 2 - υ - exp x) / 2 / (φ ^ 2 + υ + exp x) ^ 2 - (x - a) / τ ^ 2 |
|
60 |
|
61 _A = a |
|
62 _B = if _Δ ^ 2 > φ ^ 2 + υ then log (_Δ ^ 2 - φ ^ 2 - υ) else head . dropWhile ((>) 0 . f) . map (\k -> a - k * τ) $ [1 ..] |
|
63 fA = f _A |
|
64 fB = f _B |
|
65 σ' = (\(_A, _, _, _) -> exp (_A / 2)) . head . dropWhile (\(_A, _, _B, _) -> abs (_B - _A) > ε) $ iterate step5 (_A, fA, _B, fB) |
|
66 step5 (_A, fA, _B, fB) = let _C = _A + (_A - _B) * fA / (fB - fA); fC = f _C in |
|
67 if fC * fB < 0 then (_B, fB, _C, fC) else (_A, fA / 2, _C, fC) |
|
68 |
|
69 φ'sqr = 1 / (1 / (φ ^ 2 + σ' ^ 2) + 1 / υ) |
|
70 μ' = μ + φ'sqr * part1 |