1use super::{Age, Gender, Generate, Size};
2use rand::prelude::*;
3
4pub struct Species;
5
6impl Generate for Species {
7 fn gen_gender(rng: &mut impl Rng) -> Gender {
8 match rng.gen_range(1..=101) {
9 1..=50 => Gender::Feminine,
10 51..=100 => Gender::Masculine,
11 101 => Gender::NonBinaryThey,
12 _ => unreachable!(),
13 }
14 }
15
16 fn gen_age_years(rng: &mut impl Rng) -> u16 {
17 rng.gen_range(0..=79)
18 }
19
20 fn gen_years_from_age(rng: &mut impl Rng, age: &Age) -> u16 {
21 rng.gen_range(match age {
22 Age::Infant => 0..=1,
23 Age::Child => 2..=9,
24 Age::Adolescent => 10..=19,
25 Age::YoungAdult => 20..=29,
26 Age::Adult => 30..=39,
27 Age::MiddleAged => 40..=59,
28 Age::Elderly => 60..=69,
29 Age::Geriatric => 70..=79,
30 })
31 }
32
33 fn age_from_years(years: u16) -> Age {
34 match years {
35 i if i < 2 => Age::Infant,
36 i if i < 10 => Age::Child,
37 i if i < 20 => Age::Adolescent,
38 i if i < 30 => Age::YoungAdult,
39 i if i < 40 => Age::Adult,
40 i if i < 60 => Age::MiddleAged,
41 i if i < 70 => Age::Elderly,
42 _ => Age::Geriatric,
43 }
44 }
45
46 fn gen_size(rng: &mut impl Rng, age_years: u16, gender: &Gender) -> Size {
47 let is_female = match gender {
48 Gender::Masculine => rng.gen_bool(0.01),
49 Gender::Feminine => rng.gen_bool(0.99),
50 _ => rng.gen_bool(0.5),
51 };
52
53 match (age_years, is_female) {
54 (0, _) => {
55 let size = rng.gen_range(0..=30);
56 Size::Tiny {
57 height: 20 + size / 3,
58 weight: 7 + size / 2,
59 }
60 }
61 (1, _) => {
62 let size = rng.gen_range(0..=5);
63 Size::Tiny {
64 height: 30 + size,
65 weight: 22 + size,
66 }
67 }
68 (2..=9, _) => {
69 let y = (age_years - 2) as f32 / 8.;
70 let (height, weight) =
71 super::gen_height_weight(rng, (33. + y * 18.)..=(35. + y * 22.), 14.0..=17.0);
72 Size::Small { height, weight }
73 }
74 (10..=19, true) => {
75 let y = (age_years - 10) as f32;
76 let (height, weight) = super::gen_height_weight(
77 rng,
78 (51. + y * 2.).min(61.)..=(65. + y * 2.).min(67.),
79 (15. + y * 2.5 / 5.).min(18.5)..=(19. + y * 4.5 / 5.).min(25.),
80 );
81 Size::Medium { height, weight }
82 }
83 (10..=19, false) => {
84 let y = (age_years - 10) as f32 / 5.;
85 let (height, weight) = super::gen_height_weight(
86 rng,
87 (51. + y * 12.).min(66.)..=(57. + y * 13.).min(72.),
88 (15. + y * 2.5).min(18.5)..=(18.5 + y * 4.5).min(29.),
89 );
90 Size::Medium { height, weight }
91 }
92 (_, true) => {
93 let (height, weight) = super::gen_height_weight(rng, 61.0..=67.0, 19.0..=25.0);
94 Size::Medium { height, weight }
95 }
96 (_, false) => {
97 let (height, weight) = super::gen_height_weight(rng, 66.0..=72.0, 18.5..=29.0);
98 Size::Medium { height, weight }
99 }
100 }
101 }
102}
103
104#[cfg(test)]
105mod test_generate_for_species {
106 use super::*;
107 use std::collections::HashMap;
108
109 #[test]
110 fn gen_age_years_test() {
111 let mut rng = SmallRng::seed_from_u64(0);
112
113 assert_eq!(
114 [35, 35, 78, 36, 71],
115 [
116 Species::gen_age_years(&mut rng),
117 Species::gen_age_years(&mut rng),
118 Species::gen_age_years(&mut rng),
119 Species::gen_age_years(&mut rng),
120 Species::gen_age_years(&mut rng),
121 ],
122 );
123 }
124
125 #[test]
126 fn gen_years_from_age_test() {
127 let ages = [
128 Age::Infant,
129 Age::Child,
130 Age::Adolescent,
131 Age::YoungAdult,
132 Age::Adult,
133 Age::MiddleAged,
134 Age::Elderly,
135 Age::Geriatric,
136 ];
137
138 for age in ages {
139 let mut rng = SmallRng::seed_from_u64(0);
140
141 for _ in 0..10 {
142 let age_years = Species::gen_years_from_age(&mut rng, &age);
143 assert_eq!(age, Species::age_from_years(age_years));
144 }
145 }
146 }
147
148 #[test]
149 fn age_from_years_test() {
150 assert_eq!(Age::Infant, Species::age_from_years(0));
151 assert_eq!(Age::Infant, Species::age_from_years(1));
152
153 assert_eq!(Age::Child, Species::age_from_years(2));
154 assert_eq!(Age::Child, Species::age_from_years(9));
155
156 assert_eq!(Age::Adolescent, Species::age_from_years(10));
157 assert_eq!(Age::Adolescent, Species::age_from_years(19));
158
159 assert_eq!(Age::YoungAdult, Species::age_from_years(20));
160 assert_eq!(Age::YoungAdult, Species::age_from_years(29));
161
162 assert_eq!(Age::Adult, Species::age_from_years(30));
163 assert_eq!(Age::Adult, Species::age_from_years(39));
164
165 assert_eq!(Age::MiddleAged, Species::age_from_years(40));
166 assert_eq!(Age::MiddleAged, Species::age_from_years(59));
167
168 assert_eq!(Age::Elderly, Species::age_from_years(60));
169 assert_eq!(Age::Elderly, Species::age_from_years(69));
170
171 assert_eq!(Age::Geriatric, Species::age_from_years(70));
172 assert_eq!(Age::Geriatric, Species::age_from_years(u16::MAX));
173 }
174
175 #[test]
176 fn gen_gender_test() {
177 let mut rng = SmallRng::seed_from_u64(0);
178 let mut genders: HashMap<String, u16> = HashMap::new();
179
180 for _ in 0..500 {
181 let gender = Species::gen_gender(&mut rng);
182 *genders.entry(format!("{}", gender)).or_default() += 1;
183 }
184
185 assert_eq!(3, genders.len());
186 assert_eq!(Some(&3), genders.get("non-binary (they/them)"));
187 assert_eq!(Some(&233), genders.get("feminine (she/her)"));
188 assert_eq!(Some(&264), genders.get("masculine (he/him)"));
189 }
190
191 #[test]
192 fn gen_size_male_test() {
193 let mut rng = SmallRng::seed_from_u64(0);
194
195 assert_eq!(
197 vec![
198 (0, "tiny", 24, 13),
199 (1, "tiny", 32, 24),
200 (2, "small", 35, 28),
201 (3, "small", 36, 30),
202 (4, "small", 41, 39),
203 (5, "small", 44, 38),
204 (6, "small", 42, 43),
205 (7, "small", 47, 44),
206 (8, "small", 50, 55),
207 (9, "small", 54, 56),
208 (10, "medium", 53, 79),
209 (11, "medium", 54, 82),
210 (12, "medium", 57, 73),
211 (13, "medium", 59, 83),
212 (14, "medium", 67, 120),
213 (15, "medium", 65, 130),
214 (16, "medium", 66, 124),
215 (17, "medium", 70, 125),
216 (18, "medium", 71, 127),
217 (19, "medium", 72, 197),
218 (20, "medium", 67, 122),
219 ],
220 (0u16..=20)
221 .map(move |y| {
222 let size = Species::gen_size(&mut rng, y, &Gender::Masculine);
223 (y, size.name(), size.height(), size.weight())
224 })
225 .collect::<Vec<_>>(),
226 );
227 }
228
229 #[test]
230 fn gen_size_female_test() {
231 let mut rng = SmallRng::seed_from_u64(0);
232
233 assert_eq!(
235 vec![
236 (0, "tiny", 24, 13),
237 (1, "tiny", 32, 24),
238 (2, "small", 35, 28),
239 (3, "small", 36, 30),
240 (4, "small", 41, 39),
241 (5, "small", 44, 38),
242 (6, "small", 42, 43),
243 (7, "small", 47, 44),
244 (8, "small", 50, 55),
245 (9, "small", 54, 56),
246 (10, "medium", 56, 91),
247 (11, "medium", 55, 87),
248 (12, "medium", 58, 75),
249 (13, "medium", 59, 82),
250 (14, "medium", 67, 119),
251 (15, "medium", 63, 122),
252 (16, "medium", 62, 108),
253 (17, "medium", 65, 108),
254 (18, "medium", 66, 111),
255 (19, "medium", 67, 160),
256 (20, "medium", 62, 107),
257 ],
258 (0u16..=20)
259 .map(move |y| {
260 let size = Species::gen_size(&mut rng, y, &Gender::Feminine);
261 (y, size.name(), size.height(), size.weight())
262 })
263 .collect::<Vec<_>>(),
264 );
265 }
266}