Packages

Pure Elixir implementation of the Glicko-2 rating system for two-player games

Current section

Files

Jump to
glicko_rating_system lib glicko_rating_system.ex
Raw

lib/glicko_rating_system.ex

defmodule GlickoRatingSystem do
@moduledoc """
A pure Elixir implementation of the Glicko-2 rating system for two-player games.
Ratings are represented as `{rating, deviation, volatility}` tuples.
Scores are floats in `[0.0, 1.0]`, enabling graded outcomes such as
checkmate (`1.0`) vs. stalemate (`0.75`).
"""
alias GlickoRatingSystem.{Engine, Validation}
@default_rating 1500.0
@default_deviation 350.0
@default_volatility 0.06
@default_tau 0.5
@type player :: {float(), float(), float()}
@type game_result :: {player(), float()}
@doc """
Computes updated rating parameters for a player after a rating period.
## Parameters
- `player` — a `{rating, deviation, volatility}` tuple.
- `game_results` — a list of `{opponent, score}` tuples.
- `:tau` — system constant constraining volatility change (default: `#{@default_tau}`).
## Examples
iex> player = {1500.0, 200.0, 0.06}
iex> opponent = {1400.0, 30.0, 0.06}
iex> {r, _rd, _vol} = GlickoRatingSystem.rate(player, [{opponent, 1.0}])
iex> r > 1500.0
true
"""
@spec rate(player(), [game_result()], keyword()) :: player()
def rate(player, game_results, opts \\ []) do
Validation.validate_player!(player)
Validation.validate_game_results!(game_results)
tau = Keyword.get(opts, :tau, @default_tau)
Engine.rate(player, game_results, tau)
end
@doc """
Returns the expected score of `player` against `opponent`.
The result is a float in `(0.0, 1.0)` representing the probability
that `player` wins, accounting for both players' rating deviations.
## Examples
iex> player = {1500.0, 200.0, 0.06}
iex> opponent = {1400.0, 30.0, 0.06}
iex> score = GlickoRatingSystem.expected_score(player, opponent)
iex> score > 0.5
true
"""
@spec expected_score(player(), player()) :: float()
def expected_score(player, opponent) do
Validation.validate_player!(player)
Validation.validate_player!(opponent)
Engine.expected_score(player, opponent)
end
@doc "Default initial rating: `#{@default_rating}`."
@spec default_rating() :: float()
def default_rating, do: @default_rating
@doc "Default initial deviation: `#{@default_deviation}`."
@spec default_deviation() :: float()
def default_deviation, do: @default_deviation
@doc "Default initial volatility: `#{@default_volatility}`."
@spec default_volatility() :: float()
def default_volatility, do: @default_volatility
end