refactor!: overhauled gradual calc for any mode
This commit is contained in:
+46
-65
@@ -1,3 +1,5 @@
|
|||||||
|
#![cfg(feature = "gradual")]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
catch::{CatchGradualDifficultyAttributes, CatchGradualPerformanceAttributes},
|
catch::{CatchGradualDifficultyAttributes, CatchGradualPerformanceAttributes},
|
||||||
mania::{ManiaGradualDifficultyAttributes, ManiaGradualPerformanceAttributes},
|
mania::{ManiaGradualDifficultyAttributes, ManiaGradualPerformanceAttributes},
|
||||||
@@ -8,12 +10,11 @@ use crate::{
|
|||||||
|
|
||||||
/// Gradually calculate the difficulty attributes on maps of any mode.
|
/// Gradually calculate the difficulty attributes on maps of any mode.
|
||||||
///
|
///
|
||||||
/// Note that this struct implements [`Iterator`](std::iter::Iterator).
|
/// Note that this struct implements [`Iterator`].
|
||||||
/// On every call of [`Iterator::next`](std::iter::Iterator::next), the map's next hit object will
|
/// On every call of [`Iterator::next`](Iterator::next), the map's next hit object will
|
||||||
/// be processed and the [`DifficultyAttributes`] will be updated and returned.
|
/// be processed and the [`DifficultyAttributes`] will be updated and returned.
|
||||||
///
|
///
|
||||||
/// If you want to calculate performance attributes, use
|
/// If you want to calculate performance attributes, use [`GradualPerformanceAttributes`] instead.
|
||||||
/// [`GradualPerformanceAttributes`](crate::GradualPerformanceAttributes) instead.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -49,6 +50,7 @@ pub enum GradualDifficultyAttributes<'map> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'map> GradualDifficultyAttributes<'map> {
|
impl<'map> GradualDifficultyAttributes<'map> {
|
||||||
|
// FIXME: converted catch maps will always count as osu!std since their mode is not modified
|
||||||
/// Create a new gradual difficulty calculator for maps of any mode.
|
/// Create a new gradual difficulty calculator for maps of any mode.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||||
@@ -87,11 +89,10 @@ impl Iterator for GradualDifficultyAttributes<'_> {
|
|||||||
|
|
||||||
/// Gradually calculate the performance attributes on maps of any mode.
|
/// Gradually calculate the performance attributes on maps of any mode.
|
||||||
///
|
///
|
||||||
/// After each hit object you can call
|
/// After each hit object you can call [`next`](`GradualPerformanceAttributes::next`)
|
||||||
/// [`process_next_object`](`GradualPerformanceAttributes::process_next_object`)
|
|
||||||
/// and it will return the resulting current [`PerformanceAttributes`].
|
/// and it will return the resulting current [`PerformanceAttributes`].
|
||||||
/// To process multiple objects at once, use
|
/// To process multiple objects at once, use
|
||||||
/// [`process_next_n_objects`](`GradualPerformanceAttributes::process_next_n_objects`) instead.
|
/// [`nth`](`GradualPerformanceAttributes::nth`) instead.
|
||||||
///
|
///
|
||||||
/// Both methods require a [`ScoreState`] that contains the current hitresults
|
/// Both methods require a [`ScoreState`] that contains the current hitresults
|
||||||
/// as well as the maximum combo so far or just the current score for osu!mania.
|
/// as well as the maximum combo so far or just the current score for osu!mania.
|
||||||
@@ -100,13 +101,10 @@ impl Iterator for GradualDifficultyAttributes<'_> {
|
|||||||
///
|
///
|
||||||
/// Alternatively, you can match on the map's mode yourself and use the gradual
|
/// Alternatively, you can match on the map's mode yourself and use the gradual
|
||||||
/// performance attribute struct for the corresponding mode, i.e.
|
/// performance attribute struct for the corresponding mode, i.e.
|
||||||
/// [`OsuGradualPerformanceAttributes`],
|
/// [`OsuGradualPerformanceAttributes`], [`TaikoGradualPerformanceAttributes`],
|
||||||
/// [`TaikoGradualPerformanceAttributes`],
|
/// [`CatchGradualPerformanceAttributes`], or [`ManiaGradualPerformanceAttributes`].
|
||||||
/// [`CatchGradualPerformanceAttributes`], or
|
|
||||||
/// [`ManiaGradualPerformanceAttributes`].
|
|
||||||
///
|
///
|
||||||
/// If you only want to calculate difficulty attributes use
|
/// If you only want to calculate difficulty attributes use [`GradualDifficultyAttributes`] instead.
|
||||||
/// [`GradualDifficultyAttributes`](crate::GradualDifficultyAttributes) instead.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -122,62 +120,52 @@ impl Iterator for GradualDifficultyAttributes<'_> {
|
|||||||
/// let mut gradual_perf = GradualPerformanceAttributes::new(&map, mods);
|
/// let mut gradual_perf = GradualPerformanceAttributes::new(&map, mods);
|
||||||
/// let mut state = ScoreState::new(); // empty state, everything is on 0.
|
/// let mut state = ScoreState::new(); // empty state, everything is on 0.
|
||||||
///
|
///
|
||||||
/// // The first 10 hitresults are 300s and increase the score by 123 each.
|
/// // The first 10 hitresults are 300s
|
||||||
/// for _ in 0..10 {
|
/// for _ in 0..10 {
|
||||||
/// state.n300 += 1;
|
/// state.n300 += 1;
|
||||||
/// state.max_combo += 1;
|
/// state.max_combo += 1;
|
||||||
///
|
///
|
||||||
/// # /*
|
/// let performance = gradual_perf.next(state.clone()).unwrap();
|
||||||
/// let performance = gradual_perf.process_next_object(state.clone()).unwrap();
|
/// println!("PP: {}", performance.pp());
|
||||||
/// println!("PP: {}", performance.pp);
|
|
||||||
/// # */
|
|
||||||
/// # let _ = gradual_perf.process_next_object(state.clone());
|
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // Then comes a miss.
|
/// // Then comes a miss.
|
||||||
/// // Note that state's max combo won't be incremented for
|
/// // Note that state's max combo won't be incremented for
|
||||||
/// // 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());
|
|
||||||
///
|
///
|
||||||
/// // The next 10 objects will be a mixture of 300s, 100s, and 50s.
|
/// // The next 10 objects will be a mixture of 300s, 100s, and 50s.
|
||||||
/// // Notice how all 10 objects will be processed in one go.
|
/// // Notice how all 10 objects will be processed in one go.
|
||||||
/// state.n300 += 2;
|
/// state.n300 += 2;
|
||||||
/// state.n100 += 7;
|
/// state.n100 += 7;
|
||||||
/// state.n50 += 1;
|
/// state.n50 += 1;
|
||||||
/// // Don't forget state.n_katu
|
///
|
||||||
/// # /*
|
/// // 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);
|
|
||||||
///
|
///
|
||||||
/// // 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());
|
|
||||||
///
|
///
|
||||||
/// // Skip to the end
|
/// // Skip to the end
|
||||||
/// # /*
|
/// # /*
|
||||||
/// state.max_combo = ...
|
/// state.max_combo = ...
|
||||||
/// state.n300 = ...
|
/// state.n300 = ...
|
||||||
/// ...
|
/// ...
|
||||||
/// let final_performance = gradual_perf.process_next_n_objects(state.clone(), usize::MAX).unwrap();
|
|
||||||
/// println!("PP: {}", performance.pp);
|
|
||||||
/// # */
|
/// # */
|
||||||
/// # let _ = gradual_perf.process_next_n_objects(state.clone(), usize::MAX);
|
/// let final_performance = gradual_perf.last(state.clone()).unwrap();
|
||||||
|
/// println!("PP: {}", performance.pp());
|
||||||
///
|
///
|
||||||
/// // 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(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
@@ -193,6 +181,7 @@ pub enum GradualPerformanceAttributes<'map> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'map> GradualPerformanceAttributes<'map> {
|
impl<'map> GradualPerformanceAttributes<'map> {
|
||||||
|
// FIXME: converted catch maps will always count as osu!std since their mode is not modified
|
||||||
/// Create a new gradual performance calculator for maps of any mode.
|
/// Create a new gradual performance calculator for maps of any mode.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||||
@@ -207,35 +196,27 @@ impl<'map> GradualPerformanceAttributes<'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.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn process_next_object(&mut self, state: ScoreState) -> Option<PerformanceAttributes> {
|
pub fn next(&mut self, state: ScoreState) -> Option<PerformanceAttributes> {
|
||||||
self.process_next_n_objects(state, 1)
|
self.nth(state, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [`process_next_object`](`GradualPerformanceAttributes::process_next_object`)
|
/// Process all remaining hit objects and calculate the final performance attributes.
|
||||||
/// but instead of processing only one object it process `n` many.
|
pub fn last(&mut self, state: ScoreState) -> Option<PerformanceAttributes> {
|
||||||
///
|
self.nth(state, usize::MAX)
|
||||||
/// If `n` is 0 it will be considered as 1.
|
|
||||||
/// If there are still objects to be processed but `n` is larger than the amount
|
|
||||||
/// of remaining objects, `n` will be considered as the amount of remaining objects.
|
|
||||||
#[inline]
|
|
||||||
pub fn process_next_n_objects(
|
|
||||||
&mut self,
|
|
||||||
state: ScoreState,
|
|
||||||
n: usize,
|
|
||||||
) -> Option<PerformanceAttributes> {
|
|
||||||
match self {
|
|
||||||
GradualPerformanceAttributes::Osu(o) => {
|
|
||||||
o.nth(state.into(), n).map(PerformanceAttributes::Osu)
|
|
||||||
}
|
}
|
||||||
GradualPerformanceAttributes::Taiko(t) => t
|
|
||||||
.nth(state.into(), n)
|
/// Process everything up the the next `n`th hit object and calculate the performance
|
||||||
.map(PerformanceAttributes::Taiko),
|
/// attributes for the resulting score state.
|
||||||
GradualPerformanceAttributes::Catch(f) => f
|
///
|
||||||
.nth(state.into(), n)
|
/// Note that the count is zero-indexed, so `n=0` will process 1 object, `n=1` will process 2,
|
||||||
.map(PerformanceAttributes::Catch),
|
/// and so on.
|
||||||
GradualPerformanceAttributes::Mania(m) => m
|
#[inline]
|
||||||
.nth(state.into(), n)
|
pub fn nth(&mut self, state: ScoreState, n: usize) -> Option<PerformanceAttributes> {
|
||||||
.map(PerformanceAttributes::Mania),
|
match self {
|
||||||
|
GradualPerformanceAttributes::Osu(o) => o.nth(state.into(), n).map(From::from),
|
||||||
|
GradualPerformanceAttributes::Taiko(t) => t.nth(state.into(), n).map(From::from),
|
||||||
|
GradualPerformanceAttributes::Catch(f) => f.nth(state.into(), n).map(From::from),
|
||||||
|
GradualPerformanceAttributes::Mania(m) => m.nth(state.into(), n).map(From::from),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user