feat: added struct CatchOwnedGradualDifficulty

This commit is contained in:
MaxOhn
2023-11-09 11:46:52 +01:00
parent 1aa6bcdb7a
commit 49f640f939
6 changed files with 122 additions and 87 deletions
+1 -2
View File
@@ -137,7 +137,6 @@ impl BeatmapExt for Beatmap {
curve_bufs: CurveBuffers::default(),
last_pos: None,
last_time: 0.0,
map: self,
ticks: Vec::new(),
with_hr: mods.hr(),
};
@@ -145,7 +144,7 @@ impl BeatmapExt for Beatmap {
let mut hit_objects: Vec<_> = self
.hit_objects
.iter()
.filter_map(|h| FruitOrJuice::new(h, &mut params))
.filter_map(|h| FruitOrJuice::new(h, &mut params, self))
.flatten()
.collect();
+1 -1
View File
@@ -29,7 +29,7 @@ impl CatchObject {
}
}
pub(crate) fn with_hr(mut self, params: &mut FruitParams<'_>) -> Self {
pub(crate) fn with_hr(mut self, params: &mut FruitParams) -> Self {
let mut offset_pos = self.pos;
let time_diff = self.time - params.last_time;
+6 -12
View File
@@ -12,12 +12,11 @@ const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
const BASE_SCORING_DISTANCE: f64 = 100.0;
#[derive(Clone, Debug)]
pub(crate) struct FruitParams<'a> {
pub(crate) struct FruitParams {
pub(crate) attributes: CatchDifficultyAttributes,
pub(crate) curve_bufs: CurveBuffers,
pub(crate) last_pos: Option<f32>,
pub(crate) last_time: f64,
pub(crate) map: &'a Beatmap,
pub(crate) ticks: Vec<(Pos2, f64)>,
pub(crate) with_hr: bool,
}
@@ -31,7 +30,7 @@ pub(crate) enum FruitOrJuice {
}
impl FruitOrJuice {
pub(crate) fn new(h: &HitObject, params: &mut FruitParams<'_>) -> Option<Self> {
pub(crate) fn new(h: &HitObject, params: &mut FruitParams, map: &Beatmap) -> Option<Self> {
match &h.kind {
HitObjectKind::Circle => {
let mut h = CatchObject::new((h.pos, h.start_time));
@@ -54,17 +53,12 @@ impl FruitOrJuice {
params.last_pos = Some(h.pos.x + control_points[control_points.len() - 1].pos.x);
params.last_time = h.start_time;
let timing_point = params.map.timing_point_at(h.start_time);
let timing_point = map.timing_point_at(h.start_time);
let difficulty_point = params
.map
.difficulty_point_at(h.start_time)
.unwrap_or_default();
let difficulty_point = map.difficulty_point_at(h.start_time).unwrap_or_default();
let vel_factor =
BASE_SCORING_DISTANCE * params.map.slider_mult / timing_point.beat_len;
let tick_dist_factor =
BASE_SCORING_DISTANCE * params.map.slider_mult / params.map.tick_rate;
let vel_factor = BASE_SCORING_DISTANCE * map.slider_mult / timing_point.beat_len;
let tick_dist_factor = BASE_SCORING_DISTANCE * map.slider_mult / map.tick_rate;
let vel = vel_factor * difficulty_point.slider_vel;
+110 -68
View File
@@ -1,9 +1,9 @@
use std::{iter, slice::Iter};
use std::iter;
use crate::{
catch::{difficulty_object::DifficultyObject, SECTION_LENGTH, STAR_SCALING_FACTOR},
curve::CurveBuffers,
parse::{HitObject, Pos2},
parse::Pos2,
Beatmap, Mods,
};
@@ -50,9 +50,111 @@ use super::{
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
#[derive(Clone, Debug)]
pub struct CatchGradualDifficulty<'map> {
map: &'map Beatmap,
inner: CatchGradualDifficultyInner,
}
impl<'map> CatchGradualDifficulty<'map> {
/// Create a new difficulty attributes iterator for osu!catch maps.
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
let inner = CatchGradualDifficultyInner::new(map, mods);
Self { map, inner }
}
pub(crate) fn idx(&self) -> usize {
self.inner.idx
}
}
impl Iterator for CatchGradualDifficulty<'_> {
type Item = CatchDifficultyAttributes;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next(self.map)
}
}
/// Gradually calculate the difficulty attributes of an osu!catch map.
///
/// Check [`CatchGradualDifficulty`] for more information. This struct does the same
/// but includes an up-front allocation to avoid being bound to a lifetime.
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
#[derive(Clone, Debug)]
pub struct CatchOwnedGradualDifficulty {
map: Box<Beatmap>,
inner: CatchGradualDifficultyInner,
}
impl CatchOwnedGradualDifficulty {
/// Create a new difficulty attributes iterator for osu!catch maps.
pub fn new(map: Beatmap, mods: u32) -> Self {
let inner = CatchGradualDifficultyInner::new(&map, mods);
let map = Box::new(map);
Self { map, inner }
}
#[allow(unused)]
pub(crate) fn idx(&self) -> usize {
self.inner.idx
}
}
impl Iterator for CatchOwnedGradualDifficulty {
type Item = CatchDifficultyAttributes;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next(&self.map)
}
}
#[derive(Clone, Debug)]
struct CatchObjectIter {
last_object: Option<FruitOrJuice>,
params: FruitParams,
}
impl CatchObjectIter {
fn new(mods: impl Mods, attributes: CatchDifficultyAttributes) -> Self {
let params = FruitParams {
attributes,
curve_bufs: CurveBuffers::default(),
last_pos: None,
last_time: 0.0,
ticks: Vec::new(),
with_hr: mods.hr(),
};
Self {
last_object: None,
params,
}
}
fn attributes(&self) -> CatchDifficultyAttributes {
self.params.attributes.clone()
}
fn next(&mut self, map: &Beatmap, idx: usize) -> Option<CatchObject> {
if let opt @ Some(_) = self.last_object.as_mut().and_then(Iterator::next) {
return opt;
}
map.hit_objects[idx..]
.iter()
.find_map(|h| FruitOrJuice::new(h, &mut self.params, map))
.and_then(|h| self.last_object.insert(h).next())
}
}
#[derive(Clone, Debug)]
struct CatchGradualDifficultyInner {
pub(crate) idx: usize,
clock_rate: f64,
hit_objects: CatchObjectIter<'map>,
hit_objects: CatchObjectIter,
movement: Movement,
prev: CatchObject,
half_catcher_width: f64,
@@ -62,9 +164,8 @@ pub struct CatchGradualDifficulty<'map> {
strain_peak_buf: Vec<f64>,
}
impl<'map> CatchGradualDifficulty<'map> {
/// Create a new difficulty attributes iterator for osu!catch maps.
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
impl CatchGradualDifficultyInner {
fn new(map: &Beatmap, mods: u32) -> Self {
let map_attributes = map.attributes().mods(mods).build();
let attributes = CatchDifficultyAttributes {
@@ -72,7 +173,7 @@ impl<'map> CatchGradualDifficulty<'map> {
..Default::default()
};
let hit_objects = CatchObjectIter::new(map, mods, attributes);
let hit_objects = CatchObjectIter::new(mods, attributes);
let half_catcher_width =
(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE) as f64;
@@ -104,13 +205,9 @@ impl<'map> CatchGradualDifficulty<'map> {
&mut self.last_excess,
);
}
}
impl Iterator for CatchGradualDifficulty<'_> {
type Item = CatchDifficultyAttributes;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.hit_objects.next()?;
fn next(&mut self, map: &Beatmap) -> Option<CatchDifficultyAttributes> {
let curr = self.hit_objects.next(map, self.idx)?;
self.idx += 1;
if self.idx == 1 {
@@ -165,58 +262,3 @@ impl Iterator for CatchGradualDifficulty<'_> {
Some(attrs)
}
}
#[derive(Clone, Debug)]
struct CatchObjectIter<'map> {
last_object: Option<FruitOrJuice>,
hit_objects: Iter<'map, HitObject>,
params: FruitParams<'map>,
}
impl<'map> CatchObjectIter<'map> {
fn new(map: &'map Beatmap, mods: impl Mods, attributes: CatchDifficultyAttributes) -> Self {
let params = FruitParams {
attributes,
curve_bufs: CurveBuffers::default(),
last_pos: None,
last_time: 0.0,
map,
ticks: Vec::new(),
with_hr: mods.hr(),
};
Self {
last_object: None,
hit_objects: map.hit_objects.iter(),
params,
}
}
fn attributes(&self) -> CatchDifficultyAttributes {
self.params.attributes.clone()
}
}
impl Iterator for CatchObjectIter<'_> {
type Item = CatchObject;
fn next(&mut self) -> Option<Self::Item> {
if let opt @ Some(_) = self.last_object.as_mut().and_then(Iterator::next) {
return opt;
}
self.hit_objects
.find_map(|h| FruitOrJuice::new(h, &mut self.params))
.and_then(|h| self.last_object.insert(h).next())
}
fn size_hint(&self) -> (usize, Option<usize>) {
let min = self
.last_object
.as_ref()
.map(ExactSizeIterator::len)
.unwrap_or(0);
(min, None)
}
}
+1 -1
View File
@@ -138,7 +138,7 @@ impl<'map> CatchGradualPerformance<'map> {
.clone()
.attributes(difficulty)
.state(state)
.passed_objects(self.difficulty.idx)
.passed_objects(self.difficulty.idx())
.calculate();
Some(performance)
+3 -3
View File
@@ -17,7 +17,8 @@ pub use self::{catch_object::CatchObject, pp::*, score_state::CatchScoreState};
#[cfg(feature = "gradual")]
pub use self::{
gradual_difficulty::CatchGradualDifficulty, gradual_performance::CatchGradualPerformance,
gradual_difficulty::{CatchGradualDifficulty, CatchOwnedGradualDifficulty},
gradual_performance::CatchGradualPerformance,
};
pub(crate) use self::fruit_or_juice::{FruitOrJuice, FruitParams};
@@ -165,7 +166,6 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
curve_bufs: CurveBuffers::default(),
last_pos: None,
last_time: 0.0,
map,
ticks: Vec::new(), // using the same buffer for all sliders
with_hr: mods.hr(),
};
@@ -174,7 +174,7 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
let mut hit_objects = map
.hit_objects
.iter()
.filter_map(|h| FruitOrJuice::new(h, &mut params))
.filter_map(|h| FruitOrJuice::new(h, &mut params, map))
.flatten()
.take(take);