### Implement the glicko2 rating system

`The implementation is based on the description from http://www.glicko.net/glicko/glicko2.pdf.`
parent 93a7780c
skat/glicko2.py 0 → 100644
 from math import sqrt, pi, exp, log class Rating: def __init__(self, outer): self.outer = outer self.rating = outer.start_rating self.rd = outer.start_rd self.volatility = outer.start_volatility def __str__(self): return "rating: {}\nrd: {}\nvolatility: {}".format(self.rating, self.rd, self.volatility) class ScaledRating: def __init__(self, rating): self.ratio = 173.7178 self.rating = rating self.outer = rating.outer self.mu = (rating.rating - rating.outer.start_rating) / self.ratio self.phi = rating.rd / self.ratio self.sigma = rating.volatility def unscale(self): r = Rating(self.outer) r.rating = self.ratio * self.mu + self.outer.start_rating r.rd = self.ratio * self.phi r.volatility = self.sigma return r def enshure_rd(self): max_phi = self.outer.start_rd / self.ratio if self.phi > max_phi: self.phi = max_phi class Glicko2: def create_rating(self): return Rating(self) def __init__(self, start_rating, start_rd, start_volatility, tau): self.start_rating = start_rating self.start_rd = start_rd self.start_volatility = start_volatility self.tau = tau @staticmethod def g(phi): return 1/(sqrt(1+(3*(phi**2))/pi**2)) @staticmethod def E(mu, mu_j, phi_j): return 1/(1+exp(-Glicko2.g(phi_j)*(mu-mu_j))) def update_rating(self, rating, opponents, results): # step 2 s_rating = ScaledRating(rating) s_opponents = [ScaledRating(r) for r in opponents] # check rd # rd is not allowed to get higher than the start_rd for r in [s_rating] + s_opponents: r.enshure_rd() # step 6 if not opponents: s_rating.phi = sqrt(s_rating.phi ** 2 + s_rating.sigma ** 2) s_rating.enshure_rd() return s_rating.unscale() # step 3 v = 0 for r in s_opponents: v += (Glicko2.g(r.phi) ** 2) * Glicko2.E(s_rating.mu, r.mu, r.phi) * (1 - Glicko2.E(s_rating.mu, r.mu, r.phi)) v = v ** (-1) # step 4 delta = 0 for (r, s) in zip(s_opponents, results): delta += Glicko2.g(r.phi) * (s - Glicko2.E(s_rating.mu, r.mu, r.phi)) delta *= v # step 5 a = log(s_rating.sigma**2) def f(x): return (exp(x) * (delta ** 2 - s_rating.phi ** 2 - v - exp(x))) / (2 * (s_rating.phi ** 2 + v + exp(x))) - (x - a) / (self.tau**2) epsilon = 0.000001 A = a B = 0.0 if delta ** 2 > (s_rating.phi ** 2 + v): B = log(delta ** 2 - s_rating.phi ** 2 - v) else: k = 1 B = a - k * self.tau while f(B) < 0: k += 1 B = a - k * self.tau f_A = f(A) f_B = f(B) while abs(B - A) > epsilon: C = A + (((A - B) * f_A) / (f_B - f_A)) f_C = f(C) if f_C * f_B < 0: A = B f_A = f_B else: f_A = f_A / 2 B = C f_B = f_C sigma_ = exp(A / 2) # step 6 phi_s = sqrt(s_rating.phi ** 2 + sigma_ ** 2) # step 7 phi_ = 1 / sqrt((1 / (phi_s ** 2)) + (1 / v)) mu_ = 0 for (r, s) in zip(s_opponents, results): mu_ += Glicko2.g(r.phi)*(s - Glicko2.E(s_rating.mu, r.mu, r.phi)) mu_ *= phi_**2 mu_ += s_rating.mu s_rating.sigma = sigma_ s_rating.phi = phi_ s_rating.mu = mu_ # step 8 s_rating.enshure_rd() return s_rating.unscale()