refactor!: store lazer in Difficulty

This commit is contained in:
MaxOhn
2024-10-18 13:22:45 +02:00
parent f72ff79b4a
commit 2c77ddf4a9
11 changed files with 58 additions and 23 deletions
+10
View File
@@ -27,6 +27,11 @@ pub struct InspectDifficulty {
/// ///
/// Only relevant for osu!catch. /// Only relevant for osu!catch.
pub hardrock_offsets: Option<bool>, pub hardrock_offsets: Option<bool>,
/// Whether the calculated attributes belong to an osu!lazer or osu!stable
/// score.
///
/// Defaults to `true`.
pub lazer: Option<bool>,
} }
impl InspectDifficulty { impl InspectDifficulty {
@@ -41,6 +46,7 @@ impl InspectDifficulty {
hp, hp,
od, od,
hardrock_offsets, hardrock_offsets,
lazer,
} = self; } = self;
let mut difficulty = Difficulty::new().mods(mods); let mut difficulty = Difficulty::new().mods(mods);
@@ -73,6 +79,10 @@ impl InspectDifficulty {
difficulty = difficulty.hardrock_offsets(hardrock_offsets); difficulty = difficulty.hardrock_offsets(hardrock_offsets);
} }
if let Some(lazer) = lazer {
difficulty = difficulty.lazer(lazer);
}
difficulty difficulty
} }
} }
+22
View File
@@ -62,6 +62,7 @@ pub struct Difficulty {
hp: Option<ModsDependent>, hp: Option<ModsDependent>,
od: Option<ModsDependent>, od: Option<ModsDependent>,
hardrock_offsets: Option<bool>, hardrock_offsets: Option<bool>,
lazer: Option<bool>,
} }
/// Wrapper for beatmap attributes in [`Difficulty`]. /// Wrapper for beatmap attributes in [`Difficulty`].
@@ -97,6 +98,7 @@ impl Difficulty {
hp: None, hp: None,
od: None, od: None,
hardrock_offsets: None, hardrock_offsets: None,
lazer: None,
} }
} }
@@ -120,6 +122,7 @@ impl Difficulty {
hp, hp,
od, od,
hardrock_offsets, hardrock_offsets,
lazer,
} = self; } = self;
InspectDifficulty { InspectDifficulty {
@@ -131,6 +134,7 @@ impl Difficulty {
hp, hp,
od, od,
hardrock_offsets, hardrock_offsets,
lazer,
} }
} }
@@ -268,6 +272,18 @@ impl Difficulty {
self self
} }
/// Whether the calculated attributes belong to an osu!lazer or osu!stable
/// score.
///
/// Defaults to `true`.
///
/// Only relevant for osu!standard performance calculation.
pub const fn lazer(mut self, lazer: bool) -> Self {
self.lazer = Some(lazer);
self
}
/// Perform the difficulty calculation. /// Perform the difficulty calculation.
pub fn calculate(&self, map: &Beatmap) -> DifficultyAttributes { pub fn calculate(&self, map: &Beatmap) -> DifficultyAttributes {
let map = Cow::Borrowed(map); let map = Cow::Borrowed(map);
@@ -347,6 +363,10 @@ impl Difficulty {
self.hardrock_offsets self.hardrock_offsets
.unwrap_or_else(|| self.mods.hardrock_offsets()) .unwrap_or_else(|| self.mods.hardrock_offsets())
} }
pub(crate) fn get_lazer(&self) -> bool {
self.lazer.unwrap_or(true)
}
} }
fn non_zero_u32_to_f32(n: NonZeroU32) -> f32 { fn non_zero_u32_to_f32(n: NonZeroU32) -> f32 {
@@ -364,6 +384,7 @@ impl Debug for Difficulty {
hp, hp,
od, od,
hardrock_offsets, hardrock_offsets,
lazer,
} = self; } = self;
f.debug_struct("Difficulty") f.debug_struct("Difficulty")
@@ -375,6 +396,7 @@ impl Debug for Difficulty {
.field("hp", hp) .field("hp", hp)
.field("od", od) .field("od", od)
.field("hardrock_offsets", hardrock_offsets) .field("hardrock_offsets", hardrock_offsets)
.field("lazer", lazer)
.finish() .finish()
} }
} }
+1 -1
View File
@@ -302,7 +302,7 @@ impl<'map> Performance<'map> {
/// Whether the calculated attributes belong to an osu!lazer or osu!stable /// Whether the calculated attributes belong to an osu!lazer or osu!stable
/// score. /// score.
/// ///
/// Defaults to lazer. /// Defaults to `true`.
/// ///
/// This affects internal accuracy calculation because lazer considers /// This affects internal accuracy calculation because lazer considers
/// slider heads for accuracy whereas stable does not. /// slider heads for accuracy whereas stable does not.
-1
View File
@@ -481,7 +481,6 @@ impl<'map> TryFrom<OsuPerformance<'map>> for CatchPerformance<'map> {
n50, n50,
misses, misses,
hitresult_priority: _, hitresult_priority: _,
lazer: _,
} = osu; } = osu;
Ok(Self { Ok(Self {
-1
View File
@@ -834,7 +834,6 @@ impl<'map> TryFrom<OsuPerformance<'map>> for ManiaPerformance<'map> {
n50, n50,
misses, misses,
hitresult_priority, hitresult_priority,
lazer: _,
} = osu; } = osu;
Ok(Self { Ok(Self {
+1 -11
View File
@@ -55,18 +55,8 @@ pub fn convert_objects(
OsuObjectKind::Circle => attrs.n_circles += 1, OsuObjectKind::Circle => attrs.n_circles += 1,
OsuObjectKind::Slider(ref slider) => { OsuObjectKind::Slider(ref slider) => {
attrs.n_sliders += 1; attrs.n_sliders += 1;
attrs.n_slider_ticks += slider.tick_count() as u32;
attrs.max_combo += slider.nested_objects.len() as u32; attrs.max_combo += slider.nested_objects.len() as u32;
attrs.n_slider_ticks += slider
.nested_objects
.iter()
.filter(|nested| {
matches!(
nested.kind,
NestedSliderObjectKind::Tick | NestedSliderObjectKind::Repeat
)
})
.count() as u32;
} }
OsuObjectKind::Spinner(_) => attrs.n_spinners += 1, OsuObjectKind::Spinner(_) => attrs.n_spinners += 1,
} }
+2
View File
@@ -92,6 +92,7 @@ impl OsuGradualDifficulty {
attrs.n_circles = 0; attrs.n_circles = 0;
attrs.n_sliders = 0; attrs.n_sliders = 0;
attrs.n_slider_ticks = 0;
attrs.n_spinners = 0; attrs.n_spinners = 0;
attrs.max_combo = 0; attrs.max_combo = 0;
@@ -128,6 +129,7 @@ impl OsuGradualDifficulty {
OsuObjectKind::Circle => attrs.n_circles += 1, OsuObjectKind::Circle => attrs.n_circles += 1,
OsuObjectKind::Slider(slider) => { OsuObjectKind::Slider(slider) => {
attrs.n_sliders += 1; attrs.n_sliders += 1;
attrs.n_slider_ticks += slider.tick_count() as u32;
attrs.max_combo += slider.nested_objects.len() as u32; attrs.max_combo += slider.nested_objects.len() as u32;
} }
OsuObjectKind::Spinner { .. } => attrs.n_spinners += 1, OsuObjectKind::Spinner { .. } => attrs.n_spinners += 1,
+13
View File
@@ -308,6 +308,19 @@ impl OsuSlider {
.count() .count()
} }
/// Counts both ticks and repeats
pub fn tick_count(&self) -> usize {
self.nested_objects
.iter()
.filter(|nested| {
matches!(
nested.kind,
NestedSliderObjectKind::Tick | NestedSliderObjectKind::Repeat
)
})
.count()
}
pub fn tail(&self) -> Option<&NestedSliderObject> { pub fn tail(&self) -> Option<&NestedSliderObject> {
self.nested_objects self.nested_objects
.iter() .iter()
+4 -1
View File
@@ -80,15 +80,17 @@ use super::{OsuPerformanceAttributes, OsuScoreState};
/// [`next`]: OsuGradualPerformance::next /// [`next`]: OsuGradualPerformance::next
/// [`nth`]: OsuGradualPerformance::nth /// [`nth`]: OsuGradualPerformance::nth
pub struct OsuGradualPerformance { pub struct OsuGradualPerformance {
lazer: bool,
difficulty: OsuGradualDifficulty, difficulty: OsuGradualDifficulty,
} }
impl OsuGradualPerformance { impl OsuGradualPerformance {
/// Create a new gradual performance calculator for osu!standard maps. /// Create a new gradual performance calculator for osu!standard maps.
pub fn new(difficulty: Difficulty, converted: &OsuBeatmap<'_>) -> Self { pub fn new(difficulty: Difficulty, converted: &OsuBeatmap<'_>) -> Self {
let lazer = difficulty.get_lazer();
let difficulty = OsuGradualDifficulty::new(difficulty, converted); let difficulty = OsuGradualDifficulty::new(difficulty, converted);
Self { difficulty } Self { lazer, difficulty }
} }
/// Process the next hit object and calculate the performance attributes /// Process the next hit object and calculate the performance attributes
@@ -113,6 +115,7 @@ impl OsuGradualPerformance {
.difficulty .difficulty
.nth(n)? .nth(n)?
.performance() .performance()
.lazer(self.lazer)
.state(state) .state(state)
.difficulty(self.difficulty.difficulty.clone()) .difficulty(self.difficulty.difficulty.clone())
.passed_objects(self.difficulty.idx as u32) .passed_objects(self.difficulty.idx as u32)
+5 -7
View File
@@ -35,7 +35,6 @@ pub struct OsuPerformance<'map> {
pub(crate) n50: Option<u32>, pub(crate) n50: Option<u32>,
pub(crate) misses: Option<u32>, pub(crate) misses: Option<u32>,
pub(crate) hitresult_priority: HitResultPriority, pub(crate) hitresult_priority: HitResultPriority,
pub(crate) lazer: Option<bool>,
} }
impl<'map> OsuPerformance<'map> { impl<'map> OsuPerformance<'map> {
@@ -158,12 +157,12 @@ impl<'map> OsuPerformance<'map> {
/// Whether the calculated attributes belong to an osu!lazer or osu!stable /// Whether the calculated attributes belong to an osu!lazer or osu!stable
/// score. /// score.
/// ///
/// Defaults to lazer. /// Defaults to `true`.
/// ///
/// This affects internal accuracy calculation because lazer considers /// This affects internal accuracy calculation because lazer considers
/// slider heads for accuracy whereas stable does not. /// slider heads for accuracy whereas stable does not.
pub const fn lazer(mut self, lazer: bool) -> Self { pub fn lazer(mut self, lazer: bool) -> Self {
self.lazer = Some(lazer); self.difficulty = self.difficulty.lazer(lazer);
self self
} }
@@ -366,7 +365,7 @@ impl<'map> OsuPerformance<'map> {
let mut n100 = self.n100.map_or(0, |n| cmp::min(n, n_remaining)); let mut n100 = self.n100.map_or(0, |n| cmp::min(n, n_remaining));
let mut n50 = self.n50.map_or(0, |n| cmp::min(n, n_remaining)); let mut n50 = self.n50.map_or(0, |n| cmp::min(n, n_remaining));
let lazer = self.lazer.unwrap_or(true); let lazer = self.difficulty.get_lazer();
let (n_slider_ends, n_slider_ticks, max_slider_ends, max_slider_ticks) = if lazer { let (n_slider_ends, n_slider_ticks, max_slider_ends, max_slider_ticks) = if lazer {
let n_slider_ends = self let n_slider_ends = self
@@ -627,7 +626,7 @@ impl<'map> OsuPerformance<'map> {
let effective_miss_count = calculate_effective_misses(&attrs, &state); let effective_miss_count = calculate_effective_misses(&attrs, &state);
let lazer = self.lazer.unwrap_or(true); let lazer = self.difficulty.get_lazer();
let (n_slider_ends, n_slider_ticks) = if lazer { let (n_slider_ends, n_slider_ticks) = if lazer {
(attrs.n_sliders, attrs.n_slider_ticks) (attrs.n_sliders, attrs.n_slider_ticks)
@@ -660,7 +659,6 @@ impl<'map> OsuPerformance<'map> {
n50: None, n50: None,
misses: None, misses: None,
hitresult_priority: HitResultPriority::DEFAULT, hitresult_priority: HitResultPriority::DEFAULT,
lazer: None,
} }
} }
} }
-1
View File
@@ -371,7 +371,6 @@ impl<'map> TryFrom<OsuPerformance<'map>> for TaikoPerformance<'map> {
n50: _, n50: _,
misses, misses,
hitresult_priority, hitresult_priority,
lazer: _,
} = osu; } = osu;
Ok(Self { Ok(Self {