elo/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
pub struct EloRank {
    pub k: i32,
}

impl EloRank {
    fn calculate_expected(&self, score_a: f64, score_b: f64) -> f64 {
        1.0 / (1.0 + (10f64.powf((score_b - score_a) / 400.0)))
    }

    pub fn calculate(&self, winner: f64, looser: f64) -> (f64, f64) {
        let k = self.k as f64;

        let expected_a = self.calculate_expected(winner, looser);
        let expected_b = self.calculate_expected(looser, winner);

        let (score_w, score_l) = (1.0, 0.0);
        let winner_new_score = winner + k * (score_w - expected_a);
        let looser_new_score = looser + k * (score_l - expected_b);

        (winner_new_score, looser_new_score)
    }
}

#[cfg(test)]
mod tests {
    use crate::EloRank;

    #[test]
    fn calculates_correct_ratings() {
        let elo = EloRank { k: 32 };
        let (winner_new, looser_new) = elo.calculate(1200.0, 1400.0);
        assert_eq!(winner_new, 1224.3119016527346);
        assert_eq!(looser_new, 1375.6880983472654);

        let (winner_new, looser_new) = elo.calculate(1400.0, 1200.0);
        assert_eq!(winner_new, 1407.6880983472654);
        assert_eq!(looser_new, 1192.3119016527346);
    }

    #[test]
    fn rounds_ratings_properly() {
        let elo = EloRank { k: 32 };
        let (winner_new, looser_new) = elo.calculate(1802.0, 1186.0);
        assert_eq!(winner_new, 1802.8970197488543);
        assert_eq!(looser_new, 1185.1029802511457);
    }
}