Skip to main content

battler_data/mons/
leveling_rate.rs

1use serde_string_enum::{
2    DeserializeLabeledStringEnum,
3    SerializeLabeledStringEnum,
4};
5
6/// Leveling rate, which determines how much experience is required for a species to level up.
7#[derive(
8    Debug, Clone, Copy, PartialEq, SerializeLabeledStringEnum, DeserializeLabeledStringEnum,
9)]
10pub enum LevelingRate {
11    #[string = "Erratic"]
12    Erratic,
13    #[string = "Fast"]
14    Fast,
15    #[string = "Medium Fast"]
16    #[alias = "Medium"]
17    MediumFast,
18    #[string = "Medium Slow"]
19    MediumSlow,
20    #[string = "Slow"]
21    Slow,
22    #[string = "Fluctuating"]
23    Fluctuating,
24}
25
26impl LevelingRate {
27    /// The amount of experience a Mon at the given level should have.
28    pub fn exp_at_level(&self, level: u8) -> u32 {
29        if level == 1 {
30            return 0;
31        }
32        let level = level as u32;
33        let squared_level = level * level;
34        let cubed_level = level * level * level;
35        match self {
36            Self::Erratic => {
37                if level < 50 {
38                    (cubed_level * (100 - level)) / 50
39                } else if level < 68 {
40                    (cubed_level * (150 - level)) / 100
41                } else if level < 98 {
42                    (cubed_level * ((1911 - 10 * level) / 3)) / 500
43                } else {
44                    (cubed_level * (160 - level)) / 100
45                }
46            }
47            Self::Fast => (4 * cubed_level) / 5,
48            Self::MediumFast => cubed_level,
49            Self::MediumSlow => {
50                ((6 * cubed_level) / 5)
51                    .overflowing_sub(15 * squared_level)
52                    .0
53                    .overflowing_add(100 * level)
54                    .0
55                    - 140
56            }
57            Self::Slow => (5 * cubed_level) / 4,
58            Self::Fluctuating => {
59                if level < 15 {
60                    (cubed_level * ((level + 1) / 3 + 24)) / 50
61                } else if level < 36 {
62                    (cubed_level * (level + 14)) / 50
63                } else {
64                    (cubed_level * (level / 2 + 32)) / 50
65                }
66            }
67        }
68    }
69
70    /// Calculates a Mon's level based on experience points.
71    pub fn level_from_exp(&self, exp: u32) -> u8 {
72        let mut min = 1;
73        let mut max = 100;
74        while max - min > 1 {
75            let mid = (max - min) / 2 + min;
76            let mid_exp = self.exp_at_level(mid);
77            if exp < mid_exp {
78                max = mid - 1;
79            } else if exp > mid_exp {
80                // We can be between levels.
81                min = mid;
82            } else {
83                min = mid;
84                max = mid;
85            }
86        }
87        if exp >= self.exp_at_level(max) {
88            max
89        } else {
90            min
91        }
92    }
93}
94
95#[cfg(test)]
96mod leveling_rate_test {
97    use crate::{
98        mons::LevelingRate,
99        test_util::{
100            test_string_deserialization,
101            test_string_serialization,
102        },
103    };
104
105    #[test]
106    fn serializes_to_string() {
107        test_string_serialization(LevelingRate::Erratic, "Erratic");
108        test_string_serialization(LevelingRate::Fast, "Fast");
109        test_string_serialization(LevelingRate::MediumFast, "Medium Fast");
110        test_string_serialization(LevelingRate::MediumSlow, "Medium Slow");
111        test_string_serialization(LevelingRate::Slow, "Slow");
112        test_string_serialization(LevelingRate::Fluctuating, "Fluctuating");
113    }
114
115    #[test]
116    fn deserializes_lowercase() {
117        test_string_deserialization("erratic", LevelingRate::Erratic);
118        test_string_deserialization("fast", LevelingRate::Fast);
119        test_string_deserialization("medium fast", LevelingRate::MediumFast);
120        test_string_deserialization("medium slow", LevelingRate::MediumSlow);
121        test_string_deserialization("slow", LevelingRate::Slow);
122        test_string_deserialization("fluctuating", LevelingRate::Fluctuating);
123    }
124
125    #[test]
126    fn calculates_experience_at_level() {
127        assert_eq!(LevelingRate::Erratic.exp_at_level(1), 0);
128        assert_eq!(LevelingRate::Erratic.exp_at_level(2), 15);
129        assert_eq!(LevelingRate::Erratic.exp_at_level(20), 12800);
130        assert_eq!(LevelingRate::Erratic.exp_at_level(50), 125000);
131        assert_eq!(LevelingRate::Erratic.exp_at_level(77), 346965);
132        assert_eq!(LevelingRate::Erratic.exp_at_level(100), 600000);
133
134        assert_eq!(LevelingRate::Fast.exp_at_level(1), 0);
135        assert_eq!(LevelingRate::Fast.exp_at_level(2), 6);
136        assert_eq!(LevelingRate::Fast.exp_at_level(20), 6400);
137        assert_eq!(LevelingRate::Fast.exp_at_level(50), 100000);
138        assert_eq!(LevelingRate::Fast.exp_at_level(77), 365226);
139        assert_eq!(LevelingRate::Fast.exp_at_level(100), 800000);
140
141        assert_eq!(LevelingRate::MediumFast.exp_at_level(1), 0);
142        assert_eq!(LevelingRate::MediumFast.exp_at_level(2), 8);
143        assert_eq!(LevelingRate::MediumFast.exp_at_level(20), 8000);
144        assert_eq!(LevelingRate::MediumFast.exp_at_level(50), 125000);
145        assert_eq!(LevelingRate::MediumFast.exp_at_level(77), 456533);
146        assert_eq!(LevelingRate::MediumFast.exp_at_level(100), 1000000);
147
148        assert_eq!(LevelingRate::MediumSlow.exp_at_level(1), 0);
149        assert_eq!(LevelingRate::MediumSlow.exp_at_level(2), 9);
150        assert_eq!(LevelingRate::MediumSlow.exp_at_level(20), 5460);
151        assert_eq!(LevelingRate::MediumSlow.exp_at_level(50), 117360);
152        assert_eq!(LevelingRate::MediumSlow.exp_at_level(77), 466464);
153        assert_eq!(LevelingRate::MediumSlow.exp_at_level(100), 1059860);
154
155        assert_eq!(LevelingRate::Slow.exp_at_level(1), 0);
156        assert_eq!(LevelingRate::Slow.exp_at_level(2), 10);
157        assert_eq!(LevelingRate::Slow.exp_at_level(20), 10000);
158        assert_eq!(LevelingRate::Slow.exp_at_level(50), 156250);
159        assert_eq!(LevelingRate::Slow.exp_at_level(77), 570666);
160        assert_eq!(LevelingRate::Slow.exp_at_level(100), 1250000);
161
162        assert_eq!(LevelingRate::Fluctuating.exp_at_level(1), 0);
163        assert_eq!(LevelingRate::Fluctuating.exp_at_level(2), 4);
164        assert_eq!(LevelingRate::Fluctuating.exp_at_level(20), 5440);
165        assert_eq!(LevelingRate::Fluctuating.exp_at_level(50), 142500);
166        assert_eq!(LevelingRate::Fluctuating.exp_at_level(77), 639146);
167        assert_eq!(LevelingRate::Fluctuating.exp_at_level(100), 1640000);
168    }
169
170    #[test]
171    fn calculates_level_from_exp() {
172        assert_eq!(LevelingRate::Erratic.level_from_exp(0), 1);
173        assert_eq!(LevelingRate::Erratic.level_from_exp(14), 1);
174        assert_eq!(LevelingRate::Erratic.level_from_exp(15), 2);
175        assert_eq!(LevelingRate::Erratic.level_from_exp(209728), 62);
176        assert_eq!(LevelingRate::Erratic.level_from_exp(217539), 62);
177        assert_eq!(LevelingRate::Erratic.level_from_exp(217540), 63);
178        assert_eq!(LevelingRate::Erratic.level_from_exp(600000), 100);
179
180        assert_eq!(LevelingRate::Slow.level_from_exp(0), 1);
181        assert_eq!(LevelingRate::Slow.level_from_exp(9), 1);
182        assert_eq!(LevelingRate::Slow.level_from_exp(10), 2);
183        assert_eq!(LevelingRate::Slow.level_from_exp(297910), 62);
184        assert_eq!(LevelingRate::Slow.level_from_exp(312557), 62);
185        assert_eq!(LevelingRate::Slow.level_from_exp(312558), 63);
186        assert_eq!(LevelingRate::Slow.level_from_exp(1250000), 100);
187    }
188}