osrs_api/
skill.rs

1use serde::Deserialize;
2use thousands::Separable;
3
4// FIXME: f64 is not entirely accurate since
5// 32-bit integer with one decimal point is actually
6// how the xp values are stored
7// so when we do calculations it is actually off
8// by a decent margin due to the accuracy being better
9type XP = f64;
10type Level = u64;
11type Rank = u64;
12
13#[derive(Debug, Copy, Clone, PartialEq, Deserialize)]
14pub struct Skill {
15    pub rank: Rank,
16    pub level: Level,
17    pub xp: XP
18}
19
20impl Skill {
21    pub fn xp_till_200_mill(self) -> XP {
22        200_000_000f64 - self.xp
23    }
24
25    pub fn xp_at_level(level: Level) -> XP {
26        let prev_level_sum: f64 = (1..level).into_iter()
27            .map(|n: u64| n as f64 + 300.0 * 2f64.powf(n as f64/7.0))
28            .sum();
29
30        (1.0/4.0) * prev_level_sum
31    }
32
33    pub fn xp_till_level(self, level: Level) -> XP {
34        if level < self.level {
35            panic!("Your level is higher than the provided level")
36        }
37
38        Self::xp_at_level(level) - self.xp
39    }
40}
41
42impl Into<Skill> for (Rank, Level, XP) {
43    fn into(self) -> Skill {
44        Skill {
45            rank: self.0,
46            level: self.1,
47            xp: self.2
48        }
49    }
50}
51
52// TODO: Make this a conditional compilation target
53impl std::fmt::Display for Skill {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        let rank = self.rank.separate_with_commas();
56        let level = self.level.separate_with_commas();
57        let xp = self.xp.separate_with_commas();
58
59        write!(f, "({}, {}, {})", rank, level, xp)
60    }
61}
62
63
64
65impl Default for Skill {
66    fn default() -> Self {
67        Skill {
68            rank: 0,
69            level: 0,
70            xp: 0f64
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    // FIXME: Currently these tests fail because the precision
80    // is too high
81    #[test]
82    fn test_xp_at_level() {
83        assert_eq!(Skill::xp_at_level(50), 101_333f64);
84    }
85
86    #[test]
87    fn test_xp_till_200_mill() {
88        let skill_1 = Skill {
89            rank: 20_000,
90            level: 33,
91            xp: 19_000f64
92        };
93
94        assert_eq!(skill_1.xp_till_200_mill(), 200_000_000f64 - skill_1.xp);
95    }
96
97    #[test]
98    fn test_xp_till_level() {
99        let skill_1 = Skill {
100            rank: 20_000,
101            level: 33,
102            xp: 19_000f64
103        };
104
105        assert_eq!(skill_1.xp_till_level(50), 101_333f64 - 19_000f64);
106    }
107}
108