From cf61095b2dd989dcbe08897e5dbbad39d88104b5 Mon Sep 17 00:00:00 2001 From: atomflunder <80397293+atomflunder@users.noreply.github.com> Date: Sun, 25 Sep 2022 16:25:53 +0200 Subject: [PATCH] Pass reference to all functions --- CHANGELOG.md | 4 + Cargo.toml | 6 +- README.md | 2 +- src/dwz.rs | 34 ++++----- src/elo.rs | 50 ++++++------ src/glicko.rs | 46 +++++------ src/glicko2.rs | 87 ++++++++++++--------- src/ingo.rs | 31 ++++---- src/lib.rs | 2 +- src/rating.rs | 2 +- src/trueskill.rs | 195 +++++++++++++++++++++++++---------------------- src/weng_lin.rs | 102 ++++++++++++------------- 12 files changed, 295 insertions(+), 266 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c185c78..9c9f4d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ This is a broad overview of the changes that have been made over the lifespan of this library. +## v0.13.0 - 2022-09-25 + +- All functions now use references instead of taking ownership unnecessarily + ## v0.12.0 - 2022-08-26 - Add Weng-Lin (A Bayesian Approximation Method for Online Ranking) calculations diff --git a/Cargo.toml b/Cargo.toml index b5dc815..7e89e41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "skillratings" -version = "0.12.0" +version = "0.13.0" edition = "2021" description = "Calculate a player's skill rating using algorithms like Elo, Glicko, Glicko-2, TrueSkill and many more." -readme= "README.md" +readme = "README.md" repository = "https://github.com/atomflunder/skillratings" license = "MIT" keywords = ["elo", "glicko", "glicko-2", "trueskill", "rating"] @@ -11,4 +11,4 @@ categories = ["game-development", "algorithms", "mathematics"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] \ No newline at end of file +[dependencies] diff --git a/README.md b/README.md index ed78dee..f28ba4d 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Add the following to your `Cargo.toml` file: ```toml [dependencies] -skillratings = "0.12.0" +skillratings = "0.13.0" ``` ## Basic Usage diff --git a/src/dwz.rs b/src/dwz.rs index e5fff62..0f54d24 100644 --- a/src/dwz.rs +++ b/src/dwz.rs @@ -50,7 +50,7 @@ use crate::{outcomes::Outcomes, rating::DWZRating}; /// /// let outcome = Outcomes::WIN; /// -/// let (player_one_new, player_two_new) = dwz(player_one, player_two, outcome); +/// let (player_one_new, player_two_new) = dwz(&player_one, &player_two, &outcome); /// /// assert!((player_one_new.rating.round() - 1519.0).abs() < f64::EPSILON); /// assert_eq!(player_one_new.index, 43); @@ -59,9 +59,9 @@ use crate::{outcomes::Outcomes, rating::DWZRating}; /// assert_eq!(player_two_new.index, 13); /// ``` pub fn dwz( - player_one: DWZRating, - player_two: DWZRating, - outcome: Outcomes, + player_one: &DWZRating, + player_two: &DWZRating, + outcome: &Outcomes, ) -> (DWZRating, DWZRating) { let outcome1 = match outcome { Outcomes::WIN => 1.0, @@ -148,12 +148,12 @@ pub fn dwz( /// /// let results = vec![(opponent1, Outcomes::WIN), (opponent2, Outcomes::DRAW)]; /// -/// let new_player = dwz_rating_period(player, &results); +/// let new_player = dwz_rating_period(&player, &results); /// /// assert!((new_player.rating.round() - 1635.0).abs() < f64::EPSILON); /// assert_eq!(new_player.index, 18); /// ``` -pub fn dwz_rating_period(player: DWZRating, results: &Vec<(DWZRating, Outcomes)>) -> DWZRating { +pub fn dwz_rating_period(player: &DWZRating, results: &Vec<(DWZRating, Outcomes)>) -> DWZRating { let points = results .iter() .map(|r| match r.1 { @@ -165,7 +165,7 @@ pub fn dwz_rating_period(player: DWZRating, results: &Vec<(DWZRating, Outcomes)> let expected_points = results .iter() - .map(|r| expected_score(player, r.0).0) + .map(|r| expected_score(player, &r.0).0) .sum::(); #[allow(clippy::as_conversions, clippy::cast_precision_loss)] @@ -208,13 +208,13 @@ pub fn dwz_rating_period(player: DWZRating, results: &Vec<(DWZRating, Outcomes)> /// age: 12, /// }; /// -/// let (exp_one, exp_two) = expected_score(player_one, player_two); +/// let (exp_one, exp_two) = expected_score(&player_one, &player_two); /// /// /// assert!(((exp_one * 100.0).round() - 91.0).abs() < f64::EPSILON); /// assert!(((exp_two * 100.0).round() - 9.0).abs() < f64::EPSILON); /// ``` -pub fn expected_score(player_one: DWZRating, player_two: DWZRating) -> (f64, f64) { +pub fn expected_score(player_one: &DWZRating, player_two: &DWZRating) -> (f64, f64) { ( (1.0 + 10.0_f64.powf(-1.0 * (1.0 / 400.0) * (player_one.rating - player_two.rating))) .recip(), @@ -460,7 +460,7 @@ mod tests { age: 39, }; - (player_one, player_two) = dwz(player_one, player_two, Outcomes::WIN); + (player_one, player_two) = dwz(&player_one, &player_two, &Outcomes::WIN); assert!((player_one.rating.round() - 1564.0).abs() < f64::EPSILON); assert_eq!(player_one.index, 23); @@ -468,7 +468,7 @@ mod tests { assert!((player_two.rating.round() - 1906.0).abs() < f64::EPSILON); assert_eq!(player_two.index, 104); - (player_one, player_two) = dwz(player_one, player_two, Outcomes::DRAW); + (player_one, player_two) = dwz(&player_one, &player_two, &Outcomes::DRAW); assert!((player_one.rating.round() - 1578.0).abs() < f64::EPSILON); assert_eq!(player_one.index, 24); @@ -478,7 +478,7 @@ mod tests { player_two.age = 12; - (player_one, player_two) = dwz(player_one, player_two, Outcomes::LOSS); + (player_one, player_two) = dwz(&player_one, &player_two, &Outcomes::LOSS); assert!((player_one.rating.round() - 1573.0).abs() < f64::EPSILON); assert_eq!(player_one.index, 25); @@ -513,7 +513,7 @@ mod tests { (opponent1, Outcomes::LOSS), ]; - let new_player = dwz_rating_period(player, &results); + let new_player = dwz_rating_period(&player, &results); assert!((new_player.rating.round() - 1619.0).abs() < f64::EPSILON); assert_eq!(new_player.index, 18); @@ -534,7 +534,7 @@ mod tests { }; (really_good_player, really_bad_player) = - dwz(really_good_player, really_bad_player, Outcomes::WIN); + dwz(&really_good_player, &really_bad_player, &Outcomes::WIN); assert!((really_good_player.rating.round() - 3210.0).abs() < f64::EPSILON); assert_eq!(really_good_player.index, 144); @@ -546,7 +546,7 @@ mod tests { really_good_player.rating = 32_477_324_874_238.0; (really_good_player, really_bad_player) = - dwz(really_good_player, really_bad_player, Outcomes::WIN); + dwz(&really_good_player, &really_bad_player, &Outcomes::WIN); assert!((really_good_player.rating.round() - 32_477_324_874_238.0).abs() < f64::EPSILON); @@ -559,7 +559,7 @@ mod tests { really_bad_player.age = 5; (really_good_player, really_bad_player) = - dwz(really_good_player, really_bad_player, Outcomes::LOSS); + dwz(&really_good_player, &really_bad_player, &Outcomes::LOSS); assert!((really_good_player.rating.round() + 1.0).abs() < f64::EPSILON); assert!((really_bad_player.rating.round() - 68.0).abs() < f64::EPSILON); @@ -579,7 +579,7 @@ mod tests { age: 39, }; - let (exp1, exp2) = expected_score(player_one, player_two); + let (exp1, exp2) = expected_score(&player_one, &player_two); assert!(((exp1 * 100.0).round() - 9.0).abs() < f64::EPSILON); assert!(((exp2 * 100.0).round() - 91.0).abs() < f64::EPSILON); diff --git a/src/elo.rs b/src/elo.rs index 950642d..c992ac5 100644 --- a/src/elo.rs +++ b/src/elo.rs @@ -31,16 +31,16 @@ use crate::{config::EloConfig, outcomes::Outcomes, rating::EloRating}; /// /// let config = EloConfig::new(); /// -/// let (player_one_new, player_two_new) = elo(player_one, player_two, outcome, &config); +/// let (player_one_new, player_two_new) = elo(&player_one, &player_two, &outcome, &config); /// /// assert!((player_one_new.rating - 1016.0).abs() < f64::EPSILON); /// assert!((player_two_new.rating - 984.0).abs() < f64::EPSILON); /// ``` #[must_use] pub fn elo( - player_one: EloRating, - player_two: EloRating, - outcome: Outcomes, + player_one: &EloRating, + player_two: &EloRating, + outcome: &Outcomes, config: &EloConfig, ) -> (EloRating, EloRating) { let (one_expected, two_expected) = expected_score(player_one, player_two); @@ -86,7 +86,7 @@ pub fn elo( /// let opponent3 = EloRating::new(); /// /// let new_player = elo_rating_period( -/// player, +/// &player, /// &vec![ /// (opponent1, Outcomes::WIN), /// (opponent2, Outcomes::WIN), @@ -99,14 +99,14 @@ pub fn elo( /// ``` #[must_use] pub fn elo_rating_period( - player: EloRating, + player: &EloRating, results: &Vec<(EloRating, Outcomes)>, config: &EloConfig, ) -> EloRating { - let mut player = player; + let mut player = *player; for (opponent, result) in results { - let (exp, _) = expected_score(player, *opponent); + let (exp, _) = expected_score(&player, opponent); let o = match result { Outcomes::WIN => 1.0, @@ -134,13 +134,13 @@ pub fn elo_rating_period( /// let player_one = EloRating { rating: 1320.0 }; /// let player_two = EloRating { rating: 1217.0 }; /// -/// let (winner_exp, loser_exp) = expected_score(player_one, player_two); +/// let (winner_exp, loser_exp) = expected_score(&player_one, &player_two); /// /// assert!(((winner_exp * 100.0).round() - 64.0).abs() < f64::EPSILON); /// assert!(((loser_exp * 100.0).round() - 36.0).abs() < f64::EPSILON); /// ``` #[must_use] -pub fn expected_score(player_one: EloRating, player_two: EloRating) -> (f64, f64) { +pub fn expected_score(player_one: &EloRating, player_two: &EloRating) -> (f64, f64) { ( 1.0 / (1.0 + 10_f64.powf((player_two.rating - player_one.rating) / 400.0)), 1.0 / (1.0 + 10_f64.powf((player_one.rating - player_two.rating) / 400.0)), @@ -154,36 +154,36 @@ mod tests { #[test] fn test_elo() { let (winner_new_elo, loser_new_elo) = elo( - EloRating { rating: 1000.0 }, - EloRating { rating: 1000.0 }, - Outcomes::WIN, + &EloRating { rating: 1000.0 }, + &EloRating { rating: 1000.0 }, + &Outcomes::WIN, &EloConfig::new(), ); assert!((winner_new_elo.rating - 1016.0).abs() < f64::EPSILON); assert!((loser_new_elo.rating - 984.0).abs() < f64::EPSILON); let (winner_new_elo, loser_new_elo) = elo( - EloRating { rating: 1000.0 }, - EloRating { rating: 1000.0 }, - Outcomes::LOSS, + &EloRating { rating: 1000.0 }, + &EloRating { rating: 1000.0 }, + &Outcomes::LOSS, &EloConfig::new(), ); assert!((winner_new_elo.rating - 984.0).abs() < f64::EPSILON); assert!((loser_new_elo.rating - 1016.0).abs() < f64::EPSILON); let (winner_new_elo, loser_new_elo) = elo( - EloRating { rating: 1000.0 }, - EloRating { rating: 1000.0 }, - Outcomes::DRAW, + &EloRating { rating: 1000.0 }, + &EloRating { rating: 1000.0 }, + &Outcomes::DRAW, &EloConfig::new(), ); assert!((winner_new_elo.rating - 1000.0).abs() < f64::EPSILON); assert!((loser_new_elo.rating - 1000.0).abs() < f64::EPSILON); let (winner_new_elo, loser_new_elo) = elo( - EloRating { rating: 500.0 }, - EloRating { rating: 1500.0 }, - Outcomes::WIN, + &EloRating { rating: 500.0 }, + &EloRating { rating: 1500.0 }, + &Outcomes::WIN, &EloConfig::default(), ); assert!((winner_new_elo.rating.round() - 532.0).abs() < f64::EPSILON); @@ -199,7 +199,7 @@ mod tests { let opponent3 = EloRating::new(); let new_player = elo_rating_period( - player, + &player, &vec![ (opponent1, Outcomes::WIN), (opponent2, Outcomes::DRAW), @@ -216,7 +216,7 @@ mod tests { let player_one = EloRating::new(); let player_two = EloRating::default(); - let (winner_expected, loser_expected) = expected_score(player_one, player_two); + let (winner_expected, loser_expected) = expected_score(&player_one, &player_two); assert!((winner_expected - 0.5).abs() < f64::EPSILON); assert!((loser_expected - 0.5).abs() < f64::EPSILON); @@ -224,7 +224,7 @@ mod tests { let player_one = EloRating { rating: 2251.0 }; let player_two = EloRating { rating: 1934.0 }; - let (winner_expected, loser_expected) = expected_score(player_one, player_two); + let (winner_expected, loser_expected) = expected_score(&player_one, &player_two); assert!(((winner_expected * 100.0).round() - 86.0).abs() < f64::EPSILON); assert!(((loser_expected * 100.0).round() - 14.0).abs() < f64::EPSILON); diff --git a/src/glicko.rs b/src/glicko.rs index ee1c934..5bb5a3d 100644 --- a/src/glicko.rs +++ b/src/glicko.rs @@ -48,7 +48,7 @@ use std::f64::consts::PI; /// /// let outcome = Outcomes::WIN; /// -/// let (player_one_new, player_two_new) = glicko(player_one, player_two, outcome); +/// let (player_one_new, player_two_new) = glicko(&player_one, &player_two, &outcome); /// /// assert!((player_one_new.rating.round() - 1662.0).abs() < f64::EPSILON); /// assert!((player_one_new.deviation.round() - 290.0).abs() < f64::EPSILON); @@ -57,9 +57,9 @@ use std::f64::consts::PI; /// assert!((player_two_new.deviation.round() - 290.0).abs() < f64::EPSILON); /// ``` pub fn glicko( - player_one: GlickoRating, - player_two: GlickoRating, - outcome: Outcomes, + player_one: &GlickoRating, + player_two: &GlickoRating, + outcome: &Outcomes, ) -> (GlickoRating, GlickoRating) { let q = 10_f64.ln() / 400.0; @@ -155,14 +155,14 @@ pub fn glicko( /// /// let config = GlickoConfig::new(); /// -/// let new_player = glicko_rating_period(player, &results, &config); +/// let new_player = glicko_rating_period(&player, &results, &config); /// /// assert!((new_player.rating.round() - 1464.0).abs() < f64::EPSILON); /// assert!((new_player.deviation - 151.253_743_431_783_2).abs() < f64::EPSILON); /// ``` #[must_use] pub fn glicko_rating_period( - player: GlickoRating, + player: &GlickoRating, results: &Vec<(GlickoRating, Outcomes)>, config: &GlickoConfig, ) -> GlickoRating { @@ -221,11 +221,11 @@ pub fn glicko_rating_period( /// rating: 1950.0, /// deviation: 320.0, /// }; -/// let (exp_one, exp_two) = expected_score(player_one, player_two); +/// let (exp_one, exp_two) = expected_score(&player_one, &player_two); /// assert!(((exp_one * 100.0).round() - 90.0).abs() < f64::EPSILON); /// assert!(((exp_two * 100.0).round() - 10.0).abs() < f64::EPSILON); /// ``` -pub fn expected_score(player_one: GlickoRating, player_two: GlickoRating) -> (f64, f64) { +pub fn expected_score(player_one: &GlickoRating, player_two: &GlickoRating) -> (f64, f64) { let q = 10_f64.ln() / 400.0; let g = g_value(q, player_one.deviation.hypot(player_two.deviation)); @@ -253,11 +253,11 @@ pub fn expected_score(player_one: GlickoRating, player_two: GlickoRating) -> (f6 /// /// let config = GlickoConfig::new(); /// -/// let player_one_decay = decay_deviation(player_one, &config); +/// let player_one_decay = decay_deviation(&player_one, &config); /// /// assert!((player_one_decay.deviation.round() - 75.0).abs() < f64::EPSILON); /// ``` -pub fn decay_deviation(player: GlickoRating, config: &GlickoConfig) -> GlickoRating { +pub fn decay_deviation(player: &GlickoRating, config: &GlickoConfig) -> GlickoRating { let new_player_deviation = player.deviation.hypot(config.c).min(350.0); GlickoRating { @@ -282,12 +282,12 @@ pub fn decay_deviation(player: GlickoRating, config: &GlickoConfig) -> GlickoRat /// deviation: 79.0, /// }; /// -/// let (interval_low, interval_high) = confidence_interval(player); +/// let (interval_low, interval_high) = confidence_interval(&player); /// /// assert!(interval_low.round() - 2095.0 < f64::EPSILON); /// assert!(interval_high.round() - 2405.0 < f64::EPSILON); /// ``` -pub fn confidence_interval(player: GlickoRating) -> (f64, f64) { +pub fn confidence_interval(player: &GlickoRating) -> (f64, f64) { ( // Seems like there is no mul_sub function. player.rating - 1.96 * player.deviation, @@ -345,11 +345,11 @@ mod tests { deviation: 300.0, }; - let (player1, _) = glicko(player1, opponent1, Outcomes::WIN); + let (player1, _) = glicko(&player1, &opponent1, &Outcomes::WIN); - let (player1, _) = glicko(player1, opponent2, Outcomes::LOSS); + let (player1, _) = glicko(&player1, &opponent2, &Outcomes::LOSS); - let (player1, _) = glicko(player1, opponent3, Outcomes::LOSS); + let (player1, _) = glicko(&player1, &opponent3, &Outcomes::LOSS); assert!((player1.rating.round() - 1464.0).abs() < f64::EPSILON); assert!((player1.deviation - 151.253_743_431_783_2).abs() < f64::EPSILON); @@ -383,7 +383,7 @@ mod tests { (opponent3, Outcomes::LOSS), ]; - let new_player = glicko_rating_period(player, &results, &GlickoConfig::new()); + let new_player = glicko_rating_period(&player, &results, &GlickoConfig::new()); assert!((new_player.rating.round() - 1527.0).abs() < f64::EPSILON); assert!((new_player.deviation - 150.607_779_021_875_13).abs() < f64::EPSILON); @@ -395,7 +395,7 @@ mod tests { let results: Vec<(GlickoRating, Outcomes)> = Vec::new(); - let new_player = glicko_rating_period(player, &results, &GlickoConfig::new()); + let new_player = glicko_rating_period(&player, &results, &GlickoConfig::new()); assert!((new_player.deviation - 80.586_847_562_117_73).abs() < f64::EPSILON); } @@ -414,7 +414,7 @@ mod tests { deviation: 150.0, }; - let (exp_one, exp_two) = expected_score(player_one, player_two); + let (exp_one, exp_two) = expected_score(&player_one, &player_two); assert!((exp_one - 0.373_700_405_951_935).abs() < f64::EPSILON); assert!((exp_two - 0.626_299_594_048_065).abs() < f64::EPSILON); @@ -430,7 +430,7 @@ mod tests { deviation: 30.0, }; - let ci = confidence_interval(player); + let ci = confidence_interval(&player); assert!((ci.0.round() - 1441.0).abs() < f64::EPSILON); assert!((ci.1.round() - 1559.0).abs() < f64::EPSILON); @@ -445,17 +445,17 @@ mod tests { deviation: 50.0, }; - let mut player = decay_deviation(player, &GlickoConfig::new()); + let mut player = decay_deviation(&player, &GlickoConfig::new()); assert!((player.deviation - 80.586_847_562_117_73).abs() < f64::EPSILON); for _ in 0..29 { - player = decay_deviation(player, &GlickoConfig::default()); + player = decay_deviation(&player, &GlickoConfig::default()); } assert!(((player.deviation * 1000.0).round() - 349_753.0).abs() < f64::EPSILON); - player = decay_deviation(player, &GlickoConfig::new()); + player = decay_deviation(&player, &GlickoConfig::new()); assert!((player.deviation - 350.0).abs() < f64::EPSILON); } @@ -469,7 +469,7 @@ mod tests { deviation: 41.0, }; - (player, opponent) = glicko(player, opponent, Outcomes::DRAW); + (player, opponent) = glicko(&player, &opponent, &Outcomes::DRAW); assert!((player.rating.round() - 1820.0).abs() < f64::EPSILON); assert!((player.deviation.round() - 340.0).abs() < f64::EPSILON); diff --git a/src/glicko2.rs b/src/glicko2.rs index 9188678..be3e15d 100644 --- a/src/glicko2.rs +++ b/src/glicko2.rs @@ -49,7 +49,7 @@ use std::f64::consts::PI; /// /// let config = Glicko2Config::new(); /// -/// let (player_one_new, player_two_new) = glicko2(player_one, player_two, outcome, &config); +/// let (player_one_new, player_two_new) = glicko2(&player_one, &player_two, &outcome, &config); /// /// assert!((player_one_new.rating.round() - 1662.0).abs() < f64::EPSILON); /// assert!((player_one_new.deviation.round() - 290.0).abs() < f64::EPSILON); @@ -61,9 +61,9 @@ use std::f64::consts::PI; /// ``` #[must_use] pub fn glicko2( - player_one: Glicko2Rating, - player_two: Glicko2Rating, - outcome: Outcomes, + player_one: &Glicko2Rating, + player_two: &Glicko2Rating, + outcome: &Outcomes, config: &Glicko2Config, ) -> (Glicko2Rating, Glicko2Rating) { // First we need to convert the ratings into the glicko-2 scale. @@ -179,7 +179,7 @@ pub fn glicko2( /// (opponent_three, Outcomes::LOSS), /// ]; /// -/// let new_player = glicko2_rating_period(player, &results, &Glicko2Config::new()); +/// let new_player = glicko2_rating_period(&player, &results, &Glicko2Config::new()); /// /// assert!((new_player.rating.round() - 1464.0).abs() < f64::EPSILON); /// assert!((new_player.deviation.round() - 152.0).abs() < f64::EPSILON); @@ -187,7 +187,7 @@ pub fn glicko2( /// ``` #[must_use] pub fn glicko2_rating_period( - player: Glicko2Rating, + player: &Glicko2Rating, results: &Vec<(Glicko2Rating, Outcomes)>, config: &Glicko2Config, ) -> Glicko2Rating { @@ -260,12 +260,12 @@ pub fn glicko2_rating_period( /// deviation: 320.0, /// volatility: 0.06, /// }; -/// let (exp_one, exp_two) = expected_score(player_one, player_two); +/// let (exp_one, exp_two) = expected_score(&player_one, &player_two); /// assert!(((exp_one * 100.0).round() - 90.0).abs() < f64::EPSILON); /// assert!(((exp_two * 100.0).round() - 10.0).abs() < f64::EPSILON); /// ``` #[must_use] -pub fn expected_score(player_one: Glicko2Rating, player_two: Glicko2Rating) -> (f64, f64) { +pub fn expected_score(player_one: &Glicko2Rating, player_two: &Glicko2Rating) -> (f64, f64) { // First we need to convert the ratings into the glicko-2 scale. let player_one_rating = (player_one.rating - 1500.0) / 173.7178; let player_two_rating = (player_two.rating - 1500.0) / 173.7178; @@ -309,12 +309,12 @@ pub fn expected_score(player_one: Glicko2Rating, player_two: Glicko2Rating) -> ( /// volatility: 0.06, /// }; /// -/// let player_one_decay = decay_deviation(player_one); +/// let player_one_decay = decay_deviation(&player_one); /// /// assert!((player_one_decay.deviation.round() - 43.0).abs() < f64::EPSILON); /// ``` #[must_use] -pub fn decay_deviation(player: Glicko2Rating) -> Glicko2Rating { +pub fn decay_deviation(player: &Glicko2Rating) -> Glicko2Rating { let player_deviation = player.deviation / 173.7178; let new_player_deviation = player_deviation.hypot(player.volatility); @@ -342,12 +342,12 @@ pub fn decay_deviation(player: Glicko2Rating) -> Glicko2Rating { /// volatility: 0.0598, /// }; /// -/// let (interval_low, interval_high) = confidence_interval(player); +/// let (interval_low, interval_high) = confidence_interval(&player); /// /// assert!(interval_low.round() - 2095.0 < f64::EPSILON); /// assert!(interval_high.round() - 2405.0 < f64::EPSILON); /// ``` -pub fn confidence_interval(player: Glicko2Rating) -> (f64, f64) { +pub fn confidence_interval(player: &Glicko2Rating) -> (f64, f64) { ( // Seems like there is no mul_sub function. player.rating - 1.96 * player.deviation, @@ -485,7 +485,7 @@ mod tests { }; let (player1new, player2new) = - glicko2(player1, player2, Outcomes::WIN, &Glicko2Config::new()); + glicko2(&player1, &player2, &Outcomes::WIN, &Glicko2Config::new()); assert!((player1new.rating.round() - 1653.0).abs() < f64::EPSILON); assert!((player1new.deviation.round() - 292.0).abs() < f64::EPSILON); @@ -508,8 +508,12 @@ mod tests { volatility: 0.06, }; - let (player1new, player2new) = - glicko2(player1, player2, Outcomes::DRAW, &Glicko2Config::default()); + let (player1new, player2new) = glicko2( + &player1, + &player2, + &Outcomes::DRAW, + &Glicko2Config::default(), + ); assert!((player1new.rating.round() - 1550.0).abs() < f64::EPSILON); assert!((player1new.deviation.round() - 253.0).abs() < f64::EPSILON); @@ -534,8 +538,12 @@ mod tests { volatility: 0.06, }; - let (player, opponent_one) = - glicko2(player, opponent_one, Outcomes::WIN, &Glicko2Config::new()); + let (player, opponent_one) = glicko2( + &player, + &opponent_one, + &Outcomes::WIN, + &Glicko2Config::new(), + ); assert!((player.rating.round() - 1564.0).abs() < f64::EPSILON); assert!((player.deviation.round() - 175.0).abs() < f64::EPSILON); @@ -549,7 +557,12 @@ mod tests { volatility: 0.06, }; - let (player, _) = glicko2(player, opponent_two, Outcomes::LOSS, &Glicko2Config::new()); + let (player, _) = glicko2( + &player, + &opponent_two, + &Outcomes::LOSS, + &Glicko2Config::new(), + ); let opponent_three = Glicko2Rating { rating: 1700.0, @@ -558,9 +571,9 @@ mod tests { }; let (player, _) = glicko2( - player, - opponent_three, - Outcomes::LOSS, + &player, + &opponent_three, + &Outcomes::LOSS, &Glicko2Config::new(), ); @@ -601,7 +614,7 @@ mod tests { (opponent_three, Outcomes::LOSS), ]; - let new_player = glicko2_rating_period(player, &results, &Glicko2Config::new()); + let new_player = glicko2_rating_period(&player, &results, &Glicko2Config::new()); assert!((new_player.rating.round() - 1527.0).abs() < f64::EPSILON); assert!((new_player.deviation.round() - 151.0).abs() < f64::EPSILON); @@ -615,7 +628,7 @@ mod tests { let results: Vec<(Glicko2Rating, Outcomes)> = Vec::new(); - let new_player = glicko2_rating_period(player, &results, &Glicko2Config::new()); + let new_player = glicko2_rating_period(&player, &results, &Glicko2Config::new()); assert!((new_player.deviation.round() - 96.0).abs() < f64::EPSILON); } @@ -634,7 +647,7 @@ mod tests { volatility: 0.06, }; - let (exp_one, exp_two) = expected_score(player_one, player_two); + let (exp_one, exp_two) = expected_score(&player_one, &player_two); assert!((exp_one * 100.0 - 50.0).abs() < f64::EPSILON); assert!((exp_two * 100.0 - 50.0).abs() < f64::EPSILON); @@ -651,7 +664,7 @@ mod tests { volatility: 0.06, }; - let (exp_three, exp_four) = expected_score(player_three, player_four); + let (exp_three, exp_four) = expected_score(&player_three, &player_four); assert!(((exp_three * 100.0).round() - 76.0).abs() < f64::EPSILON); assert!(((exp_four * 100.0).round() - 24.0).abs() < f64::EPSILON); @@ -677,13 +690,13 @@ mod tests { volatility: 0.059_998, }; - let player_one_decayed = decay_deviation(player_one); - let player_one_decayed_2 = decay_deviation(player_one_decayed); + let player_one_decayed = decay_deviation(&player_one); + let player_one_decayed_2 = decay_deviation(&player_one_decayed); - let player_two_decayed = decay_deviation(player_two); + let player_two_decayed = decay_deviation(&player_two); - let player_three_decayed = decay_deviation(player_three); - let player_three_decayed_2 = decay_deviation(player_three_decayed); + let player_three_decayed = decay_deviation(&player_three); + let player_three_decayed_2 = decay_deviation(&player_three_decayed); assert!((player_one_decayed.deviation.round() - 350.0).abs() < f64::EPSILON); assert!((player_one_decayed_2.deviation.round() - 350.0).abs() < f64::EPSILON); @@ -700,7 +713,7 @@ mod tests { volatility: 0.06, }; - let ci = confidence_interval(player); + let ci = confidence_interval(&player); assert!((ci.0.round() - 1441.0).abs() < f64::EPSILON); assert!((ci.1.round() - 1559.0).abs() < f64::EPSILON); @@ -725,7 +738,7 @@ mod tests { convergence_tolerance: 0.000_001, }; - (player, opponent) = glicko2(player, opponent, Outcomes::WIN, &config); + (player, opponent) = glicko2(&player, &opponent, &Outcomes::WIN, &config); assert!((player.rating.round() - 2596.0).abs() < f64::EPSILON); assert!((opponent.rating.round() - 2249.0).abs() < f64::EPSILON); @@ -738,10 +751,11 @@ mod tests { let mut opponent = Glicko2Rating::default(); for _ in 0..6 { - (player, opponent) = glicko2(player, opponent, Outcomes::LOSS, &Glicko2Config::new()); + (player, opponent) = + glicko2(&player, &opponent, &Outcomes::LOSS, &Glicko2Config::new()); } - (player, opponent) = glicko2(player, opponent, Outcomes::WIN, &Glicko2Config::new()); + (player, opponent) = glicko2(&player, &opponent, &Outcomes::WIN, &Glicko2Config::new()); assert!((player.rating.round() - 1397.0).abs() < f64::EPSILON); assert!((player.deviation.round() - 212.0).abs() < f64::EPSILON); @@ -756,10 +770,11 @@ mod tests { let mut opponent = Glicko2Rating::new(); for _ in 0..25 { - (player, opponent) = glicko2(player, opponent, Outcomes::LOSS, &Glicko2Config::new()); + (player, opponent) = + glicko2(&player, &opponent, &Outcomes::LOSS, &Glicko2Config::new()); } - (player, opponent) = glicko2(player, opponent, Outcomes::WIN, &Glicko2Config::new()); + (player, opponent) = glicko2(&player, &opponent, &Outcomes::WIN, &Glicko2Config::new()); assert!((player.rating.round() - 1248.0).abs() < f64::EPSILON); assert!((player.deviation.round() - 176.0).abs() < f64::EPSILON); diff --git a/src/ingo.rs b/src/ingo.rs index 5b9ff8b..a972fcc 100644 --- a/src/ingo.rs +++ b/src/ingo.rs @@ -41,15 +41,15 @@ use crate::{outcomes::Outcomes, rating::IngoRating}; /// age: 40, /// }; /// -/// let (p1, p2) = ingo(player_one, player_two, Outcomes::WIN); +/// let (p1, p2) = ingo(&player_one, &player_two, &Outcomes::WIN); /// /// assert!((p1.rating.round() - 129.0).abs() < f64::EPSILON); /// assert!((p2.rating.round() - 161.0).abs() < f64::EPSILON); /// ``` pub fn ingo( - player_one: IngoRating, - player_two: IngoRating, - outcome: Outcomes, + player_one: &IngoRating, + player_two: &IngoRating, + outcome: &Outcomes, ) -> (IngoRating, IngoRating) { let score1 = match outcome { Outcomes::WIN => 1.0, @@ -136,11 +136,14 @@ pub fn ingo( /// (player_five, Outcomes::LOSS), /// ]; /// -/// let p1 = ingo_rating_period(player_one, &results); +/// let p1 = ingo_rating_period(&player_one, &results); /// /// assert!((p1.rating.round() - 126.0).abs() < f64::EPSILON); /// ``` -pub fn ingo_rating_period(player: IngoRating, results: &Vec<(IngoRating, Outcomes)>) -> IngoRating { +pub fn ingo_rating_period( + player: &IngoRating, + results: &Vec<(IngoRating, Outcomes)>, +) -> IngoRating { let development = match player.age { usize::MIN..=20 => 10.0, 21..=25 => 15.0, @@ -191,14 +194,14 @@ pub fn ingo_rating_period(player: IngoRating, results: &Vec<(IngoRating, Outcome /// age: 40, /// }; /// -/// let (exp1, exp2) = expected_score(player_one, player_two); +/// let (exp1, exp2) = expected_score(&player_one, &player_two); /// /// assert!(((exp1 * 100.0).round() - 80.0).abs() < f64::EPSILON); /// assert!(((exp2 * 100.0).round() - 20.0).abs() < f64::EPSILON); /// /// assert!((exp1 + exp2 - 1.0).abs() < f64::EPSILON); /// ``` -pub fn expected_score(player_one: IngoRating, player_two: IngoRating) -> (f64, f64) { +pub fn expected_score(player_one: &IngoRating, player_two: &IngoRating) -> (f64, f64) { let exp_one = 0.5 + (player_two.rating - player_one.rating) / 100.0; (exp_one, 1.0 - exp_one) @@ -225,12 +228,12 @@ mod tests { age: 40, }; - let (p1, p2) = ingo(player_one, player_two, Outcomes::WIN); + let (p1, p2) = ingo(&player_one, &player_two, &Outcomes::WIN); assert!((p1.rating.round() - 129.0).abs() < f64::EPSILON); assert!((p2.rating.round() - 161.0).abs() < f64::EPSILON); - let (p1, p2) = ingo(player_one, player_two, Outcomes::LOSS); + let (p1, p2) = ingo(&player_one, &player_two, &Outcomes::LOSS); assert!((p1.rating.round() - 134.0).abs() < f64::EPSILON); assert!((p2.rating.round() - 156.0).abs() < f64::EPSILON); @@ -245,7 +248,7 @@ mod tests { age: 78, }; - let (yp, op) = ingo(young_player, old_player, Outcomes::DRAW); + let (yp, op) = ingo(&young_player, &old_player, &Outcomes::DRAW); assert!((yp.rating.round() - 219.0).abs() < f64::EPSILON); assert!((op.rating.round() - 115.0).abs() < f64::EPSILON); @@ -264,7 +267,7 @@ mod tests { let results = vec![(player_two, Outcomes::WIN)]; - let p1 = ingo_rating_period(player_one, &results); + let p1 = ingo_rating_period(&player_one, &results); assert!((p1.rating.round() - 129.0).abs() < f64::EPSILON); @@ -295,7 +298,7 @@ mod tests { (player_five, Outcomes::LOSS), ]; - let p1 = ingo_rating_period(player_one, &results); + let p1 = ingo_rating_period(&player_one, &results); assert!((p1.rating.round() - 126.0).abs() < f64::EPSILON); } @@ -311,7 +314,7 @@ mod tests { age: 40, }; - let (exp1, exp2) = expected_score(player_one, player_two); + let (exp1, exp2) = expected_score(&player_one, &player_two); assert!(((exp1 * 100.0).round() - 80.0).abs() < f64::EPSILON); assert!(((exp2 * 100.0).round() - 20.0).abs() < f64::EPSILON); diff --git a/src/lib.rs b/src/lib.rs index 2cf5d08..fd68528 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ clippy::expect_used, clippy::as_conversions )] -#![allow(clippy::module_name_repetitions, clippy::doc_markdown)] +#![allow(clippy::module_name_repetitions, clippy::doc_markdown, clippy::ptr_arg)] //! Skillratings provides functions on calculating a player's skill rating. //! diff --git a/src/rating.rs b/src/rating.rs index dffda9a..76df503 100644 --- a/src/rating.rs +++ b/src/rating.rs @@ -3,7 +3,7 @@ /// The Elo rating of a player. /// /// The default rating is 1000.0. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct EloRating { /// The player's Elo rating number, by default 1000.0. pub rating: f64, diff --git a/src/trueskill.rs b/src/trueskill.rs index 99b2603..ce63729 100644 --- a/src/trueskill.rs +++ b/src/trueskill.rs @@ -58,7 +58,7 @@ use crate::{config::TrueSkillConfig, outcomes::Outcomes, rating::TrueSkillRating /// /// let config = TrueSkillConfig::new(); /// -/// let (player_one, player_two) = trueskill(player_one, player_two, outcome, &config); +/// let (player_one, player_two) = trueskill(&player_one, &player_two, &outcome, &config); /// /// assert!(((player_one.rating * 100.0).round() - 4410.0).abs() < f64::EPSILON); /// assert!(((player_one.uncertainty * 100.0).round() - 528.0).abs() < f64::EPSILON); @@ -67,9 +67,9 @@ use crate::{config::TrueSkillConfig, outcomes::Outcomes, rating::TrueSkillRating /// assert!(((player_two.uncertainty * 100.0).round() - 121.0).abs() < f64::EPSILON); /// ``` pub fn trueskill( - player_one: TrueSkillRating, - player_two: TrueSkillRating, - outcome: Outcomes, + player_one: &TrueSkillRating, + player_two: &TrueSkillRating, + outcome: &Outcomes, config: &TrueSkillConfig, ) -> (TrueSkillRating, TrueSkillRating) { let draw_margin = draw_margin(config.draw_probability, config.beta, 2.0); @@ -83,12 +83,12 @@ pub fn trueskill( ) .sqrt(); - let winning_rating = if outcome == Outcomes::WIN || outcome == Outcomes::DRAW { + let winning_rating = if outcome == &Outcomes::WIN || outcome == &Outcomes::DRAW { player_one.rating } else { player_two.rating }; - let losing_rating = if outcome == Outcomes::WIN || outcome == Outcomes::DRAW { + let losing_rating = if outcome == &Outcomes::WIN || outcome == &Outcomes::DRAW { player_two.rating } else { player_one.rating @@ -96,13 +96,13 @@ pub fn trueskill( let rating_delta = winning_rating - losing_rating; - let v = if outcome == Outcomes::DRAW { + let v = if outcome == &Outcomes::DRAW { v_draw(rating_delta, draw_margin, c) } else { v_non_draw(rating_delta, draw_margin, c) }; - let w = if outcome == Outcomes::DRAW { + let w = if outcome == &Outcomes::DRAW { w_draw(rating_delta, draw_margin, c) } else { w_non_draw(rating_delta, draw_margin, c) @@ -119,7 +119,7 @@ pub fn trueskill( }; let player_one_new = update_rating( - player_one, + *player_one, v, w, c, @@ -127,7 +127,7 @@ pub fn trueskill( rank_multiplier1, ); let player_two_new = update_rating( - player_two, + *player_two, v, w, c, @@ -175,7 +175,7 @@ pub fn trueskill( /// }; /// /// let player = trueskill_rating_period( -/// player_one, +/// &player_one, /// &vec![ /// (player_two, Outcomes::WIN), /// (player_three, Outcomes::WIN), @@ -188,11 +188,11 @@ pub fn trueskill( /// assert!(((player.uncertainty * 100.0).round() - 566.0).abs() < f64::EPSILON); /// ``` pub fn trueskill_rating_period( - player: TrueSkillRating, + player: &TrueSkillRating, results: &Vec<(TrueSkillRating, Outcomes)>, config: &TrueSkillConfig, ) -> TrueSkillRating { - let mut player = player; + let mut player = *player; let draw_margin = draw_margin(config.draw_probability, config.beta, 2.0); @@ -282,9 +282,9 @@ pub fn trueskill_rating_period( /// }; /// /// let (team_one, team_two) = trueskill_teams( -/// vec![player_one, player_two], -/// vec![player_three, player_four], -/// Outcomes::WIN, +/// &vec![player_one, player_two], +/// &vec![player_three, player_four], +/// &Outcomes::WIN, /// &TrueSkillConfig::new(), /// ); /// @@ -294,13 +294,13 @@ pub fn trueskill_rating_period( /// assert!((team_two[1].rating - 36.210_764_756_738_115).abs() < f64::EPSILON); /// ``` pub fn trueskill_teams( - team_one: Vec, - team_two: Vec, - outcome: Outcomes, + team_one: &Vec, + team_two: &Vec, + outcome: &Outcomes, config: &TrueSkillConfig, ) -> (Vec, Vec) { if team_one.is_empty() || team_two.is_empty() { - return (team_one, team_two); + return (team_one.clone(), team_two.clone()); } let total_players = (team_one.len() + team_two.len()) as f64; @@ -320,12 +320,12 @@ pub fn trueskill_teams( ) .sqrt(); - let winning_rating = if outcome == Outcomes::WIN || outcome == Outcomes::DRAW { + let winning_rating = if outcome == &Outcomes::WIN || outcome == &Outcomes::DRAW { rating_one_sum } else { rating_two_sum }; - let losing_rating = if outcome == Outcomes::WIN || outcome == Outcomes::DRAW { + let losing_rating = if outcome == &Outcomes::WIN || outcome == &Outcomes::DRAW { rating_two_sum } else { rating_one_sum @@ -333,13 +333,13 @@ pub fn trueskill_teams( let rating_delta = winning_rating - losing_rating; - let v = if outcome == Outcomes::DRAW { + let v = if outcome == &Outcomes::DRAW { v_draw(rating_delta, draw_margin, c) } else { v_non_draw(rating_delta, draw_margin, c) }; - let w = if outcome == Outcomes::DRAW { + let w = if outcome == &Outcomes::DRAW { w_draw(rating_delta, draw_margin, c) } else { w_non_draw(rating_delta, draw_margin, c) @@ -355,10 +355,22 @@ pub fn trueskill_teams( Outcomes::LOSS => 1.0, }; - let new_team_one = - update_rating_teams(team_one, v, w, c, config.default_dynamics, rank_multiplier1); - let new_team_two = - update_rating_teams(team_two, v, w, c, config.default_dynamics, rank_multiplier2); + let new_team_one = update_rating_teams( + team_one.clone(), + v, + w, + c, + config.default_dynamics, + rank_multiplier1, + ); + let new_team_two = update_rating_teams( + team_two.clone(), + v, + w, + c, + config.default_dynamics, + rank_multiplier2, + ); (new_team_one, new_team_two) } @@ -380,14 +392,14 @@ pub fn trueskill_teams( /// /// let config = TrueSkillConfig::new(); /// -/// let quality = match_quality(player_one, player_two, &config); +/// let quality = match_quality(&player_one, &player_two, &config); /// /// // According to TrueSkill, there is a 44.7% chance this match will end in a draw. /// assert!(((quality * 1000.0).round() - 447.0).abs() < f64::EPSILON); /// ``` pub fn match_quality( - player_one: TrueSkillRating, - player_two: TrueSkillRating, + player_one: &TrueSkillRating, + player_two: &TrueSkillRating, config: &TrueSkillConfig, ) -> f64 { let delta: f64 = player_one.rating - player_two.rating; @@ -446,8 +458,8 @@ pub fn match_quality( /// }; /// /// let qual = match_quality_teams( -/// vec![player_one, player_two], -/// vec![player_three, player_four], +/// &vec![player_one, player_two], +/// &vec![player_three, player_four], /// &TrueSkillConfig::new(), /// ); /// @@ -455,8 +467,8 @@ pub fn match_quality( /// assert!((qual - 0.084_108_145_418_343_24).abs() < f64::EPSILON); /// ``` pub fn match_quality_teams( - team_one: Vec, - team_two: Vec, + team_one: &Vec, + team_two: &Vec, config: &TrueSkillConfig, ) -> f64 { let total_players = (team_one.len() + team_two.len()) as f64; @@ -506,7 +518,7 @@ pub fn match_quality_teams( /// /// let config = TrueSkillConfig::new(); /// -/// let (exp1, exp2) = expected_score(better_player, worse_player, &config); +/// let (exp1, exp2) = expected_score(&better_player, &worse_player, &config); /// /// /// // Player one has an 80% chance to win and player two a 20% chance. @@ -516,8 +528,8 @@ pub fn match_quality_teams( /// assert!((exp1.mul_add(100.0, exp2 * 100.0).round() - 100.0).abs() < f64::EPSILON); /// ``` pub fn expected_score( - player_one: TrueSkillRating, - player_two: TrueSkillRating, + player_one: &TrueSkillRating, + player_two: &TrueSkillRating, config: &TrueSkillConfig, ) -> (f64, f64) { let delta1 = player_one.rating - player_two.rating; @@ -583,8 +595,8 @@ pub fn expected_score( /// }; /// /// let (exp1, exp2) = expected_score_teams( -/// vec![player_one, player_two], -/// vec![player_three, player_four], +/// &vec![player_one, player_two], +/// &vec![player_three, player_four], /// &TrueSkillConfig::new(), /// ); /// @@ -595,8 +607,8 @@ pub fn expected_score( /// assert!(((exp2 * 100.0).round() - 88.0).abs() < f64::EPSILON); /// ``` pub fn expected_score_teams( - team_one: Vec, - team_two: Vec, + team_one: &Vec, + team_two: &Vec, config: &TrueSkillConfig, ) -> (f64, f64) { let player_count = (team_one.len() + team_two.len()) as f64; @@ -643,13 +655,13 @@ pub fn expected_score_teams( /// uncertainty: 1.92, /// }; /// -/// let new_rank = get_rank(new_player); -/// let older_rank = get_rank(older_player); +/// let new_rank = get_rank(&new_player); +/// let older_rank = get_rank(&older_player); /// /// assert!((new_rank.round() - 0.0).abs() < f64::EPSILON); /// assert!((older_rank.round() - 37.0).abs() < f64::EPSILON); /// ``` -pub fn get_rank(player: TrueSkillRating) -> f64 { +pub fn get_rank(player: &TrueSkillRating) -> f64 { player.rating - (player.uncertainty * 3.0) } @@ -895,9 +907,9 @@ mod tests { }; let (p1, p2) = trueskill( - player_one, - player_two, - Outcomes::WIN, + &player_one, + &player_two, + &Outcomes::WIN, &TrueSkillConfig::new(), ); @@ -908,9 +920,9 @@ mod tests { assert!(((p2.uncertainty * 100.0).round() - 120.0).abs() < f64::EPSILON); let (p1, p2) = trueskill( - player_two, - player_one, - Outcomes::LOSS, + &player_two, + &player_one, + &Outcomes::LOSS, &TrueSkillConfig::new(), ); @@ -923,9 +935,9 @@ mod tests { let player_two = TrueSkillRating::new(); let (p1, p2) = trueskill( - player_one, - player_two, - Outcomes::WIN, + &player_one, + &player_two, + &Outcomes::WIN, &TrueSkillConfig::new(), ); @@ -953,7 +965,7 @@ mod tests { }; let player = trueskill_rating_period( - player_one, + &player_one, &vec![(player_two, Outcomes::WIN)], &TrueSkillConfig::new(), ); @@ -962,7 +974,7 @@ mod tests { assert!(((player.uncertainty * 100.0).round() - 597.0).abs() < f64::EPSILON); let player = trueskill_rating_period( - player_one, + &player_one, &vec![ (player_two, Outcomes::WIN), (player_three, Outcomes::DRAW), @@ -984,9 +996,9 @@ mod tests { }; let (p1, p2) = trueskill( - player_one, - player_two, - Outcomes::DRAW, + &player_one, + &player_two, + &Outcomes::DRAW, &TrueSkillConfig::new(), ); @@ -997,9 +1009,9 @@ mod tests { assert!(((p2.uncertainty * 100.0).round() - 119.0).abs() < f64::EPSILON); let (p2, p1) = trueskill( - player_two, - player_one, - Outcomes::DRAW, + &player_two, + &player_one, + &Outcomes::DRAW, &TrueSkillConfig::new(), ); @@ -1010,7 +1022,7 @@ mod tests { assert!(((p2.uncertainty * 100.0).round() - 119.0).abs() < f64::EPSILON); let p1 = trueskill_rating_period( - player_one, + &player_one, &vec![(player_two, Outcomes::DRAW)], &TrueSkillConfig::new(), ); @@ -1031,9 +1043,9 @@ mod tests { }; let (p1, p2) = trueskill( - player_one, - player_two, - Outcomes::WIN, + &player_one, + &player_two, + &Outcomes::WIN, &TrueSkillConfig::new(), ); @@ -1068,9 +1080,9 @@ mod tests { }; let (team_one, team_two) = trueskill_teams( - vec![player_one, player_two], - vec![player_three, player_four], - Outcomes::WIN, + &vec![player_one, player_two], + &vec![player_three, player_four], + &Outcomes::WIN, &TrueSkillConfig::new(), ); @@ -1085,9 +1097,9 @@ mod tests { assert!((team_two[1].uncertainty - 4.767_945_180_134_836).abs() < f64::EPSILON); let (team_two, team_one) = trueskill_teams( - vec![player_three, player_four], - vec![player_one, player_two], - Outcomes::LOSS, + &vec![player_three, player_four], + &vec![player_one, player_two], + &Outcomes::LOSS, &TrueSkillConfig::new(), ); @@ -1120,9 +1132,9 @@ mod tests { }; let (team_one, team_two) = trueskill_teams( - vec![player_one, player_two], - vec![player_three, player_four], - Outcomes::DRAW, + &vec![player_one, player_two], + &vec![player_three, player_four], + &Outcomes::DRAW, &TrueSkillConfig::new(), ); @@ -1137,9 +1149,9 @@ mod tests { assert!((team_two[1].uncertainty - 2.930_957_525_591_959_5).abs() < f64::EPSILON); let (team_one, _) = trueskill_teams( - vec![player_one], - vec![], - Outcomes::WIN, + &vec![player_one], + &vec![], + &Outcomes::WIN, &TrueSkillConfig::new(), ); @@ -1167,14 +1179,14 @@ mod tests { }; let qual = match_quality_teams( - vec![player_one, player_two], - vec![player_three, player_four], + &vec![player_one, player_two], + &vec![player_three, player_four], &TrueSkillConfig::new(), ); let qual2 = match_quality_teams( - vec![player_three, player_four], - vec![player_one, player_two], + &vec![player_three, player_four], + &vec![player_one, player_two], &TrueSkillConfig::new(), ); @@ -1204,8 +1216,8 @@ mod tests { }; let (exp1, exp2) = expected_score_teams( - vec![player_one, player_two], - vec![player_three, player_four], + &vec![player_one, player_two], + &vec![player_three, player_four], &TrueSkillConfig::new(), ); @@ -1219,7 +1231,7 @@ mod tests { let player_one = TrueSkillRating::new(); let player_two = TrueSkillRating::new(); - let quality = match_quality(player_one, player_two, &TrueSkillConfig::new()); + let quality = match_quality(&player_one, &player_two, &TrueSkillConfig::new()); assert!(((quality * 1000.0).round() - 447.0).abs() < f64::EPSILON); @@ -1233,11 +1245,11 @@ mod tests { ..Default::default() }; - let quality = match_quality(player_one, player_two, &TrueSkillConfig::new()); + let quality = match_quality(&player_one, &player_two, &TrueSkillConfig::new()); assert!(((quality * 10000.0).round() - 12.0).abs() < f64::EPSILON); - let quality2 = match_quality(player_two, player_one, &TrueSkillConfig::new()); + let quality2 = match_quality(&player_two, &player_one, &TrueSkillConfig::new()); assert!((quality - quality2).abs() < f64::EPSILON); } @@ -1247,7 +1259,7 @@ mod tests { let player_one = TrueSkillRating::new(); let player_two = TrueSkillRating::new(); - let (exp1, exp2) = expected_score(player_one, player_two, &TrueSkillConfig::new()); + let (exp1, exp2) = expected_score(&player_one, &player_two, &TrueSkillConfig::new()); assert!((exp1 * 100.0 - 50.0).round().abs() < f64::EPSILON); assert!((exp2 * 100.0 - 50.0).round().abs() < f64::EPSILON); @@ -1261,7 +1273,8 @@ mod tests { uncertainty: 3.0, }; - let (exp1, exp2) = expected_score(better_player, worse_player, &TrueSkillConfig::default()); + let (exp1, exp2) = + expected_score(&better_player, &worse_player, &TrueSkillConfig::default()); assert!((exp1 * 100.0 - 80.0).round().abs() < f64::EPSILON); assert!((exp2 * 100.0 - 20.0).round().abs() < f64::EPSILON); @@ -1277,8 +1290,8 @@ mod tests { uncertainty: 1.92, }; - let new_rank = get_rank(new_player); - let older_rank = get_rank(older_player); + let new_rank = get_rank(&new_player); + let older_rank = get_rank(&older_player); assert!((new_rank.round() - 0.0).abs() < f64::EPSILON); assert!((older_rank.round() - 37.0).abs() < f64::EPSILON); diff --git a/src/weng_lin.rs b/src/weng_lin.rs index 01f8fb2..e2b34ab 100644 --- a/src/weng_lin.rs +++ b/src/weng_lin.rs @@ -42,7 +42,7 @@ use crate::{config::WengLinConfig, outcomes::Outcomes, rating::WengLinRating}; /// }; /// let player_two = WengLinRating::new(); /// -/// let (player_one, player_two) = weng_lin(player_one, player_two, Outcomes::WIN, &WengLinConfig::new()); +/// let (player_one, player_two) = weng_lin(&player_one, &player_two, &Outcomes::WIN, &WengLinConfig::new()); /// /// assert!(((player_one.rating * 100.0).round() - 4203.0).abs() < f64::EPSILON); /// assert!(((player_one.uncertainty * 100.0).round() - 130.0).abs() < f64::EPSILON); @@ -50,9 +50,9 @@ use crate::{config::WengLinConfig, outcomes::Outcomes, rating::WengLinRating}; /// assert!(((player_two.uncertainty * 100.0).round() - 803.0).abs() < f64::EPSILON); /// ``` pub fn weng_lin( - player_one: WengLinRating, - player_two: WengLinRating, - outcome: Outcomes, + player_one: &WengLinRating, + player_two: &WengLinRating, + outcome: &Outcomes, config: &WengLinConfig, ) -> (WengLinRating, WengLinRating) { let c = 2.0f64 @@ -134,7 +134,7 @@ pub fn weng_lin( /// }; /// /// let player = weng_lin_rating_period( -/// player, +/// &player, /// &vec![ /// (opponent_one, Outcomes::WIN), /// (opponent_two, Outcomes::DRAW), @@ -146,11 +146,11 @@ pub fn weng_lin( /// assert!(((player.uncertainty * 100.0).round() - 780.0).abs() < f64::EPSILON); /// ``` pub fn weng_lin_rating_period( - player: WengLinRating, + player: &WengLinRating, results: &Vec<(WengLinRating, Outcomes)>, config: &WengLinConfig, ) -> WengLinRating { - let mut player = player; + let mut player = *player; for (opponent, result) in results { let c = 2.0f64 @@ -162,7 +162,7 @@ pub fn weng_lin_rating_period( ) .sqrt(); - let (p1, p2) = expected_score(player, *opponent, config); + let (p1, p2) = expected_score(&player, opponent, config); let outcome = match result { Outcomes::WIN => 1.0, @@ -228,7 +228,7 @@ pub fn weng_lin_rating_period( /// ]; /// /// let (team_one, team_two) = -/// weng_lin_teams(team_one, team_two, Outcomes::WIN, &WengLinConfig::new()); +/// weng_lin_teams(&team_one, &team_two, &Outcomes::WIN, &WengLinConfig::new()); /// /// assert!(((team_one[0].rating * 100.0).round() - 2790.0).abs() < f64::EPSILON); /// assert!(((team_one[1].rating * 100.0).round() - 3006.0).abs() < f64::EPSILON); @@ -239,14 +239,15 @@ pub fn weng_lin_rating_period( /// assert!(((team_two[2].rating * 100.0).round() - 1843.0).abs() < f64::EPSILON); /// ``` pub fn weng_lin_teams( - team_one: Vec, - team_two: Vec, - outcome: Outcomes, + team_one: &Vec, + team_two: &Vec, + outcome: &Outcomes, config: &WengLinConfig, ) -> (Vec, Vec) { if team_one.is_empty() || team_two.is_empty() { - return (team_one, team_two); + return (team_one.clone(), team_two.clone()); } + let team_one_uncertainties = team_one.iter().map(|p| p.uncertainty.powi(2)).sum::(); let team_two_uncertainties = team_two.iter().map(|p| p.uncertainty.powi(2)).sum::(); @@ -257,7 +258,7 @@ pub fn weng_lin_teams( ) .sqrt(); - let (p1, p2) = expected_score_teams(team_one.clone(), team_two.clone(), config); + let (p1, p2) = expected_score_teams(team_one, team_two, config); let outcome1 = match outcome { Outcomes::WIN => 1.0, @@ -339,15 +340,15 @@ pub fn weng_lin_teams( /// uncertainty: 1.2, /// }; /// -/// let (exp1, exp2) = expected_score(p1, p2, &WengLinConfig::new()); +/// let (exp1, exp2) = expected_score(&p1, &p2, &WengLinConfig::new()); /// /// assert!((exp1 + exp2 - 1.0).abs() < f64::EPSILON); /// /// assert!(((exp1 * 100.0).round() - 85.0).abs() < f64::EPSILON); /// ``` pub fn expected_score( - player_one: WengLinRating, - player_two: WengLinRating, + player_one: &WengLinRating, + player_two: &WengLinRating, config: &WengLinConfig, ) -> (f64, f64) { let c = 2.0f64 @@ -407,15 +408,15 @@ pub fn expected_score( /// }, /// ]; /// -/// let (exp1, exp2) = expected_score_teams(team_one, team_two, &WengLinConfig::new()); +/// let (exp1, exp2) = expected_score_teams(&team_one, &team_two, &WengLinConfig::new()); /// /// assert!((exp1 + exp2 - 1.0).abs() < f64::EPSILON); /// /// assert!(((exp1 * 100.0).round() - 21.0).abs() < f64::EPSILON); /// ``` pub fn expected_score_teams( - team_one: Vec, - team_two: Vec, + team_one: &Vec, + team_two: &Vec, config: &WengLinConfig, ) -> (f64, f64) { let team_one_ratings = team_one.iter().map(|p| p.rating).sum::(); @@ -446,7 +447,7 @@ mod tests { let p1 = WengLinRating::new(); let p2 = WengLinRating::new(); - let (t1, t2) = weng_lin(p1, p2, Outcomes::WIN, &WengLinConfig::new()); + let (t1, t2) = weng_lin(&p1, &p2, &Outcomes::WIN, &WengLinConfig::new()); assert!((t1.rating - 27.635_231_383_473_65).abs() < f64::EPSILON); assert!((t1.uncertainty - 8.065_506_316_323_548).abs() < f64::EPSILON); @@ -459,21 +460,21 @@ mod tests { }; let p2 = WengLinRating::new(); - let (t1, t2) = weng_lin(p1, p2, Outcomes::WIN, &WengLinConfig::new()); + let (t1, t2) = weng_lin(&p1, &p2, &Outcomes::WIN, &WengLinConfig::new()); assert!((t1.rating - 42.026_412_401_802_894).abs() < f64::EPSILON); assert!((t1.uncertainty - 1.299_823_053_277_078_3).abs() < f64::EPSILON); assert!((t2.rating - 23.914_677_769_440_46).abs() < f64::EPSILON); assert!((t2.uncertainty - 8.029_022_445_649_298).abs() < f64::EPSILON); - let (t1, t2) = weng_lin(p1, p2, Outcomes::LOSS, &WengLinConfig::new()); + let (t1, t2) = weng_lin(&p1, &p2, &Outcomes::LOSS, &WengLinConfig::new()); assert!((t1.rating - 41.862_153_998_286_94).abs() < f64::EPSILON); assert!((t1.uncertainty - 1.299_823_053_277_078_3).abs() < f64::EPSILON); assert!((t2.rating - 30.664_283_436_598_35).abs() < f64::EPSILON); assert!((t2.uncertainty - 8.029_022_445_649_298).abs() < f64::EPSILON); - let (t1, t2) = weng_lin(p1, p2, Outcomes::DRAW, &WengLinConfig::new()); + let (t1, t2) = weng_lin(&p1, &p2, &Outcomes::DRAW, &WengLinConfig::new()); assert!((t1.rating - 41.944_283_200_044_92).abs() < f64::EPSILON); assert!((t1.uncertainty - 1.299_823_053_277_078_3).abs() < f64::EPSILON); @@ -508,8 +509,7 @@ mod tests { }, ]; - let (nt1, nt2) = - weng_lin_teams(t1.clone(), t2.clone(), Outcomes::WIN, &WengLinConfig::new()); + let (nt1, nt2) = weng_lin_teams(&t1, &t2, &Outcomes::WIN, &WengLinConfig::new()); assert!((nt1[0].rating - 27.904_443_970_057_24).abs() < f64::EPSILON); assert!((nt1[1].rating - 30.060_226_550_163_108).abs() < f64::EPSILON); @@ -527,12 +527,7 @@ mod tests { assert!((nt2[1].uncertainty - 1.399_187_149_975_365_4).abs() < f64::EPSILON); assert!((nt2[2].uncertainty - 4.276_389_807_576_043).abs() < f64::EPSILON); - let (nt1, nt2) = weng_lin_teams( - t1.clone(), - t2.clone(), - Outcomes::DRAW, - &WengLinConfig::new(), - ); + let (nt1, nt2) = weng_lin_teams(&t1, &t2, &Outcomes::DRAW, &WengLinConfig::new()); assert!((nt1[0].rating - 25.652_558_832_338_293).abs() < f64::EPSILON); assert!((nt1[1].rating - 30.013_531_459_947_366).abs() < f64::EPSILON); @@ -545,7 +540,7 @@ mod tests { // The uncertainties do not change. assert!((nt1[0].uncertainty - 8.138_803_466_450_47).abs() < f64::EPSILON); - let (nt1, nt2) = weng_lin_teams(t1, t2, Outcomes::LOSS, &WengLinConfig::default()); + let (nt1, nt2) = weng_lin_teams(&t1, &t2, &Outcomes::LOSS, &WengLinConfig::default()); assert!((nt1[0].rating - 23.400_673_694_619_35).abs() < f64::EPSILON); assert!((nt1[1].rating - 29.966_836_369_731_627).abs() < f64::EPSILON); @@ -561,12 +556,7 @@ mod tests { let t1 = vec![WengLinRating::new()]; let t2 = Vec::new(); - let (nt1, nt2) = weng_lin_teams( - t1.clone(), - t2.clone(), - Outcomes::DRAW, - &WengLinConfig::new(), - ); + let (nt1, nt2) = weng_lin_teams(&t1, &t2, &Outcomes::DRAW, &WengLinConfig::new()); assert_eq!(t1, nt1); assert_eq!(t2, nt2); @@ -577,7 +567,7 @@ mod tests { let p1 = WengLinRating::new(); let p2 = WengLinRating::new(); - let (exp1, exp2) = expected_score(p1, p2, &WengLinConfig::new()); + let (exp1, exp2) = expected_score(&p1, &p2, &WengLinConfig::new()); assert!((exp1 - exp2).abs() < f64::EPSILON); @@ -590,7 +580,7 @@ mod tests { uncertainty: 1.2, }; - let (exp1, exp2) = expected_score(p1, p2, &WengLinConfig::new()); + let (exp1, exp2) = expected_score(&p1, &p2, &WengLinConfig::new()); assert!((exp1 + exp2 - 1.0).abs() < f64::EPSILON); @@ -603,7 +593,7 @@ mod tests { let p1 = vec![WengLinRating::new()]; let p2 = vec![WengLinRating::new()]; - let (exp1, exp2) = expected_score_teams(p1, p2, &WengLinConfig::new()); + let (exp1, exp2) = expected_score_teams(&p1, &p2, &WengLinConfig::new()); assert!((exp1 - exp2).abs() < f64::EPSILON); @@ -616,7 +606,7 @@ mod tests { uncertainty: 1.2, }]; - let (exp1, exp2) = expected_score_teams(p1.clone(), p2.clone(), &WengLinConfig::new()); + let (exp1, exp2) = expected_score_teams(&p1, &p2, &WengLinConfig::new()); assert!((exp1 + exp2 - 1.0).abs() < f64::EPSILON); @@ -634,7 +624,7 @@ mod tests { uncertainty: 1.2, }); - let (exp1, exp2) = expected_score_teams(p1.clone(), p2.clone(), &WengLinConfig::new()); + let (exp1, exp2) = expected_score_teams(&p1, &p2, &WengLinConfig::new()); assert!((exp1 + exp2 - 1.0).abs() < f64::EPSILON); @@ -643,7 +633,7 @@ mod tests { p2.push(WengLinRating::new()); - let (exp1, _) = expected_score_teams(p1, p2, &WengLinConfig::new()); + let (exp1, _) = expected_score_teams(&p1, &p2, &WengLinConfig::new()); assert!((exp1 - 0.213_836_440_502_453_18).abs() < f64::EPSILON); } @@ -658,23 +648,27 @@ mod tests { uncertainty: 4.2, }; - let (normal_player, _) = - weng_lin(player, opponent_one, Outcomes::WIN, &WengLinConfig::new()); let (normal_player, _) = weng_lin( - normal_player, - opponent_two, - Outcomes::DRAW, + &player, + &opponent_one, + &Outcomes::WIN, + &WengLinConfig::new(), + ); + let (normal_player, _) = weng_lin( + &normal_player, + &opponent_two, + &Outcomes::DRAW, &WengLinConfig::new(), ); let (normal_player, _) = weng_lin( - normal_player, - opponent_two, - Outcomes::LOSS, + &normal_player, + &opponent_two, + &Outcomes::LOSS, &WengLinConfig::new(), ); let rating_player = weng_lin_rating_period( - player, + &player, &vec![ (opponent_one, Outcomes::WIN), (opponent_two, Outcomes::DRAW),