elo_multiplayer/
lib.rs

1pub struct EloRank {
2    pub k: i32,
3    pub players: Vec<f64>,
4    pub score_base: f64,
5}
6
7impl Default for EloRank {
8    fn default() -> EloRank {
9        EloRank {
10            k: 32,
11            players: vec![1000f64, 1000f64],
12            score_base: 1.0,
13        }
14    }
15}
16
17impl EloRank {
18    fn calculate_expected(&self) -> Vec<f64> {
19        let amount_of_players = self.players.len() as f64;
20        let mut expected_array: Vec<f64> = vec![];
21        for (i, _) in self.players.iter().enumerate() {
22            let sum = self.players.iter().enumerate().fold(0f64, |s, (j, p)| {
23                if i == j {
24                    s
25                } else {
26                    s + 1.0 / (1.0 + 10f64.powf((p - self.players[i]) / 400f64))
27                }
28            });
29            let expected = sum / ((amount_of_players * (amount_of_players - 1f64)) / 2f64);
30            expected_array.push(expected)
31        }
32        expected_array
33    }
34
35    fn calculate_scores(&self) -> Vec<f64> {
36        let amount_of_players = self.players.len() as f64;
37        let mut scores: Vec<f64> = vec![];
38        for (i, _) in self.players.iter().enumerate() {
39            let score: f64;
40            if self.score_base == 1.0 {
41                score = (amount_of_players - (i as f64 + 1f64))
42                    / (amount_of_players * (amount_of_players - 1f64) / 2f64);
43            } else {
44                score = (self.score_base.powf(amount_of_players - (i + 1) as f64) - 1f64)
45                    / (self.players.iter().enumerate().fold(0.0, |s, (j, _)| {
46                        s + (self.score_base.powf(amount_of_players - (j + 1) as f64) - 1f64)
47                    }));
48            }
49            scores.push(score);
50        }
51        scores
52    }
53
54    pub fn calculate(&self) -> Vec<f64> {
55        let amount_of_players = self.players.len() as f64;
56        let expected = self.calculate_expected();
57        let scores = self.calculate_scores();
58        let mut new_elo: Vec<f64> = vec![];
59        for (i, p) in self.players.iter().enumerate() {
60            new_elo
61                .push(p + (self.k as f64) * (amount_of_players - 1f64) * (scores[i] - expected[i]));
62        }
63        new_elo
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use crate::EloRank;
70    #[test]
71    fn test_elo() {
72        let elo = EloRank {
73            players: vec![897.0, 978.0],
74            ..Default::default()
75        };
76        assert_eq!(elo.calculate(), vec![916.6640435522738, 958.3359564477262]);
77
78        let elo = EloRank {
79            players: vec![897.0, 978.0],
80            score_base: 2.0,
81            ..Default::default()
82        };
83        
84        assert_eq!(elo.calculate(), vec![916.6640435522738, 958.3359564477262]);
85
86        let elo = EloRank {
87            players: vec![978.0, 897.0],
88            ..Default::default()
89        };
90        assert_eq!(elo.calculate(), vec![990.3359564477262, 884.6640435522738]);
91
92        let elo = EloRank {
93            players: vec![1000.0, 1000.0, 1000.0],
94            ..Default::default()
95        };
96        assert_eq!(
97            elo.calculate(),
98            vec![1021.3333333333334, 1000.0, 978.6666666666666]
99        );
100
101        let elo = EloRank {
102            players: vec![1000.0, 1000.0, 1000.0],
103            score_base: 1.5,
104            ..Default::default()
105        };
106        assert_eq!(
107            elo.calculate(),
108            [1024.3809523809523, 996.952380952381, 978.6666666666666]
109        );
110
111        let elo = EloRank {
112            players: vec![897.0, 978.0, 982.0, 995.0, 1017.0, 1034.0, 1096.0],
113            ..Default::default()
114        };
115        assert_eq!(
116            elo.calculate(),
117            vec![
118                933.4271852776218,
119                998.2520240277877,
120                992.7510517787325,
121                995.4420183323965,
122                1006.3245067439552,
123                1012.6627230848082,
124                1060.140490754698
125            ]
126        );
127    }
128}