Crate instant_glicko_2

source ·
Expand description

This crate provides an implementation of the Glicko-2 rating system. Due to the concept of rating periods, Glicko-2 has the problem that rankings cannot easily be updated instantly after a match concludes.

This implementation aims to solve that problem by allowing fractional rating periods, so that ratings can be updated directly after every game, and not just once a rating period closes. This draws inspiration from the rating system implementation for open-source chess website Lichess, as well as two blogpost (1, 2) by Ryan Juckett on skill ratings for INVERSUS Deluxe.

Examples

Example calculation from Glickman’s paper using algorithm:

use instant_glicko_2::{Parameters, PublicRating, IntoWithParameters};
use instant_glicko_2::algorithm::{self, PublicGame};

let parameters = Parameters::default().with_volatility_change(0.5);

// Create our player's rating
let mut player = PublicRating::new(1500.0, 200.0, 0.06);

// Create our opponents
// Their volatility is not specified in the paper and it doesn't matter in the calculation,
// so we're just using the default starting volatility.
let opponent_a = PublicRating::new(1400.0, 30.0, parameters.start_rating().volatility());
let opponent_b = PublicRating::new(1550.0, 100.0, parameters.start_rating().volatility());
let opponent_c = PublicRating::new(1700.0, 300.0, parameters.start_rating().volatility());

// Create match results for our player
let results = [
    // Wins first game (score 1.0)
    PublicGame::new(opponent_a, 1.0).into_with_parameters(parameters),
    // Loses second game (score 0.0)
    PublicGame::new(opponent_b, 0.0).into_with_parameters(parameters),
    // Loses third game (score 0.0)
    PublicGame::new(opponent_c, 0.0).into_with_parameters(parameters),
];

// Update rating after rating period
let new_rating: PublicRating = algorithm::rate_games_untimed(player.into_with_parameters(parameters), &results, 1.0, parameters).into_with_parameters(parameters);

// The rating after the rating period are very close to the results from the paper
assert!((new_rating.rating() - 1464.06).abs() < 0.01);
assert!((new_rating.deviation() - 151.52).abs() < 0.01);
assert!((new_rating.volatility() - 0.05999).abs() < 0.0001);

Different example using RatingEngine:

use std::time::Duration;

use instant_glicko_2::{Parameters, PublicRating};
use instant_glicko_2::engine::{MatchResult, RatingEngine};

let parameters = Parameters::default();

// Create a RatingEngine with a one day rating period duration
// The first rating period starts instantly
let mut engine = RatingEngine::start_new(
    Duration::from_secs(60 * 60 * 24),
    Parameters::default(),
);

// Register two players
// The first player is relatively strong
let player_1_rating_old = PublicRating::new(1700.0, 300.0, 0.06);
let player_1 = engine.register_player(player_1_rating_old).0;
// The second player hasn't played any games
let player_2_rating_old = parameters.start_rating();
let player_2 = engine.register_player(player_2_rating_old).0;

// They play and player_2 wins
engine.register_result(
    player_1,
    player_2,
    &MatchResult::Loss,
);

// Print the new ratings
// Type signatures are needed because we could also work with the internal InternalRating
// That skips one step of calculation,
// but the rating values are not as pretty and not comparable to the original Glicko ratings
let player_1_rating_new: PublicRating = engine.player_rating(player_1).0;
println!("Player 1 old rating: {player_1_rating_old:?}, new rating: {player_1_rating_new:?}");
let player_2_rating_new: PublicRating = engine.player_rating(player_2).0;
println!("Player 2 old rating: {player_2_rating_old:?}, new rating: {player_2_rating_new:?}");

// Loser's rating goes down, winner's rating goes up
assert!(player_1_rating_old.rating() > player_1_rating_new.rating());
assert!(player_2_rating_old.rating() < player_2_rating_new.rating());

The algorithm module provides an implementation of the Glicko-2 algorithm that allows for fractional rating periods.

The engine module provides the RatingEngine struct which allows for adding games and getting the current rating of managed players at any point in time.

Modules

  • This module hosts the methods and types necessary to perform Glicko-2 calculations with fractional rating periods.
  • Various constants defined or recommended in Glickman’s paper, including defaults for model::Parameters.
  • This mod defines the RatingEngine struct which abstracts away the rating period from rating calculations.
  • Utilities that are needed or useful for the rest of the crate, but that don’t really have anything to do with the core of the crate.

Structs

Traits

  • Trait to convert between two types with Parameters. Usually used to convert between the internal rating scaling and the public Glicko rating scaling.
  • Trait to convert between two types with Parameters. Usually used to convert between the internal rating scaling and the public Glicko rating scaling.