use crate::{
Beatmap, Difficulty,
any::CalculateError,
catch::{CatchGradualDifficulty, CatchPerformanceAttributes, CatchScoreState},
model::mode::ConvertError,
};
pub struct CatchGradualPerformance {
difficulty: CatchGradualDifficulty,
}
impl CatchGradualPerformance {
pub fn new(difficulty: Difficulty, map: &Beatmap) -> Result<Self, ConvertError> {
let difficulty = CatchGradualDifficulty::new(difficulty, map)?;
Ok(Self { difficulty })
}
pub fn checked_new(difficulty: Difficulty, map: &Beatmap) -> Result<Self, CalculateError> {
let difficulty = CatchGradualDifficulty::checked_new(difficulty, map)?;
Ok(Self { difficulty })
}
pub fn next(&mut self, state: CatchScoreState) -> Option<CatchPerformanceAttributes> {
self.nth(state, 0)
}
pub fn last(&mut self, state: CatchScoreState) -> Option<CatchPerformanceAttributes> {
self.nth(state, usize::MAX)
}
#[expect(clippy::missing_panics_doc, reason = "unreachable")]
pub fn nth(&mut self, state: CatchScoreState, n: usize) -> Option<CatchPerformanceAttributes> {
let performance = self
.difficulty
.nth(n)?
.performance()
.state(state)
.difficulty(self.difficulty.difficulty.clone())
.passed_objects(self.difficulty.idx as u32)
.calculate()
.expect("no conversion required");
Some(performance)
}
#[expect(clippy::len_without_is_empty, reason = "TODO")]
pub fn len(&self) -> usize {
self.difficulty.len()
}
}
#[cfg(test)]
mod tests {
use crate::{Beatmap, catch::CatchPerformance};
use super::*;
#[test]
fn next_and_nth() {
let map = Beatmap::from_path("./resources/2118524.osu").unwrap();
let difficulty = Difficulty::new().mods(88);
let mut gradual = CatchGradualPerformance::new(difficulty.clone(), &map).unwrap();
let mut gradual_2nd = CatchGradualPerformance::new(difficulty.clone(), &map).unwrap();
let mut gradual_3rd = CatchGradualPerformance::new(difficulty.clone(), &map).unwrap();
let mut state = CatchScoreState::default();
for i in 1.. {
state.hitresults.misses += 1;
let Some(next_gradual) = gradual.next(state.clone()) else {
assert_eq!(i, 731);
assert!(gradual_2nd.last(state.clone()).is_none()); assert!(gradual_3rd.last(state.clone()).is_some()); break;
};
if i % 2 == 0 {
let next_gradual_2nd = gradual_2nd.nth(state.clone(), 1).unwrap();
assert_eq!(next_gradual, next_gradual_2nd);
}
if i % 3 == 0 {
let next_gradual_3rd = gradual_3rd.nth(state.clone(), 2).unwrap();
assert_eq!(next_gradual, next_gradual_3rd);
}
let regular_calc = CatchPerformance::new(&map)
.difficulty(difficulty.clone())
.passed_objects(i as u32)
.state(state.clone());
let expected = regular_calc.calculate().unwrap();
assert_eq!(next_gradual, expected);
}
}
}