refactor!: overhauled gradual calc for taiko

This commit is contained in:
MaxOhn
2023-11-07 22:20:15 +01:00
parent 79e19d5a9b
commit 74083e5ecb
11 changed files with 188 additions and 182 deletions
+1 -1
View File
@@ -228,7 +228,7 @@ impl<'map> GradualPerformanceAttributes<'map> {
o.nth(state.into(), n).map(PerformanceAttributes::Osu) o.nth(state.into(), n).map(PerformanceAttributes::Osu)
} }
GradualPerformanceAttributes::Taiko(t) => t GradualPerformanceAttributes::Taiko(t) => t
.process_next_n_objects(state.into(), n) .nth(state.into(), n)
.map(PerformanceAttributes::Taiko), .map(PerformanceAttributes::Taiko),
GradualPerformanceAttributes::Catch(f) => f GradualPerformanceAttributes::Catch(f) => f
.process_next_n_objects(state.into(), n) .process_next_n_objects(state.into(), n)
+1 -1
View File
@@ -151,7 +151,7 @@
//! }; //! };
//! //!
//! // Process the next 10 objects in one go //! // Process the next 10 objects in one go
//! let curr_performance = match gradual_performance.process_next_n_objects(state, 10) { //! let curr_performance = match gradual_performance.nth(state, 10) {
//! Some(perf) => perf, //! Some(perf) => perf,
//! None => panic!("the last `process_next_object` already processed the last object"), //! None => panic!("the last `process_next_object` already processed the last object"),
//! }; //! };
View File
+1 -1
View File
@@ -154,7 +154,7 @@ impl ColourDifficultyPreprocessor {
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>, mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>,
) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> { ) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
let mut hit_patterns = Vec::new(); let mut hit_patterns = Vec::new();
let mut curr_hit_pattern: Option<Rc<std::cell::RefCell<_>>> = None; let mut curr_hit_pattern: Option<Rc<RefCell<_>>> = None;
while !data.is_empty() { while !data.is_empty() {
let old = curr_hit_pattern.as_ref().map(Rc::downgrade); let old = curr_hit_pattern.as_ref().map(Rc::downgrade);
+7 -7
View File
@@ -62,21 +62,21 @@ impl RepeatingHitPatterns {
} }
pub(crate) fn find_repetition_interval(&mut self) { pub(crate) fn find_repetition_interval(&mut self) {
let mut other = match self.prev.as_ref().and_then(Weak::upgrade) { let Some(mut other) = self.prev.as_ref().and_then(Weak::upgrade) else {
Some(prev) => prev, return self.repetition_interval = Self::MAX_REPETITION_INTERVAL + 1;
None => return self.repetition_interval = Self::MAX_REPETITION_INTERVAL + 1,
}; };
let mut interval = 1; let mut interval = 1;
while interval < Self::MAX_REPETITION_INTERVAL { while interval < Self::MAX_REPETITION_INTERVAL {
if self.is_repetition_of(&other.borrow()) { if self.is_repetition_of(&other.borrow()) {
return self.repetition_interval = interval.min(Self::MAX_REPETITION_INTERVAL); self.repetition_interval = interval.min(Self::MAX_REPETITION_INTERVAL);
return;
} }
let next = match other.borrow().prev.as_ref().and_then(Weak::upgrade) { let Some(next) = other.borrow().prev.as_ref().and_then(Weak::upgrade) else {
Some(prev) => prev, break;
None => break,
}; };
// gotta love NLL... // gotta love NLL...
+1
View File
@@ -111,6 +111,7 @@ fn closest_rhythm(
.unwrap() .unwrap()
} }
// TODO: Remove Default impl and replace with `with_capacity` method for efficiency
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub(crate) struct ObjectLists { pub(crate) struct ObjectLists {
pub(crate) all: Vec<Rc<RefCell<TaikoDifficultyObject>>>, pub(crate) all: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
+41 -30
View File
@@ -1,3 +1,5 @@
#![cfg(feature = "gradual")]
use std::{borrow::Cow, cell::RefCell, rc::Rc, vec::IntoIter}; use std::{borrow::Cow, cell::RefCell, rc::Rc, vec::IntoIter};
use crate::{beatmap::BeatmapHitWindows, taiko::rescale, Beatmap, GameMode, Mods}; use crate::{beatmap::BeatmapHitWindows, taiko::rescale, Beatmap, GameMode, Mods};
@@ -40,15 +42,15 @@ use super::{
/// // ... /// // ...
/// } /// }
/// ``` /// ```
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct TaikoGradualDifficultyAttributes { pub struct TaikoGradualDifficultyAttributes {
pub(crate) idx: usize,
attrs: TaikoDifficultyAttributes, attrs: TaikoDifficultyAttributes,
hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject>>>, diff_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject>>>,
lists: ObjectLists, lists: ObjectLists,
peaks: Peaks, peaks: Peaks,
total_hits: usize, total_hits: usize,
is_convert: bool, is_convert: bool,
pub(crate) started: bool,
} }
impl TaikoGradualDifficultyAttributes { impl TaikoGradualDifficultyAttributes {
@@ -77,29 +79,27 @@ impl TaikoGradualDifficultyAttributes {
if map.hit_objects.len() < 2 { if map.hit_objects.len() < 2 {
return Self { return Self {
hit_objects: Vec::new().into_iter(), idx: 0,
diff_objects: Vec::new().into_iter(),
lists: ObjectLists::default(), lists: ObjectLists::default(),
peaks, peaks,
attrs, attrs,
total_hits: 0, total_hits: 0,
is_convert, is_convert,
started: false,
}; };
} }
attrs.max_combo += map.hit_objects[0].is_circle() as usize; attrs.max_combo += map.hit_objects[0].is_circle() as usize;
attrs.max_combo += map.hit_objects[1].is_circle() as usize; attrs.max_combo += map.hit_objects[1].is_circle() as usize;
let mut total_hits = attrs.max_combo; let mut total_hits = attrs.max_combo;
let mut diff_objects = ObjectLists::default();
let mut diff_objects = map map.taiko_objects()
.taiko_objects()
.skip(2) .skip(2)
.zip(map.hit_objects.iter().skip(1)) .zip(map.hit_objects.iter().skip(1))
.zip(map.hit_objects.iter()) .zip(map.hit_objects.iter())
.enumerate() .enumerate()
.fold( .for_each(|(idx, (((base, base_start_time), last), last_last))| {
ObjectLists::default(),
|mut lists, (idx, (((base, base_start_time), last), last_last))| {
total_hits += base.is_hit as usize; total_hits += base.is_hit as usize;
let diff_obj = TaikoDifficultyObject::new( let diff_obj = TaikoDifficultyObject::new(
@@ -108,36 +108,33 @@ impl TaikoGradualDifficultyAttributes {
last.start_time, last.start_time,
last_last.start_time, last_last.start_time,
clock_rate, clock_rate,
&lists, &diff_objects,
idx, idx,
); );
match &diff_obj.mono_idx { match &diff_obj.mono_idx {
MonoIndex::Centre(_) => lists.centres.push(idx), MonoIndex::Centre(_) => diff_objects.centres.push(idx),
MonoIndex::Rim(_) => lists.rims.push(idx), MonoIndex::Rim(_) => diff_objects.rims.push(idx),
MonoIndex::None => {} MonoIndex::None => {}
} }
if diff_obj.note_idx.is_some() { if diff_obj.note_idx.is_some() {
lists.notes.push(idx); diff_objects.notes.push(idx);
} }
lists.all.push(Rc::new(RefCell::new(diff_obj))); diff_objects.all.push(Rc::new(RefCell::new(diff_obj)));
});
lists
},
);
ColourDifficultyPreprocessor::process_and_assign(&mut diff_objects); ColourDifficultyPreprocessor::process_and_assign(&mut diff_objects);
Self { Self {
hit_objects: diff_objects.all.clone().into_iter(), idx: 0,
diff_objects: diff_objects.all.clone().into_iter(),
lists: diff_objects, lists: diff_objects,
peaks, peaks,
attrs, attrs,
total_hits, total_hits,
is_convert, is_convert,
started: false,
} }
} }
} }
@@ -146,10 +143,13 @@ impl Iterator for TaikoGradualDifficultyAttributes {
type Item = TaikoDifficultyAttributes; type Item = TaikoDifficultyAttributes;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.started = true; // The first difficulty object belongs to the third note since each difficulty
// object requires the current the last, and the second to last note. Hence, if we're still
// on the first or second object, we don't have a difficulty object yet and just skip
// processing.
if self.idx >= 2 {
loop { loop {
let curr = self.hit_objects.next()?; let curr = self.diff_objects.next()?;
let borrowed = curr.borrow(); let borrowed = curr.borrow();
self.peaks.process(&borrowed, &self.lists); self.peaks.process(&borrowed, &self.lists);
@@ -159,6 +159,11 @@ impl Iterator for TaikoGradualDifficultyAttributes {
break; break;
} }
} }
} else if self.lists.all.is_empty() {
return None;
}
self.idx += 1;
let PeaksDifficultyValues { let PeaksDifficultyValues {
mut colour_rating, mut colour_rating,
@@ -203,18 +208,24 @@ impl Iterator for TaikoGradualDifficultyAttributes {
} }
fn nth(&mut self, n: usize) -> Option<Self::Item> { fn nth(&mut self, n: usize) -> Option<Self::Item> {
let skip = n let mut take = n.min(self.len().saturating_sub(1));
.min(self.total_hits - self.attrs.max_combo)
.saturating_sub(1);
for _ in 0..skip { // The first two notes have no difficulty object
if self.idx < 2 && take > 0 {
let skipped = take.min(2);
take -= skipped;
self.idx += skipped;
}
for _ in 0..take {
loop { loop {
let curr = self.hit_objects.next()?; let curr = self.diff_objects.next()?;
let borrowed = curr.borrow(); let borrowed = curr.borrow();
self.peaks.process(&borrowed, &self.lists); self.peaks.process(&borrowed, &self.lists);
if borrowed.base.is_hit { if borrowed.base.is_hit {
self.attrs.max_combo += 1; self.attrs.max_combo += 1;
self.idx += 1;
break; break;
} }
@@ -228,6 +239,6 @@ impl Iterator for TaikoGradualDifficultyAttributes {
impl ExactSizeIterator for TaikoGradualDifficultyAttributes { impl ExactSizeIterator for TaikoGradualDifficultyAttributes {
#[inline] #[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
self.hit_objects.len() self.total_hits - self.idx
} }
} }
+28 -82
View File
@@ -1,66 +1,21 @@
use crate::{Beatmap, TaikoPP}; #![cfg(feature = "gradual")]
use crate::{taiko::TaikoScoreState, Beatmap, TaikoPP};
use super::{TaikoGradualDifficultyAttributes, TaikoPerformanceAttributes}; use super::{TaikoGradualDifficultyAttributes, TaikoPerformanceAttributes};
/// Aggregation for a score's current state i.e. what was the
/// maximum combo so far and what are the current hitresults.
///
/// This struct is used for [`TaikoGradualPerformanceAttributes`].
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TaikoScoreState {
/// Maximum combo that the score has had so far.
/// **Not** the maximum possible combo of the map so far.
pub max_combo: usize,
/// Amount of current 300s.
pub n300: usize,
/// Amount of current 100s.
pub n100: usize,
/// Amount of current misses.
pub n_misses: usize,
}
impl TaikoScoreState {
/// Create a new empty score state.
#[inline]
pub fn new() -> Self {
Self::default()
}
/// Return the total amount of hits by adding everything up.
#[inline]
pub fn total_hits(&self) -> usize {
self.n300 + self.n100 + self.n_misses
}
/// Calculate the accuracy between `0.0` and `1.0` for this state.
#[inline]
pub fn accuracy(&self) -> f64 {
let total_hits = self.total_hits();
if total_hits == 0 {
return 0.0;
}
let numerator = 2 * self.n300 + self.n100;
let denominator = 2 * total_hits;
numerator as f64 / denominator as f64
}
}
/// Gradually calculate the performance attributes of an osu!taiko map. /// Gradually calculate the performance attributes of an osu!taiko map.
/// ///
/// After each hit object you can call /// After each hit object you can call [`next`](`TaikoGradualPerformanceAttributes::next`)
/// [`process_next_object`](`TaikoGradualPerformanceAttributes::process_next_object`)
/// and it will return the resulting current [`TaikoPerformanceAttributes`]. /// and it will return the resulting current [`TaikoPerformanceAttributes`].
/// To process multiple objects at once, use /// To process multiple objects at once, use
/// [`process_next_n_objects`](`TaikoGradualPerformanceAttributes::process_next_n_objects`) instead. /// [`nth`](`TaikoGradualPerformanceAttributes::nth`) instead.
/// ///
/// Both methods require a [`TaikoScoreState`] that contains the current /// Both methods require a [`TaikoScoreState`] that contains the current
/// hitresults as well as the maximum combo so far. /// hitresults as well as the maximum combo so far.
/// ///
/// If you only want to calculate difficulty attributes use /// If you only want to calculate difficulty attributes use
/// [`TaikoGradualDifficultyAttributes`](crate::taiko::TaikoGradualDifficultyAttributes) instead. /// [`TaikoGradualDifficultyAttributes`] instead.
/// ///
/// # Example /// # Example
/// ///
@@ -82,10 +37,10 @@ impl TaikoScoreState {
/// state.max_combo += 1; /// state.max_combo += 1;
/// ///
/// # /* /// # /*
/// let performance = gradual_perf.process_next_object(state.clone()).unwrap(); /// let performance = gradual_perf.next(state.clone()).unwrap();
/// println!("PP: {}", performance.pp); /// println!("PP: {}", performance.pp);
/// # */ /// # */
/// # let _ = gradual_perf.process_next_object(state.clone()); /// # let _ = gradual_perf.next(state.clone());
/// } /// }
/// ///
/// // Then comes a miss. /// // Then comes a miss.
@@ -93,29 +48,30 @@ impl TaikoScoreState {
/// // the next few objects because the combo is reset. /// // the next few objects because the combo is reset.
/// state.n_misses += 1; /// state.n_misses += 1;
/// # /* /// # /*
/// let performance = gradual_perf.process_next_object(state.clone()).unwrap(); /// let performance = gradual_perf.next(state.clone()).unwrap();
/// println!("PP: {}", performance.pp); /// println!("PP: {}", performance.pp);
/// # */ /// # */
/// # let _ = gradual_perf.process_next_object(state.clone()); /// # let _ = gradual_perf.next(state.clone());
/// ///
/// // The next 10 objects will be a mixture of 300s and 100s. /// // The next 10 objects will be a mixture of 300s and 100s.
/// // Notice how all 10 objects will be processed in one go. /// // Notice how all 10 objects will be processed in one go.
/// state.n300 += 3; /// state.n300 += 3;
/// state.n100 += 7; /// state.n100 += 7;
/// // The `nth` method takes a zero-based value.
/// # /* /// # /*
/// let performance = gradual_perf.process_next_n_objects(state.clone(), 10).unwrap(); /// let performance = gradual_perf.nth(state.clone(), 9).unwrap();
/// println!("PP: {}", performance.pp); /// println!("PP: {}", performance.pp);
/// # */ /// # */
/// # let _ = gradual_perf.process_next_n_objects(state.clone(), 10); /// # let _ = gradual_perf.nth(state.clone(), 9);
/// ///
/// // Now comes another 300. Note that the max combo gets incremented again. /// // Now comes another 300. Note that the max combo gets incremented again.
/// state.n300 += 1; /// state.n300 += 1;
/// state.max_combo += 1; /// state.max_combo += 1;
/// # /* /// # /*
/// let performance = gradual_perf.process_next_object(state.clone()).unwrap(); /// let performance = gradual_perf.next(state.clone()).unwrap();
/// println!("PP: {}", performance.pp); /// println!("PP: {}", performance.pp);
/// # */ /// # */
/// # let _ = gradual_perf.process_next_object(state.clone()); /// # let _ = gradual_perf.next(state.clone());
/// ///
/// // Skip to the end /// // Skip to the end
/// # /* /// # /*
@@ -123,16 +79,16 @@ impl TaikoScoreState {
/// state.n300 = ... /// state.n300 = ...
/// state.n100 = ... /// state.n100 = ...
/// state.n_misses = ... /// state.n_misses = ...
/// let final_performance = gradual_perf.process_next_n_objects(state.clone(), usize::MAX).unwrap(); /// let final_performance = gradual_perf.nth(state.clone(), usize::MAX).unwrap();
/// println!("PP: {}", performance.pp); /// println!("PP: {}", performance.pp);
/// # */ /// # */
/// # let _ = gradual_perf.process_next_n_objects(state.clone(), usize::MAX); /// # let _ = gradual_perf.nth(state.clone(), usize::MAX);
/// ///
/// // Once the final performance was calculated, /// // Once the final performance was calculated,
/// // attempting to process further objects will return `None`. /// // attempting to process further objects will return `None`.
/// assert!(gradual_perf.process_next_object(state).is_none()); /// assert!(gradual_perf.next(state).is_none());
/// ``` /// ```
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct TaikoGradualPerformanceAttributes<'map> { pub struct TaikoGradualPerformanceAttributes<'map> {
difficulty: TaikoGradualDifficultyAttributes, difficulty: TaikoGradualDifficultyAttributes,
performance: TaikoPP<'map>, performance: TaikoPP<'map>,
@@ -152,34 +108,24 @@ impl<'map> TaikoGradualPerformanceAttributes<'map> {
/// Process the next hit object and calculate the /// Process the next hit object and calculate the
/// performance attributes for the resulting score. /// performance attributes for the resulting score.
pub fn process_next_object( pub fn next(&mut self, state: TaikoScoreState) -> Option<TaikoPerformanceAttributes> {
&mut self, self.nth(state, 0)
state: TaikoScoreState,
) -> Option<TaikoPerformanceAttributes> {
self.process_next_n_objects(state, 1)
} }
/// Same as [`process_next_object`](`TaikoGradualPerformanceAttributes::process_next_object`) /// Process everything up the the next `n`th hit object and calculate the performance
/// but instead of processing only one object it process `n` many. /// attributes for the resulting score state.
/// ///
/// If `n` is 0 it will be considered as 1. /// Note that the count is zero-indexed, so `n=0` will process 1 object, `n=1` will process 2,
/// If there are still objects to be processed but `n` is larger than the amount /// and so on.
/// of remaining objects, `n` will be considered as the amount of remaining objects. pub fn nth(&mut self, state: TaikoScoreState, n: usize) -> Option<TaikoPerformanceAttributes> {
pub fn process_next_n_objects( let difficulty = self.difficulty.nth(n)?;
&mut self,
state: TaikoScoreState,
n: usize,
) -> Option<TaikoPerformanceAttributes> {
let sub = 2 * !self.difficulty.started as usize;
let difficulty = self.difficulty.nth(n.saturating_sub(sub))?;
let passed_objects = difficulty.max_combo;
let performance = self let performance = self
.performance .performance
.clone() .clone()
.attributes(difficulty) .attributes(difficulty)
.state(state) .state(state)
.passed_objects(passed_objects) .passed_objects(self.difficulty.idx)
.calculate(); .calculate();
Some(performance) Some(performance)
+21 -18
View File
@@ -1,17 +1,24 @@
mod colours; mod colours;
mod difficulty_object; mod difficulty_object;
mod gradual_difficulty;
mod gradual_performance;
mod pp; mod pp;
mod rim; mod rim;
mod score_state;
mod skills; mod skills;
mod taiko_object; mod taiko_object;
#[cfg(feature = "gradual")]
mod gradual_difficulty;
#[cfg(feature = "gradual")]
mod gradual_performance;
use std::{borrow::Cow, cell::RefCell, rc::Rc}; use std::{borrow::Cow, cell::RefCell, rc::Rc};
pub use self::{pp::*, score_state::TaikoScoreState, taiko_object::TaikoObjectPub as TaikoObject};
#[cfg(feature = "gradual")]
pub use self::{ pub use self::{
gradual_difficulty::*, gradual_performance::*, pp::*, gradual_difficulty::TaikoGradualDifficultyAttributes,
taiko_object::TaikoObjectPub as TaikoObject, gradual_performance::TaikoGradualPerformanceAttributes,
}; };
pub(crate) use self::taiko_object::IntoTaikoObjectIter; pub(crate) use self::taiko_object::IntoTaikoObjectIter;
@@ -226,8 +233,9 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
let mut peaks = Peaks::new(); let mut peaks = Peaks::new();
let mut max_combo = 0; let mut max_combo = 0;
let mut diff_objects = map let mut diff_objects = ObjectLists::default();
.taiko_objects()
map.taiko_objects()
.take_while(|(h, _)| { .take_while(|(h, _)| {
if h.is_hit { if h.is_hit {
if take == 0 { if take == 0 {
@@ -244,34 +252,29 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
.zip(map.hit_objects.iter().skip(1)) .zip(map.hit_objects.iter().skip(1))
.zip(map.hit_objects.iter()) .zip(map.hit_objects.iter())
.enumerate() .enumerate()
.fold( .for_each(|(idx, (((base, base_start_time), last), last_last))| {
ObjectLists::default(),
|mut lists, (idx, (((base, base_start_time), last), last_last))| {
let diff_obj = TaikoDifficultyObject::new( let diff_obj = TaikoDifficultyObject::new(
base, base,
base_start_time, base_start_time,
last.start_time, last.start_time,
last_last.start_time, last_last.start_time,
clock_rate, clock_rate,
&lists, &diff_objects,
idx, idx,
); );
match &diff_obj.mono_idx { match &diff_obj.mono_idx {
MonoIndex::Centre(_) => lists.centres.push(idx), MonoIndex::Centre(_) => diff_objects.centres.push(idx),
MonoIndex::Rim(_) => lists.rims.push(idx), MonoIndex::Rim(_) => diff_objects.rims.push(idx),
MonoIndex::None => {} MonoIndex::None => {}
} }
if diff_obj.note_idx.is_some() { if diff_obj.note_idx.is_some() {
lists.notes.push(idx); diff_objects.notes.push(idx);
} }
lists.all.push(Rc::new(RefCell::new(diff_obj))); diff_objects.all.push(Rc::new(RefCell::new(diff_obj)));
});
lists
},
);
ColourDifficultyPreprocessor::process_and_assign(&mut diff_objects); ColourDifficultyPreprocessor::process_and_assign(&mut diff_objects);
+45
View File
@@ -0,0 +1,45 @@
/// Aggregation for a score's current state i.e. what was the
/// maximum combo so far and what are the current hitresults.
///
/// This struct is used for [`TaikoGradualPerformanceAttributes`](crate::taiko::TaikoGradualPerformanceAttributes).
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TaikoScoreState {
/// Maximum combo that the score has had so far.
/// **Not** the maximum possible combo of the map so far.
pub max_combo: usize,
/// Amount of current 300s.
pub n300: usize,
/// Amount of current 100s.
pub n100: usize,
/// Amount of current misses.
pub n_misses: usize,
}
impl TaikoScoreState {
/// Create a new empty score state.
#[inline]
pub fn new() -> Self {
Self::default()
}
/// Return the total amount of hits by adding everything up.
#[inline]
pub fn total_hits(&self) -> usize {
self.n300 + self.n100 + self.n_misses
}
/// Calculate the accuracy between `0.0` and `1.0` for this state.
#[inline]
pub fn accuracy(&self) -> f64 {
let total_hits = self.total_hits();
if total_hits == 0 {
return 0.0;
}
let numerator = 2 * self.n300 + self.n100;
let denominator = 2 * total_hits;
numerator as f64 / denominator as f64
}
}
+9 -9
View File
@@ -35,10 +35,10 @@ fn correct_empty() {
let mut gradual = TaikoGradualPerformanceAttributes::new(&map, 0); let mut gradual = TaikoGradualPerformanceAttributes::new(&map, 0);
let state = TaikoScoreState::default(); let state = TaikoScoreState::default();
let first_attrs = gradual.process_next_n_objects(state.clone(), usize::MAX); let first_attrs = gradual.nth(state.clone(), usize::MAX);
assert!(first_attrs.is_some()); assert!(first_attrs.is_some());
assert!(gradual.process_next_object(state).is_none()); assert!(gradual.next(state).is_none());
} }
#[test] #[test]
@@ -50,14 +50,14 @@ fn next_and_next_n() {
let mut gradual2 = TaikoGradualPerformanceAttributes::new(&map, 0); let mut gradual2 = TaikoGradualPerformanceAttributes::new(&map, 0);
for _ in 0..50 { for _ in 0..50 {
let _ = gradual1.process_next_object(state.clone()); let _ = gradual1.next(state.clone());
let _ = gradual2.process_next_object(state.clone()); let _ = gradual2.next(state.clone());
} }
let n = 200; let n = 200;
for _ in 1..n { for _ in 1..n {
let _ = gradual1.process_next_object(state.clone()); let _ = gradual1.next(state.clone());
} }
let state = TaikoScoreState { let state = TaikoScoreState {
@@ -67,8 +67,8 @@ fn next_and_next_n() {
n_misses: 6, n_misses: 6,
}; };
let next = gradual1.process_next_object(state.clone()); let next = gradual1.next(state.clone());
let next_n = gradual2.process_next_n_objects(state, n); let next_n = gradual2.nth(state, n - 1);
assert_eq!(next_n, next); assert_eq!(next_n, next);
} }
@@ -86,7 +86,7 @@ fn gradual_end_eq_regular() {
n_misses: 0, n_misses: 0,
}; };
let gradual_end = gradual.process_next_n_objects(state, usize::MAX).unwrap(); let gradual_end = gradual.nth(state, usize::MAX).unwrap();
assert_eq!(regular, gradual_end); assert_eq!(regular, gradual_end);
} }
@@ -106,7 +106,7 @@ fn gradual_eq_regular_passed() {
n_misses: 0, n_misses: 0,
}; };
let gradual = gradual.process_next_n_objects(state, n).unwrap(); let gradual = gradual.nth(state, n - 1).unwrap();
assert_eq!(regular, gradual); assert_eq!(regular, gradual);
} }