rosu_pp/catch/performance/hitresult_generator/
ignore_acc.rs1use std::cmp;
2
3use crate::{
4 any::{HitResultGenerator, hitresult_generator::IgnoreAccuracy},
5 catch::{Catch, CatchHitResults, performance::inspect::InspectCatchPerformance},
6};
7
8impl HitResultGenerator<Catch> for IgnoreAccuracy {
9 fn generate_hitresults(inspect: InspectCatchPerformance<'_>) -> CatchHitResults {
10 let n_fruits = inspect.attrs.n_fruits;
11 let n_droplets = inspect.attrs.n_droplets;
12 let n_tiny_droplets = inspect.attrs.n_tiny_droplets;
13
14 let misses = inspect.misses();
16
17 let mut fruit_droplet_remain = (n_fruits + n_droplets).saturating_sub(misses);
19
20 let mut tiny_droplet_remain = n_tiny_droplets;
22
23 let mut assign_fruit_droplet = |specified: Option<u32>, max: u32| -> Option<u32> {
25 let value = specified?;
26 let assigned = cmp::min(cmp::min(value, max), fruit_droplet_remain);
27 fruit_droplet_remain = fruit_droplet_remain.saturating_sub(assigned);
28
29 Some(assigned)
30 };
31
32 let mut assign_tiny_droplet = |specified: Option<u32>| -> Option<u32> {
34 let value = specified?;
35 let assigned = cmp::min(value, tiny_droplet_remain);
36 tiny_droplet_remain = tiny_droplet_remain.saturating_sub(assigned);
37
38 Some(assigned)
39 };
40
41 let fruits = assign_fruit_droplet(inspect.fruits, n_fruits);
43 let droplets = assign_fruit_droplet(inspect.droplets, n_droplets);
44 let tiny_droplets = assign_tiny_droplet(inspect.tiny_droplets);
45 let tiny_droplet_misses = assign_tiny_droplet(inspect.tiny_droplet_misses);
46
47 let fruits = fruits.unwrap_or_else(|| {
49 let take = cmp::min(fruit_droplet_remain, n_fruits);
50 fruit_droplet_remain = fruit_droplet_remain.saturating_sub(take);
51
52 take
53 });
54
55 let droplets = droplets.unwrap_or_else(|| {
56 let take = cmp::min(fruit_droplet_remain, n_droplets);
57 fruit_droplet_remain = fruit_droplet_remain.saturating_sub(take);
58
59 take
60 });
61
62 let tiny_droplets = tiny_droplets.unwrap_or_else(|| {
63 let take = tiny_droplet_remain;
64 tiny_droplet_remain = 0;
65
66 take
67 });
68
69 let tiny_droplet_misses = tiny_droplet_misses.unwrap_or(tiny_droplet_remain);
70
71 let pool_total = n_fruits + n_droplets;
74 let current_sum = fruits + droplets + misses;
75
76 let (fruits, droplets) = match current_sum.cmp(&pool_total) {
77 cmp::Ordering::Less => {
78 let needed = pool_total - current_sum;
80 let new_droplets = cmp::min(droplets + needed, n_droplets);
81 let still_needed = pool_total.saturating_sub(fruits + new_droplets + misses);
82 let new_fruits = cmp::min(fruits + still_needed, n_fruits);
83
84 (new_fruits, new_droplets)
85 }
86 cmp::Ordering::Equal => (fruits, droplets),
87 cmp::Ordering::Greater => {
88 let excess = current_sum - pool_total;
90 let new_droplets = droplets.saturating_sub(excess);
91 let still_excess = (fruits + new_droplets + misses).saturating_sub(pool_total);
92 let new_fruits = fruits.saturating_sub(still_excess);
93
94 (new_fruits, new_droplets)
95 }
96 };
97
98 let tiny_pool_total = n_tiny_droplets;
100 let tiny_current_sum = tiny_droplets + tiny_droplet_misses;
101
102 let (tiny_droplets, tiny_droplet_misses) = match tiny_current_sum.cmp(&tiny_pool_total) {
103 cmp::Ordering::Less => {
104 let needed = tiny_pool_total - tiny_current_sum;
106 let new_tiny_droplets = cmp::min(tiny_droplets + needed, n_tiny_droplets);
107 let still_needed = tiny_pool_total.saturating_sub(new_tiny_droplets);
108
109 (new_tiny_droplets, still_needed)
110 }
111 cmp::Ordering::Equal => (tiny_droplets, tiny_droplet_misses),
112 cmp::Ordering::Greater => {
113 let excess = tiny_current_sum - tiny_pool_total;
115 let new_tiny_droplet_misses = tiny_droplet_misses.saturating_sub(excess);
116 let still_excess =
117 (tiny_droplets + new_tiny_droplet_misses).saturating_sub(tiny_pool_total);
118 let new_tiny_droplets = tiny_droplets.saturating_sub(still_excess);
119
120 (new_tiny_droplets, new_tiny_droplet_misses)
121 }
122 };
123
124 CatchHitResults {
125 fruits,
126 droplets,
127 tiny_droplets,
128 tiny_droplet_misses,
129 misses,
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use crate::{Difficulty, catch::CatchDifficultyAttributes};
137
138 use super::*;
139
140 #[test]
141 fn all_missing() {
142 const N_FRUITS: u32 = 50;
143 const N_DROPLETS: u32 = 25;
144 const N_TINY_DROPLETS: u32 = 100;
145
146 let inspect = InspectCatchPerformance {
147 attrs: &CatchDifficultyAttributes {
148 n_fruits: N_FRUITS,
149 n_droplets: N_DROPLETS,
150 n_tiny_droplets: N_TINY_DROPLETS,
151 ..Default::default()
152 },
153 difficulty: &Difficulty::new(),
154 acc: None,
155 combo: None,
156 fruits: None,
157 droplets: None,
158 tiny_droplets: None,
159 tiny_droplet_misses: None,
160 misses: Some(5),
161 };
162
163 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
164
165 assert_eq!(result.fruits, N_FRUITS);
168 assert_eq!(result.droplets, 20); assert_eq!(result.tiny_droplets, N_TINY_DROPLETS);
170 assert_eq!(result.tiny_droplet_misses, 0);
171 assert_eq!(result.misses, 5);
172 assert_eq!(result.total_hits(), N_FRUITS + N_DROPLETS + N_TINY_DROPLETS);
173 }
174
175 #[test]
176 fn some_provided() {
177 const N_FRUITS: u32 = 50;
178 const N_DROPLETS: u32 = 25;
179 const N_TINY_DROPLETS: u32 = 100;
180
181 let inspect = InspectCatchPerformance {
182 attrs: &CatchDifficultyAttributes {
183 n_fruits: N_FRUITS,
184 n_droplets: N_DROPLETS,
185 n_tiny_droplets: N_TINY_DROPLETS,
186 ..Default::default()
187 },
188 difficulty: &Difficulty::new(),
189 acc: None,
190 combo: None,
191 fruits: Some(30),
192 droplets: None,
193 tiny_droplets: Some(50),
194 tiny_droplet_misses: None,
195 misses: Some(10),
196 };
197
198 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
199
200 assert_eq!(result.fruits, 40);
206 assert_eq!(result.droplets, 25);
207 assert_eq!(result.tiny_droplets, 50);
208 assert_eq!(result.tiny_droplet_misses, 50); assert_eq!(result.misses, 10);
210 }
211
212 #[test]
213 fn droplets_provided() {
214 const N_FRUITS: u32 = 50;
215 const N_DROPLETS: u32 = 25;
216 const N_TINY_DROPLETS: u32 = 100;
217
218 let inspect = InspectCatchPerformance {
219 attrs: &CatchDifficultyAttributes {
220 n_fruits: N_FRUITS,
221 n_droplets: N_DROPLETS,
222 n_tiny_droplets: N_TINY_DROPLETS,
223 ..Default::default()
224 },
225 difficulty: &Difficulty::new(),
226 acc: None,
227 combo: None,
228 fruits: None,
229 droplets: Some(15),
230 tiny_droplets: None,
231 tiny_droplet_misses: Some(80),
232 misses: Some(8),
233 };
234
235 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
236
237 assert_eq!(result.fruits, 50);
243 assert_eq!(result.droplets, 17);
244 assert_eq!(result.tiny_droplets, 20); assert_eq!(result.tiny_droplet_misses, 80);
246 assert_eq!(result.misses, 8);
247 }
248
249 #[test]
250 fn all_provided() {
251 const N_FRUITS: u32 = 40;
252 const N_DROPLETS: u32 = 20;
253 const N_TINY_DROPLETS: u32 = 80;
254
255 let inspect = InspectCatchPerformance {
256 attrs: &CatchDifficultyAttributes {
257 n_fruits: N_FRUITS,
258 n_droplets: N_DROPLETS,
259 n_tiny_droplets: N_TINY_DROPLETS,
260 ..Default::default()
261 },
262 difficulty: &Difficulty::new(),
263 acc: None,
264 combo: None,
265 fruits: Some(35),
266 droplets: Some(18),
267 tiny_droplets: Some(70),
268 tiny_droplet_misses: Some(10),
269 misses: Some(3),
270 };
271
272 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
273
274 assert_eq!(result.fruits, 37);
280 assert_eq!(result.droplets, 20);
281 assert_eq!(result.misses, 3);
282
283 assert_eq!(result.tiny_droplets, 70);
285 assert_eq!(result.tiny_droplet_misses, 10);
286 }
287
288 #[test]
289 fn no_misses() {
290 const N_FRUITS: u32 = 30;
291 const N_DROPLETS: u32 = 15;
292 const N_TINY_DROPLETS: u32 = 60;
293
294 let inspect = InspectCatchPerformance {
295 attrs: &CatchDifficultyAttributes {
296 n_fruits: N_FRUITS,
297 n_droplets: N_DROPLETS,
298 n_tiny_droplets: N_TINY_DROPLETS,
299 ..Default::default()
300 },
301 difficulty: &Difficulty::new(),
302 acc: None,
303 combo: None,
304 fruits: None,
305 droplets: None,
306 tiny_droplets: None,
307 tiny_droplet_misses: None,
308 misses: Some(0),
309 };
310
311 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
312
313 assert_eq!(result.fruits, N_FRUITS);
315 assert_eq!(result.droplets, N_DROPLETS);
316 assert_eq!(result.tiny_droplets, N_TINY_DROPLETS);
317 assert_eq!(result.tiny_droplet_misses, 0);
318 assert_eq!(result.misses, 0);
319 }
320
321 #[test]
322 fn excess_values_clamped() {
323 const N_FRUITS: u32 = 20;
324 const N_DROPLETS: u32 = 10;
325 const N_TINY_DROPLETS: u32 = 40;
326
327 let inspect = InspectCatchPerformance {
328 attrs: &CatchDifficultyAttributes {
329 n_fruits: N_FRUITS,
330 n_droplets: N_DROPLETS,
331 n_tiny_droplets: N_TINY_DROPLETS,
332 ..Default::default()
333 },
334 difficulty: &Difficulty::new(),
335 acc: None,
336 combo: None,
337 fruits: Some(100), droplets: Some(50),
339 tiny_droplets: Some(200),
340 tiny_droplet_misses: Some(100),
341 misses: Some(5),
342 };
343
344 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
345
346 assert_eq!(result.fruits, 20);
351 assert_eq!(result.droplets, 5);
352 assert_eq!(result.tiny_droplets, 40); assert_eq!(result.tiny_droplet_misses, 0); assert_eq!(result.misses, 5);
355 }
356
357 #[test]
358 fn missing_objects() {
359 const N_FRUITS: u32 = 728;
360 const N_DROPLETS: u32 = 2;
361 const N_TINY_DROPLETS: u32 = 263;
362
363 let inspect = InspectCatchPerformance {
364 attrs: &CatchDifficultyAttributes {
365 n_fruits: N_FRUITS,
366 n_droplets: N_DROPLETS,
367 n_tiny_droplets: N_TINY_DROPLETS,
368 ..Default::default()
369 },
370 difficulty: &Difficulty::new(),
371 acc: None,
372 combo: None,
373 fruits: Some(N_FRUITS - 10),
374 droplets: Some(N_DROPLETS - 1),
375 tiny_droplets: Some(N_TINY_DROPLETS - 50),
376 tiny_droplet_misses: Some(20),
377 misses: Some(2),
378 };
379
380 let result = <IgnoreAccuracy as HitResultGenerator<Catch>>::generate_hitresults(inspect);
381
382 assert_eq!(result.fruits, 726);
388 assert_eq!(result.droplets, 2);
389
390 assert_eq!(result.tiny_droplets, 243);
394 assert_eq!(result.tiny_droplet_misses, 20);
395 assert_eq!(result.misses, 2);
396 }
397}