### 2 unstable releases

0.2.0 | Nov 12, 2023 |
---|---|

0.1.0 | Mar 31, 2022 |

#**437** in Math

**MIT**license

96KB

1.5K
SLoC

# Instant Glicko-2

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.

For more on the implementation, I wrote something here.

Documentation for the crate can be found on Docs.rs.

To use this as a dependency, add the following line to your

dependencies:`Cargo.toml`

`instant-glicko-2 = "0.1.0"
`

# 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``(``)``)``;`

#### Dependencies

~170KB