apply some rosu-pp-older changes

This commit is contained in:
tsunyoku
2024-10-13 15:42:13 +01:00
parent f04943de56
commit c4e7a74e5f
2 changed files with 26 additions and 137 deletions
+23 -128
View File
@@ -33,14 +33,13 @@ pub struct OsuPP<'m> {
map: &'m Beatmap, map: &'m Beatmap,
attributes: Option<OsuDifficultyAttributes>, attributes: Option<OsuDifficultyAttributes>,
mods: GameMods, mods: GameMods,
combo: Option<usize>, combo: Option<u32>,
acc: Option<f32>, acc: Option<f32>,
n300: Option<usize>, n300: Option<u32>,
n100: Option<usize>, n100: Option<u32>,
n50: Option<usize>, n50: Option<u32>,
n_misses: usize, n_misses: u32,
passed_objects: Option<usize>,
} }
impl<'m> OsuPP<'m> { impl<'m> OsuPP<'m> {
@@ -53,12 +52,10 @@ impl<'m> OsuPP<'m> {
mods: GameMods::default(), mods: GameMods::default(),
combo: None, combo: None,
acc: None, acc: None,
n300: None, n300: None,
n100: None, n100: None,
n50: None, n50: None,
n_misses: 0, n_misses: 0,
passed_objects: None,
} }
} }
@@ -70,7 +67,7 @@ impl<'m> OsuPP<'m> {
#[inline] #[inline]
pub fn attributes(mut self, attributes: impl OsuAttributeProvider) -> Self { pub fn attributes(mut self, attributes: impl OsuAttributeProvider) -> Self {
if let Some(attributes) = attributes.attributes() { if let Some(attributes) = attributes.attributes() {
self.attributes.replace(attributes); self.attributes = Some(attributes);
} }
self self
@@ -88,58 +85,50 @@ impl<'m> OsuPP<'m> {
/// Specify the max combo of the play. /// Specify the max combo of the play.
#[inline] #[inline]
pub fn combo(mut self, combo: usize) -> Self { pub fn combo(mut self, combo: u32) -> Self {
self.combo.replace(combo); self.combo = Some(combo);
self self
} }
/// Specify the amount of 300s of a play. /// Specify the amount of 300s of a play.
#[inline] #[inline]
pub fn n300(mut self, n300: usize) -> Self { pub fn n300(mut self, n300: u32) -> Self {
self.n300.replace(n300); self.n300 = Some(n300);
self self
} }
/// Specify the amount of 100s of a play. /// Specify the amount of 100s of a play.
#[inline] #[inline]
pub fn n100(mut self, n100: usize) -> Self { pub fn n100(mut self, n100: u32) -> Self {
self.n100.replace(n100); self.n100 = Some(n100);
self self
} }
/// Specify the amount of 50s of a play. /// Specify the amount of 50s of a play.
#[inline] #[inline]
pub fn n50(mut self, n50: usize) -> Self { pub fn n50(mut self, n50: u32) -> Self {
self.n50.replace(n50); self.n50 = Some(n50);
self self
} }
/// Specify the amount of misses of a play. /// Specify the amount of misses of a play.
#[inline] #[inline]
pub fn misses(mut self, n_misses: usize) -> Self { pub fn misses(mut self, n_misses: u32) -> Self {
self.n_misses = n_misses; self.n_misses = n_misses;
self self
} }
/// Amount of passed objects for partial plays, e.g. a fail.
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects.replace(passed_objects);
self
}
/// Generate the hit results with respect to the given accuracy between `0` and `100`. /// Generate the hit results with respect to the given accuracy between `0` and `100`.
/// ///
/// Be sure to set `misses` beforehand! /// Be sure to set `misses` beforehand!
/// In case of a partial play, be also sure to set `passed_objects` beforehand! /// In case of a partial play, be also sure to set `passed_objects` beforehand!
pub fn accuracy(mut self, acc: f32) -> Self { pub fn accuracy(mut self, acc: f32) -> Self {
let n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len()); let n_objects = self.map.hit_objects.len() as u32;
let acc = acc / 100.0; let acc = acc / 100.0;
@@ -150,7 +139,7 @@ impl<'m> OsuPP<'m> {
let placed_points = 2 * n100 + n50 + self.n_misses; let placed_points = 2 * n100 + n50 + self.n_misses;
let missing_objects = n_objects - n100 - n50 - self.n_misses; let missing_objects = n_objects - n100 - n50 - self.n_misses;
let missing_points = let missing_points =
((6.0 * acc * n_objects as f32).round() as usize).saturating_sub(placed_points); ((6.0 * acc * n_objects as f32).round() as u32).saturating_sub(placed_points);
let mut n300 = missing_objects.min(missing_points / 6); let mut n300 = missing_objects.min(missing_points / 6);
n50 += missing_objects - n300; n50 += missing_objects - n300;
@@ -170,7 +159,7 @@ impl<'m> OsuPP<'m> {
self.n50.replace(n50); self.n50.replace(n50);
} else { } else {
let misses = self.n_misses.min(n_objects); let misses = self.n_misses.min(n_objects);
let target_total = (acc * n_objects as f32 * 6.0).round() as usize; let target_total = (acc * n_objects as f32 * 6.0).round() as u32;
let delta = target_total - (n_objects - misses); let delta = target_total - (n_objects - misses);
let mut n300 = delta / 5; let mut n300 = delta / 5;
@@ -198,7 +187,7 @@ impl<'m> OsuPP<'m> {
fn assert_hitresults(&mut self) { fn assert_hitresults(&mut self) {
if self.acc.is_none() { if self.acc.is_none() {
let n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len()); let n_objects = self.map.hit_objects.len() as u32;
let remaining = n_objects let remaining = n_objects
.saturating_sub(self.n300.unwrap_or(0)) .saturating_sub(self.n300.unwrap_or(0))
@@ -234,7 +223,7 @@ impl<'m> OsuPP<'m> {
/// containing stars and other attributes. /// containing stars and other attributes.
pub fn calculate(mut self) -> OsuPerformanceAttributes { pub fn calculate(mut self) -> OsuPerformanceAttributes {
if self.attributes.is_none() { if self.attributes.is_none() {
let attributes = stars(self.map, self.mods.clone(), self.passed_objects); let attributes = stars(self.map, self.mods.clone());
self.attributes.replace(attributes); self.attributes.replace(attributes);
} }
@@ -481,8 +470,8 @@ impl<'m> OsuPP<'m> {
} }
#[inline] #[inline]
fn total_hits(&self) -> usize { fn total_hits(&self) -> u32 {
let n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len()); let n_objects = self.map.hit_objects.len() as u32;
(self.n300.unwrap_or(0) + self.n100.unwrap_or(0) + self.n50.unwrap_or(0) + self.n_misses) (self.n300.unwrap_or(0) + self.n100.unwrap_or(0) + self.n50.unwrap_or(0) + self.n_misses)
.min(n_objects) .min(n_objects)
@@ -501,7 +490,7 @@ impl<'m> OsuPP<'m> {
let mut combo_based_miss_count = 0.0; let mut combo_based_miss_count = 0.0;
let attributes = self.attributes.as_ref().unwrap(); let attributes = self.attributes.as_ref().unwrap();
let combo = self.combo.unwrap_or(attributes.max_combo) as f32; let combo = self.combo.unwrap_or(attributes.max_combo as u32) as f32;
let n100 = self.n100.unwrap_or(0) as f32; let n100 = self.n100.unwrap_or(0) as f32;
let n50 = self.n50.unwrap_or(0) as f32; let n50 = self.n50.unwrap_or(0) as f32;
@@ -536,97 +525,3 @@ impl OsuAttributeProvider for OsuPerformanceAttributes {
Some(self.difficulty) Some(self.difficulty)
} }
} }
#[cfg(test)]
mod test {
use super::*;
use crate::Beatmap;
#[test]
fn osu_only_accuracy() {
let map = Beatmap::default();
let total_objects = 1234;
let target_acc = 97.5;
let calculator = OsuPP::new(&map)
.passed_objects(total_objects)
.accuracy(target_acc);
let numerator = 6 * calculator.n300.unwrap_or(0)
+ 2 * calculator.n100.unwrap_or(0)
+ calculator.n50.unwrap_or(0);
let denominator = 6 * total_objects;
let acc = 100.0 * numerator as f32 / denominator as f32;
assert!(
(target_acc - acc).abs() < 1.0,
"Expected: {} | Actual: {}",
target_acc,
acc
);
}
#[test]
fn osu_accuracy_and_n50() {
let map = Beatmap::default();
let total_objects = 1234;
let target_acc = 97.5;
let n50 = 30;
let calculator = OsuPP::new(&map)
.passed_objects(total_objects)
.n50(n50)
.accuracy(target_acc);
assert!(
(calculator.n50.unwrap() as i32 - n50 as i32).abs() <= 4,
"Expected: {} | Actual: {}",
n50,
calculator.n50.unwrap()
);
let numerator = 6 * calculator.n300.unwrap_or(0)
+ 2 * calculator.n100.unwrap_or(0)
+ calculator.n50.unwrap_or(0);
let denominator = 6 * total_objects;
let acc = 100.0 * numerator as f32 / denominator as f32;
assert!(
(target_acc - acc).abs() < 1.0,
"Expected: {} | Actual: {}",
target_acc,
acc
);
}
#[test]
fn osu_missing_objects() {
let map = Beatmap::default();
let total_objects = 1234;
let n300 = 1000;
let n100 = 200;
let n50 = 30;
let mut calculator = OsuPP::new(&map)
.passed_objects(total_objects)
.n300(n300)
.n100(n100)
.n50(n50);
calculator.assert_hitresults();
let n_objects = calculator.n300.unwrap()
+ calculator.n100.unwrap()
+ calculator.n50.unwrap()
+ calculator.n_misses;
assert_eq!(
total_objects, n_objects,
"Expected: {} | Actual: {}",
total_objects, n_objects
);
}
}
+3 -9
View File
@@ -22,13 +22,7 @@ const NORMALIZED_RADIUS: f32 = 52.0;
/// processing stack leniency is relatively expensive. /// processing stack leniency is relatively expensive.
/// ///
/// In case of a partial play, e.g. a fail, one can specify the amount of passed objects. /// In case of a partial play, e.g. a fail, one can specify the amount of passed objects.
pub fn stars( pub fn stars(map: &Beatmap, mods: GameMods) -> OsuDifficultyAttributes {
map: &Beatmap,
mods: GameMods,
passed_objects: Option<usize>,
) -> OsuDifficultyAttributes {
let take = passed_objects.unwrap_or(map.hit_objects.len());
let map_attributes = map.attributes().mods(mods).build(); let map_attributes = map.attributes().mods(mods).build();
let mut diff_attributes = OsuDifficultyAttributes { let mut diff_attributes = OsuDifficultyAttributes {
@@ -38,7 +32,7 @@ pub fn stars(
..Default::default() ..Default::default()
}; };
if take < 2 { if map.hit_objects.len() < 2 {
return diff_attributes; return diff_attributes;
} }
@@ -54,7 +48,7 @@ pub fn stars(
let mut ticks_buf = Vec::new(); let mut ticks_buf = Vec::new();
let mut curve_bufs = CurveBuffers::default(); let mut curve_bufs = CurveBuffers::default();
let mut hit_objects = map.hit_objects.iter().take(take).filter_map(|h| { let mut hit_objects = map.hit_objects.iter().filter_map(|h| {
Some(OsuObject::new( Some(OsuObject::new(
h, h,
map, map,