feat: added owned gradual performance calcs
This commit is contained in:
@@ -79,11 +79,11 @@ impl Iterator for CatchGradualDifficulty<'_> {
|
|||||||
/// Gradually calculate the difficulty attributes of an osu!catch map.
|
/// Gradually calculate the difficulty attributes of an osu!catch map.
|
||||||
///
|
///
|
||||||
/// Check [`CatchGradualDifficulty`] for more information. This struct does the same
|
/// Check [`CatchGradualDifficulty`] for more information. This struct does the same
|
||||||
/// but includes an up-front allocation to avoid being bound to a lifetime.
|
/// but takes ownership of [`Beatmap`] to avoid being bound to a lifetime.
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CatchOwnedGradualDifficulty {
|
pub struct CatchOwnedGradualDifficulty {
|
||||||
map: Box<Beatmap>,
|
pub(crate) map: Beatmap,
|
||||||
inner: CatchGradualDifficultyInner,
|
inner: CatchGradualDifficultyInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,6 @@ impl CatchOwnedGradualDifficulty {
|
|||||||
/// Create a new difficulty attributes iterator for osu!catch maps.
|
/// Create a new difficulty attributes iterator for osu!catch maps.
|
||||||
pub fn new(map: Beatmap, mods: u32) -> Self {
|
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||||
let inner = CatchGradualDifficultyInner::new(&map, mods);
|
let inner = CatchGradualDifficultyInner::new(&map, mods);
|
||||||
let map = Box::new(map);
|
|
||||||
|
|
||||||
Self { map, inner }
|
Self { map, inner }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use crate::{Beatmap, CatchPP};
|
use crate::{Beatmap, CatchPP};
|
||||||
|
|
||||||
use super::{CatchGradualDifficulty, CatchPerformanceAttributes, CatchScoreState};
|
use super::{
|
||||||
|
CatchGradualDifficulty, CatchOwnedGradualDifficulty, CatchPerformanceAttributes,
|
||||||
|
CatchScoreState,
|
||||||
|
};
|
||||||
|
|
||||||
/// Gradually calculate the performance attributes of an osu!catch map.
|
/// Gradually calculate the performance attributes of an osu!catch map.
|
||||||
///
|
///
|
||||||
@@ -144,3 +147,55 @@ impl<'map> CatchGradualPerformance<'map> {
|
|||||||
Some(performance)
|
Some(performance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gradually calculate the performance attributes of an osu!catch map.
|
||||||
|
///
|
||||||
|
/// Check [`CatchGradualPerformance`] for more information. This struct does the same
|
||||||
|
/// but takes ownership of [`Beatmap`] to avoid being bound to a lifetime.
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CatchOwnedGradualPerformance {
|
||||||
|
difficulty: CatchOwnedGradualDifficulty,
|
||||||
|
mods: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CatchOwnedGradualPerformance {
|
||||||
|
/// Create a new gradual performance calculator for osu!standard maps.
|
||||||
|
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||||
|
let difficulty = CatchOwnedGradualDifficulty::new(map, mods);
|
||||||
|
|
||||||
|
Self { difficulty, mods }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process the next hit object and calculate the
|
||||||
|
/// performance attributes for the resulting score state.
|
||||||
|
///
|
||||||
|
/// Note that neither hits nor misses of tiny droplets require
|
||||||
|
/// to be processed. Only fruits and droplets do.
|
||||||
|
pub fn next(&mut self, state: CatchScoreState) -> Option<CatchPerformanceAttributes> {
|
||||||
|
self.nth(state, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process all remaining hit objects and calculate the final performance attributes.
|
||||||
|
pub fn last(&mut self, state: CatchScoreState) -> Option<CatchPerformanceAttributes> {
|
||||||
|
self.nth(state, usize::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process everything up the the next `n`th hit object and calculate the performance
|
||||||
|
/// attributes for the resulting score state.
|
||||||
|
///
|
||||||
|
/// Note that the count is zero-indexed, so `n=0` will process 1 object, `n=1` will process 2,
|
||||||
|
/// and so on.
|
||||||
|
pub fn nth(&mut self, state: CatchScoreState, n: usize) -> Option<CatchPerformanceAttributes> {
|
||||||
|
let difficulty = self.difficulty.by_ref().take(n.saturating_add(1)).last()?;
|
||||||
|
|
||||||
|
let performance = CatchPP::new(&self.difficulty.map)
|
||||||
|
.mods(self.mods)
|
||||||
|
.attributes(difficulty)
|
||||||
|
.state(state)
|
||||||
|
.passed_objects(self.difficulty.idx())
|
||||||
|
.calculate();
|
||||||
|
|
||||||
|
Some(performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ pub use self::{catch_object::CatchObject, pp::*, score_state::CatchScoreState};
|
|||||||
#[cfg(feature = "gradual")]
|
#[cfg(feature = "gradual")]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
gradual_difficulty::{CatchGradualDifficulty, CatchOwnedGradualDifficulty},
|
gradual_difficulty::{CatchGradualDifficulty, CatchOwnedGradualDifficulty},
|
||||||
gradual_performance::CatchGradualPerformance,
|
gradual_performance::{CatchGradualPerformance, CatchOwnedGradualPerformance},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::fruit_or_juice::{FruitOrJuice, FruitParams};
|
pub(crate) use self::fruit_or_juice::{FruitOrJuice, FruitParams};
|
||||||
|
|||||||
@@ -97,27 +97,29 @@ impl ExactSizeIterator for ManiaGradualDifficulty<'_> {
|
|||||||
/// Gradually calculate the difficulty attributes of an osu!mania map.
|
/// Gradually calculate the difficulty attributes of an osu!mania map.
|
||||||
///
|
///
|
||||||
/// Check [`ManiaGradualDifficulty`] for more information. This struct does the same
|
/// Check [`ManiaGradualDifficulty`] for more information. This struct does the same
|
||||||
/// but includes an up-front clone to avoid being bound to a lifetime.
|
/// but takes ownership of [`Beatmap`] to avoid being bound to a lifetime.
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ManiaOwnedGradualDifficulty {
|
pub struct ManiaOwnedGradualDifficulty {
|
||||||
hit_objects: Vec<HitObject>,
|
// Technically only `Beatmap::hit_objects` are required here but storing
|
||||||
|
// the full map lets us get away with not storing the map in `ManiaOwnedGradualPerformance`.
|
||||||
|
pub(crate) map: Beatmap,
|
||||||
inner: ManiaGradualDifficultyInner,
|
inner: ManiaGradualDifficultyInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManiaOwnedGradualDifficulty {
|
impl ManiaOwnedGradualDifficulty {
|
||||||
/// Create a new owned difficulty attributes iterator for osu!mania maps.
|
/// Create a new owned difficulty attributes iterator for osu!mania maps.
|
||||||
pub fn new(map: &Beatmap, mods: u32) -> Self {
|
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||||
let map = map.convert_mode(GameMode::Mania);
|
let converted_map = map.convert_mode(GameMode::Mania);
|
||||||
let is_convert = matches!(map, Cow::Owned(_));
|
let is_convert = matches!(converted_map, Cow::Owned(_));
|
||||||
let inner = ManiaGradualDifficultyInner::new(&map, is_convert, mods);
|
let inner = ManiaGradualDifficultyInner::new(&converted_map, is_convert, mods);
|
||||||
|
|
||||||
let hit_objects = match map {
|
let map = match converted_map {
|
||||||
Cow::Owned(map) => map.hit_objects,
|
Cow::Owned(map) => map,
|
||||||
Cow::Borrowed(map) => map.hit_objects.clone(),
|
Cow::Borrowed(_) => map,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { hit_objects, inner }
|
Self { map, inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -131,7 +133,7 @@ impl Iterator for ManiaOwnedGradualDifficulty {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.inner.next(&self.hit_objects)
|
self.inner.next(&self.map.hit_objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -141,7 +143,7 @@ impl Iterator for ManiaOwnedGradualDifficulty {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
self.inner.nth(n, &self.hit_objects)
|
self.inner.nth(n, &self.map.hit_objects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
use crate::{Beatmap, ManiaPP};
|
use crate::{Beatmap, ManiaPP};
|
||||||
|
|
||||||
use super::{ManiaGradualDifficulty, ManiaPerformanceAttributes, ManiaScoreState};
|
use super::{
|
||||||
|
ManiaGradualDifficulty, ManiaOwnedGradualDifficulty, ManiaPerformanceAttributes,
|
||||||
|
ManiaScoreState,
|
||||||
|
};
|
||||||
|
|
||||||
/// Gradually calculate the performance attributes of an osu!mania map.
|
/// Gradually calculate the performance attributes of an osu!mania map.
|
||||||
///
|
///
|
||||||
@@ -124,3 +127,52 @@ impl<'map> ManiaGradualPerformance<'map> {
|
|||||||
Some(performance)
|
Some(performance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gradually calculate the performance attributes of an osu!mania map.
|
||||||
|
///
|
||||||
|
/// Check [`ManiaGradualPerformance`] for more information. This struct does the same
|
||||||
|
/// but takes ownership of [`Beatmap`] to avoid being bound to a lifetime.
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ManiaOwnedGradualPerformance {
|
||||||
|
difficulty: ManiaOwnedGradualDifficulty,
|
||||||
|
mods: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManiaOwnedGradualPerformance {
|
||||||
|
/// Create a new gradual performance calculator for osu!mania maps.
|
||||||
|
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||||
|
let difficulty = ManiaOwnedGradualDifficulty::new(map, mods);
|
||||||
|
|
||||||
|
Self { difficulty, mods }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process the next hit object and calculate the
|
||||||
|
/// performance attributes for the resulting score.
|
||||||
|
pub fn next(&mut self, state: ManiaScoreState) -> Option<ManiaPerformanceAttributes> {
|
||||||
|
self.nth(state, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process all remaining hit objects and calculate the final performance attributes.
|
||||||
|
pub fn last(&mut self, state: ManiaScoreState) -> Option<ManiaPerformanceAttributes> {
|
||||||
|
self.nth(state, usize::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process everything up the the next `n`th hit object and calculate the performance
|
||||||
|
/// attributes for the resulting score state.
|
||||||
|
///
|
||||||
|
/// Note that the count is zero-indexed, so `n=0` will process 1 object, `n=1` will process 2,
|
||||||
|
/// and so on.
|
||||||
|
pub fn nth(&mut self, state: ManiaScoreState, n: usize) -> Option<ManiaPerformanceAttributes> {
|
||||||
|
let difficulty = self.difficulty.nth(n)?;
|
||||||
|
|
||||||
|
let performance = ManiaPP::new(&self.difficulty.map)
|
||||||
|
.mods(self.mods)
|
||||||
|
.attributes(difficulty)
|
||||||
|
.state(state)
|
||||||
|
.passed_objects(self.difficulty.idx())
|
||||||
|
.calculate();
|
||||||
|
|
||||||
|
Some(performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ pub use self::{mania_object::ManiaObject, pp::*, score_state::ManiaScoreState};
|
|||||||
#[cfg(feature = "gradual")]
|
#[cfg(feature = "gradual")]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
gradual_difficulty::{ManiaGradualDifficulty, ManiaOwnedGradualDifficulty},
|
gradual_difficulty::{ManiaGradualDifficulty, ManiaOwnedGradualDifficulty},
|
||||||
gradual_performance::ManiaGradualPerformance,
|
gradual_performance::{ManiaGradualPerformance, ManiaOwnedGradualPerformance},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::mania_object::ObjectParameters;
|
pub(crate) use self::mania_object::ObjectParameters;
|
||||||
|
|||||||
@@ -138,3 +138,57 @@ impl<'map> OsuGradualPerformance<'map> {
|
|||||||
Some(performance)
|
Some(performance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gradually calculate the performance attributes of an osu!standard map.
|
||||||
|
///
|
||||||
|
/// Check [`OsuGradualPerformance`] for more information. This struct does the same
|
||||||
|
/// but takes ownership of [`Beatmap`] to avoid being bound to a lifetime.
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OsuOwnedGradualPerformance {
|
||||||
|
difficulty: OsuGradualDifficulty,
|
||||||
|
map: Beatmap,
|
||||||
|
mods: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OsuOwnedGradualPerformance {
|
||||||
|
/// Create a new gradual performance calculator for osu!standard maps.
|
||||||
|
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||||
|
let difficulty = OsuGradualDifficulty::new(&map, mods);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
difficulty,
|
||||||
|
map,
|
||||||
|
mods,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process the next hit object and calculate the
|
||||||
|
/// performance attributes for the resulting score state.
|
||||||
|
pub fn next(&mut self, state: OsuScoreState) -> Option<OsuPerformanceAttributes> {
|
||||||
|
self.nth(state, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process all remaining hit objects and calculate the final performance attributes.
|
||||||
|
pub fn last(&mut self, state: OsuScoreState) -> Option<OsuPerformanceAttributes> {
|
||||||
|
self.nth(state, usize::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process everything up the the next `n`th hit object and calculate the performance
|
||||||
|
/// attributes for the resulting score state.
|
||||||
|
///
|
||||||
|
/// Note that the count is zero-indexed, so `n=0` will process 1 object, `n=1` will process 2,
|
||||||
|
/// and so on.
|
||||||
|
pub fn nth(&mut self, state: OsuScoreState, n: usize) -> Option<OsuPerformanceAttributes> {
|
||||||
|
let difficulty = self.difficulty.nth(n)?;
|
||||||
|
|
||||||
|
let performance = OsuPP::new(&self.map)
|
||||||
|
.mods(self.mods)
|
||||||
|
.attributes(difficulty)
|
||||||
|
.state(state)
|
||||||
|
.passed_objects(self.difficulty.idx)
|
||||||
|
.calculate();
|
||||||
|
|
||||||
|
Some(performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -22,7 +22,8 @@ pub use self::{osu_object::*, pp::*, score_state::OsuScoreState};
|
|||||||
|
|
||||||
#[cfg(feature = "gradual")]
|
#[cfg(feature = "gradual")]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
gradual_difficulty::OsuGradualDifficulty, gradual_performance::OsuGradualPerformance,
|
gradual_difficulty::OsuGradualDifficulty,
|
||||||
|
gradual_performance::{OsuGradualPerformance, OsuOwnedGradualPerformance},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::scaling_factor::ScalingFactor;
|
pub(crate) use self::scaling_factor::ScalingFactor;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![cfg(feature = "gradual")]
|
#![cfg(feature = "gradual")]
|
||||||
|
|
||||||
use crate::{taiko::TaikoScoreState, Beatmap, TaikoPP};
|
use std::borrow::Cow;
|
||||||
|
use crate::{taiko::TaikoScoreState, Beatmap, TaikoPP, GameMode};
|
||||||
|
|
||||||
use super::{TaikoGradualDifficulty, TaikoPerformanceAttributes};
|
use super::{TaikoGradualDifficulty, TaikoPerformanceAttributes};
|
||||||
|
|
||||||
@@ -136,3 +137,62 @@ impl<'map> TaikoGradualPerformance<'map> {
|
|||||||
Some(performance)
|
Some(performance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gradually calculate the performance attributes of an osu!taiko map.
|
||||||
|
///
|
||||||
|
/// Check [`TaikoGradualPerformance`] for more information. This struct does the same
|
||||||
|
/// but takes ownership of [`Beatmap`] to avoid being bound to a lifetime.
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TaikoOwnedGradualPerformance {
|
||||||
|
difficulty: TaikoGradualDifficulty,
|
||||||
|
map: Beatmap,
|
||||||
|
mods: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaikoOwnedGradualPerformance {
|
||||||
|
/// Create a new gradual performance calculator for osu!taiko maps.
|
||||||
|
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||||
|
let map = match map.convert_mode(GameMode::Taiko) {
|
||||||
|
Cow::Owned(map) => map,
|
||||||
|
Cow::Borrowed(_) => map,
|
||||||
|
};
|
||||||
|
|
||||||
|
let difficulty = TaikoGradualDifficulty::new(&map, mods);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
difficulty,
|
||||||
|
map,
|
||||||
|
mods,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process the next hit object and calculate the
|
||||||
|
/// performance attributes for the resulting score.
|
||||||
|
pub fn next(&mut self, state: TaikoScoreState) -> Option<TaikoPerformanceAttributes> {
|
||||||
|
self.nth(state, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process all remaining hit objects and calculate the final performance attributes.
|
||||||
|
pub fn last(&mut self, state: TaikoScoreState) -> Option<TaikoPerformanceAttributes> {
|
||||||
|
self.nth(state, usize::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process everything up the the next `n`th hit object and calculate the performance
|
||||||
|
/// attributes for the resulting score state.
|
||||||
|
///
|
||||||
|
/// Note that the count is zero-indexed, so `n=0` will process 1 object, `n=1` will process 2,
|
||||||
|
/// and so on.
|
||||||
|
pub fn nth(&mut self, state: TaikoScoreState, n: usize) -> Option<TaikoPerformanceAttributes> {
|
||||||
|
let difficulty = self.difficulty.nth(n)?;
|
||||||
|
|
||||||
|
let performance = TaikoPP::new(&self.map)
|
||||||
|
.mods(self.mods)
|
||||||
|
.attributes(difficulty)
|
||||||
|
.state(state)
|
||||||
|
.passed_objects(self.difficulty.idx)
|
||||||
|
.calculate();
|
||||||
|
|
||||||
|
Some(performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@ pub use self::{pp::*, score_state::TaikoScoreState, taiko_object::TaikoObjectPub
|
|||||||
|
|
||||||
#[cfg(feature = "gradual")]
|
#[cfg(feature = "gradual")]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
gradual_difficulty::TaikoGradualDifficulty, gradual_performance::TaikoGradualPerformance,
|
gradual_difficulty::TaikoGradualDifficulty,
|
||||||
|
gradual_performance::{TaikoGradualPerformance, TaikoOwnedGradualPerformance},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::taiko_object::IntoTaikoObjectIter;
|
pub(crate) use self::taiko_object::IntoTaikoObjectIter;
|
||||||
|
|||||||
Reference in New Issue
Block a user