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.
Example calculation from Glickman's paper using algorithm
:
```rs use instantglicko2::{Parameters, Rating}; use instantglicko2::algorithm::{self, PlayerResult};
let parameters = Parameters::default().withvolatilitychange(0.5);
// Create our player's rating let player = Rating::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 opponenta = Rating::new(1400.0, 30.0, parameters.startrating().volatility()); let opponentb = Rating::new(1550.0, 100.0, parameters.startrating().volatility()); let opponentc = Rating::new(1700.0, 300.0, parameters.startrating().volatility());
// Create match results for our player let results = [ // Wins first game (score 1.0) PlayerResult::new(opponenta, 1.0), // Loses second game (score 0.0) PlayerResult::new(opponentb, 0.0), // Loses third game (score 0.0) PlayerResult::new(opponent_c, 0.0), ];
// Calculate new rating after 1.0 rating periods let newrating = algorithm::rateplayer(player, &results, 1.0, parameters);
// The results are close to the results from the paper. assert!((newrating.rating() - 1464.06).abs() < 0.01); assert!((newrating.deviation() - 151.52).abs() < 0.01); assert!((new_rating.volatility() - 0.05999).abs() < 0.0001); ```
Different example using RatingEngine
:
```rs use std::time::Duration;
use instantglicko2::{Parameters, Rating}; use instantglicko2::engine::{MatchResult, RatingEngine, RatingResult};
let parameters = Parameters::default();
// Create a RatingEngine with a one day rating period duration // The first rating period starts instantly let mut engine = RatingEngine::startnew( Duration::fromsecs(60 * 60 * 24), Parameters::default(), );
// Register two players // The first player is relatively strong let player1ratingold = Rating::new(1700.0, 300.0, 0.06); let player1 = engine.registerplayer(player1ratingold); // The second player hasn't played any games let player2ratingold = parameters.startrating(); let player2 = engine.registerplayer(player2rating_old);
// They play and player2 wins engine.registerresult(&RatingResult::new( player1, player2, MatchResult::Loss, ));
// Print the new ratings // Type signatures are needed because we could also work with the internal ScaledRating // That skips one step of calculation, // but the rating values are not as pretty and not comparable to the original Glicko ratings let player1ratingnew: Rating = engine.playerrating(player1); println!("Player 1 old rating: {player1ratingold:?}, new rating: {player1ratingnew:?}"); let player2ratingnew: Rating = engine.playerrating(player2); println!("Player 2 old rating: {player2ratingold:?}, new rating: {player2ratingnew:?}");
// Loser's rating goes down, winner's rating goes up assert!(player1ratingold.rating() > player1ratingnew.rating()); assert!(player2ratingold.rating() < player2ratingnew.rating()); ```