mania fixes

This commit is contained in:
MaxOhn
2022-10-15 10:19:48 +02:00
parent a091d30e1e
commit fa91703a2a
7 changed files with 87 additions and 27 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ pub enum GradualDifficultyAttributes<'map> {
/// Gradual osu!catch difficulty attributes.
Catch(CatchGradualDifficultyAttributes<'map>),
/// Gradual osu!mania difficulty attributes.
Mania(ManiaGradualDifficultyAttributes),
Mania(ManiaGradualDifficultyAttributes<'map>),
/// Gradual osu!standard difficulty attributes.
Osu(OsuGradualDifficultyAttributes),
/// Gradual osu!taiko difficulty attributes.
+33 -8
View File
@@ -1,4 +1,4 @@
use crate::{beatmap::BeatmapHitWindows, Beatmap, Mods};
use crate::{beatmap::BeatmapHitWindows, parse::HitObjectKind, Beatmap, Mods};
use super::{
difficulty_object::ManiaDifficultyObject,
@@ -37,16 +37,18 @@ use super::{
/// }
/// ```
#[derive(Clone, Debug)]
pub struct ManiaGradualDifficultyAttributes {
pub struct ManiaGradualDifficultyAttributes<'map> {
pub(crate) idx: usize,
map: &'map Beatmap,
hit_window: f64,
strain: Strain,
diff_objects: Vec<ManiaDifficultyObject>,
curr_combo: usize,
}
impl ManiaGradualDifficultyAttributes {
impl<'map> ManiaGradualDifficultyAttributes<'map> {
/// Create a new difficulty attributes iterator for osu!mania maps.
pub fn new(map: &Beatmap, mods: u32) -> Self {
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
let total_columns = map.cs.round().max(1.0);
let clock_rate = mods.clock_rate();
@@ -55,8 +57,8 @@ impl ManiaGradualDifficultyAttributes {
let BeatmapHitWindows { od: hit_window, .. } = map
.attributes()
.mods(mods)
// TODO: allow converts
// .converted(is_convert)
// TODO: allow converts in gradual calc
.converted(false)
.clock_rate(clock_rate)
.hit_windows();
@@ -71,30 +73,53 @@ impl ManiaGradualDifficultyAttributes {
ManiaDifficultyObject::new(base, prev, clock_rate, total_columns, i)
});
let curr_combo = if let Some(h) = map.hit_objects.first() {
match &h.kind {
HitObjectKind::Hold { end_time } => {
1 + ((*end_time - h.start_time) / 100.0) as usize
}
_ => 1,
}
} else {
0
};
let mut diff_objects = Vec::with_capacity(map.hit_objects.len().saturating_sub(1));
diff_objects.extend(diff_objects_iter);
Self {
idx: 0,
map,
hit_window,
strain,
diff_objects,
curr_combo,
}
}
}
impl Iterator for ManiaGradualDifficultyAttributes {
impl Iterator for ManiaGradualDifficultyAttributes<'_> {
type Item = ManiaDifficultyAttributes;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.diff_objects.get(self.idx)?;
self.idx += 1;
if let Some(h) = self.map.hit_objects.get(self.idx) {
match &h.kind {
HitObjectKind::Hold { end_time } => {
self.curr_combo += 1 + ((*end_time - h.start_time) / 100.0) as usize
}
_ => self.curr_combo += 1,
}
}
self.strain.process(curr, &self.diff_objects);
Some(ManiaDifficultyAttributes {
stars: self.strain.clone().difficulty_value() * STAR_SCALING_FACTOR,
hit_window: self.hit_window,
max_combo: self.curr_combo,
})
}
@@ -106,7 +131,7 @@ impl Iterator for ManiaGradualDifficultyAttributes {
}
}
impl ExactSizeIterator for ManiaGradualDifficultyAttributes {
impl ExactSizeIterator for ManiaGradualDifficultyAttributes<'_> {
#[inline]
fn len(&self) -> usize {
self.diff_objects.len() - self.idx
+1 -1
View File
@@ -101,7 +101,7 @@ impl ManiaScoreState {
/// ```
#[derive(Clone, Debug)]
pub struct ManiaGradualPerformanceAttributes<'map> {
difficulty: ManiaGradualDifficultyAttributes,
difficulty: ManiaGradualDifficultyAttributes<'map>,
performance: ManiaPP<'map>,
}
+34 -6
View File
@@ -7,7 +7,7 @@ mod skills;
use std::borrow::Cow;
use crate::{beatmap::BeatmapHitWindows, Beatmap, GameMode, Mods, OsuStars};
use crate::{beatmap::BeatmapHitWindows, parse::HitObjectKind, Beatmap, GameMode, Mods, OsuStars};
pub use self::{gradual_difficulty::*, gradual_performance::*, pp::*};
@@ -45,6 +45,7 @@ pub struct ManiaStars<'map> {
mods: u32,
passed_objects: Option<usize>,
clock_rate: Option<f64>,
is_convert: bool,
}
impl<'map> ManiaStars<'map> {
@@ -56,6 +57,7 @@ impl<'map> ManiaStars<'map> {
mods: 0,
passed_objects: None,
clock_rate: None,
is_convert: false,
}
}
@@ -91,10 +93,19 @@ impl<'map> ManiaStars<'map> {
self
}
/// Specify whether the map is a convert i.e. an osu!standard map.
#[inline]
pub fn is_convert(mut self, is_convert: bool) -> Self {
self.is_convert = is_convert;
self
}
/// Calculate all difficulty related values, including stars.
#[inline]
pub fn calculate(self) -> ManiaDifficultyAttributes {
let is_convert = matches!(self.map, Cow::Owned(_));
let is_convert = self.is_convert || matches!(self.map, Cow::Owned(_));
let clock_rate = self.clock_rate.unwrap_or_else(|| self.mods.clock_rate());
let BeatmapHitWindows { od: hit_window, .. } = self
@@ -105,11 +116,12 @@ impl<'map> ManiaStars<'map> {
.clock_rate(clock_rate)
.hit_windows();
let strain = calculate_strain(self);
let ManiaResult { strain, max_combo } = calculate_result(self);
ManiaDifficultyAttributes {
stars: strain.difficulty_value() * STAR_SCALING_FACTOR,
hit_window,
max_combo,
}
}
@@ -119,7 +131,7 @@ impl<'map> ManiaStars<'map> {
#[inline]
pub fn strains(self) -> ManiaStrains {
let clock_rate = self.clock_rate.unwrap_or_else(|| self.mods.clock_rate());
let strain = calculate_strain(self);
let ManiaResult { strain, .. } = calculate_result(self);
ManiaStrains {
section_len: SECTION_LEN * clock_rate, // TODO: clock_rate correct here?
@@ -147,12 +159,13 @@ impl ManiaStrains {
}
}
fn calculate_strain(params: ManiaStars<'_>) -> Strain {
fn calculate_result(params: ManiaStars<'_>) -> ManiaResult {
let ManiaStars {
map,
mods,
passed_objects,
clock_rate,
is_convert: _,
} = params;
let take = passed_objects.unwrap_or(map.hit_objects.len());
@@ -160,11 +173,18 @@ fn calculate_strain(params: ManiaStars<'_>) -> Strain {
let clock_rate = clock_rate.unwrap_or_else(|| mods.clock_rate());
let mut strain = Strain::new(total_columns as usize);
let mut max_combo = 0;
let diff_objects_iter = map
.hit_objects
.iter()
.take(take)
.inspect(|h| match &h.kind {
HitObjectKind::Hold { end_time } => {
max_combo += 1 + ((*end_time - h.start_time) / 100.0) as usize
}
_ => max_combo += 1,
})
.skip(1)
.map(ManiaObject::new)
.enumerate()
@@ -180,7 +200,12 @@ fn calculate_strain(params: ManiaStars<'_>) -> Strain {
strain.process(curr, &diff_objects);
}
strain
ManiaResult { strain, max_combo }
}
struct ManiaResult {
strain: Strain,
max_combo: usize,
}
/// The result of a difficulty calculation on an osu!mania map.
@@ -190,6 +215,8 @@ pub struct ManiaDifficultyAttributes {
pub stars: f64,
/// The perceived hit window for an n300 inclusive of rate-adjusting mods (DT/HT/etc).
pub hit_window: f64,
/// The maximum achievable combo.
pub max_combo: usize,
}
/// The result of a performance calculation on an osu!mania map.
@@ -239,6 +266,7 @@ impl<'map> From<OsuStars<'map>> for ManiaStars<'map> {
mods,
passed_objects,
clock_rate,
is_convert: true,
}
}
}
+13 -6
View File
@@ -212,8 +212,9 @@ impl<'map> ManiaPP<'map> {
/// Calculate all performance related values, including pp and stars.
pub fn calculate(self) -> ManiaPerformanceAttributes {
let attrs = self.attributes.unwrap_or_else(|| {
// TODO: handle converts
let mut calculator = ManiaStars::new(self.map.as_ref()).mods(self.mods);
let mut calculator = ManiaStars::new(self.map.as_ref())
.mods(self.mods)
.is_convert(matches!(self.map, Cow::Owned(_)));
if let Some(passed_objects) = self.passed_objects {
calculator = calculator.passed_objects(passed_objects);
@@ -355,7 +356,7 @@ impl ManiaPpInner {
fn calculate(self) -> ManiaPerformanceAttributes {
// * Arbitrary initial value for scaling pp in order to standardize distributions across game modes.
// * The specific number has no intrinsic meaning and can be adjusted as needed.
let mut multiplier = 0.8;
let mut multiplier = 8.0;
if self.mods.nf() {
multiplier *= 0.75;
@@ -379,7 +380,7 @@ impl ManiaPpInner {
// Star rating to pp curve
(self.attrs.stars - 0.15).max(0.05).powf(2.2)
// From 80% accuracy, 1/20th of total pp is awarded per additional 1% accuracy
* (5.0 * self.custom_accuracy() - 4.0).max(0.0)
* (5.0 * self.calculate_custom_accuracy() - 4.0).max(0.0)
// Length bonus, capped at 1500 notes
* (1.0 + 0.1 * (self.total_hits() / 1500.0).min(1.0))
}
@@ -388,7 +389,7 @@ impl ManiaPpInner {
self.state.total_hits() as f64
}
fn custom_accuracy(&self) -> f64 {
fn calculate_custom_accuracy(&self) -> f64 {
let ManiaScoreState {
n320,
n300,
@@ -398,8 +399,14 @@ impl ManiaPpInner {
n_misses: _,
} = &self.state;
let total_hits = self.state.total_hits();
if total_hits == 0 {
return 0.0;
}
let numerator = *n320 * 320 + *n300 * 300 + *n200 * 200 + *n100 * 100 + *n50 * 50;
let denominator = self.total_hits() * 320.0;
let denominator = total_hits as f64 * 320.0;
numerator as f64 / denominator
}
+4 -4
View File
@@ -25,9 +25,9 @@ impl Strain {
pub(crate) fn new(total_columns: usize) -> Self {
Self {
start_times: Vec::with_capacity(total_columns),
end_times: Vec::with_capacity(total_columns),
individual_strains: Vec::with_capacity(total_columns),
start_times: vec![0.0; total_columns],
end_times: vec![0.0; total_columns],
individual_strains: vec![0.0; total_columns],
individual_strain: 0.0,
overall_strain: 1.0,
curr_strain: 0.0,
@@ -194,6 +194,6 @@ impl StrainDecaySkill for Strain {
Self::OVERALL_DECAY_BASE,
);
individual_decay * overall_decay
individual_decay + overall_decay
}
}
+1 -1
View File
@@ -209,7 +209,7 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
mods,
passed_objects,
clock_rate,
..
is_convert: _,
} = params;
let take = passed_objects.unwrap_or(map.hit_objects.len());