The rating model
Each settled game runs one Glicko-2 update without the volatility step. Both players update from their pre-game values. There is no global re-solve, and a settled result is never revised.
Every player has a rating μ and a deviation φ (how uncertain μ is), both in logits, Glicko-2's internal scale.
Aging
A player's uncertainty grows while they're idle. Before each update both players' variance is aged to the game's start:
var* = min(φ0², φ² + c(r)² · days)
c(r) = 1.15 · 2 · ln 2 / half_life(r)
The half-life is 15 days below 15k, rising linearly to 45 days at 1d and above: KGS's half-lives. Stronger players are assumed to change more slowly.
Update
With the opponent's aged deviation φ_opp:
g = 1 / √(1 + 3·φ_opp² / π²)
E = 1 / (1 + e^(−g·(μ − μ_opp))) expected score
1/φ'² = 1/var* + g² · E · (1 − E)
μ' = μ + φ'² · g · (s − E) s = 1, ½ or 0
A win against a stronger opponent moves μ more than a win against a weaker one, and a player whose rating is uncertain moves more than a settled one.
Limits
- New players start at φ0 = 2.0.
- φ never drops below 0.01.
- Stored μ is clamped to OGS's range, ratings 100 to 3500 (μ from about −8.06 to 11.51), well beyond the ranks shown (30k to 9d). A player losing on purpose below 30k keeps losing μ, so the clamp adds no inflation.
One rating for every board
9×9, 13×13 and 19×19 share one rating, and every game counts at full weight. Rated games are even games at a fixed komi per board size. Down-weighting small boards made predictions on them worse (evidence).
Short games
A game shorter than 20 steps is void, as OGS voids aborted games, unless it ended onchain by resignation or timeout. Then only the loser's rating changes, since a stale anchor may hide the game's real length.
Peak
A player's peak is the best μ − 2φ they've reached. It moves only in
queue games between two settled players: each with 10 or more games and
aged φ at most 1.0. Every rated game updates both ratings, but only these
games can raise a peak, which bounds what a repeatable trick is worth.
A player becomes established the first time φ ≤ 1.0 with at least one win and one loss, and stays established.
Fixed point
The update runs onchain in Q32.32 fixed point, with i128 intermediates
(the largest is 98 bits):
- divisions round half to even;
expuses ln 2 range reduction, a 12-entry table and Horner's rule;- square roots use
u128sqrt.
One update is roughly 63 multiplications, 66 divisions, 5 square roots and 3
exponentials. The SDK's rating.mjs is the reference implementation, and
generated test vectors make Cairo match it bit for bit.
Over all 30.4M OGS games of the backtest, the integer update matched the floating-point model within 1.6×10⁻⁸ in win probability. Truncating division instead drifted about 100 times more than rounding half to even.
Storage
Each player is one packed felt: μ, φ, last-played time, starting band, flags and the constants' version, game counters and peak. A rating writes three storage slots: one per player and the ticket's status.
last_played only moves forward (max(last_played, played_at)), so rating
games out of order can't move a player's clock backwards.