apply some rosu-pp-older changes

This commit is contained in:
tsunyoku
2024-10-13 15:42:13 +01:00
parent 897aa6ccfc
commit 3663c8e11c
2 changed files with 26 additions and 137 deletions
+23 -128
View File
@@ -33,14 +33,13 @@ pub struct OsuPP<'m> {
map: &'m Beatmap,
attributes: Option<OsuDifficultyAttributes>,
mods: GameMods,
combo: Option<usize>,
combo: Option<u32>,
acc: Option<f32>,
n300: Option<usize>,
n100: Option<usize>,
n50: Option<usize>,
n_misses: usize,
passed_objects: Option<usize>,
n300: Option<u32>,
n100: Option<u32>,
n50: Option<u32>,
n_misses: u32,
}
impl<'m> OsuPP<'m> {
@@ -53,12 +52,10 @@ impl<'m> OsuPP<'m> {
mods: GameMods::default(),
combo: None,
acc: None,
n300: None,
n100: None,
n50: None,
n_misses: 0,
passed_objects: None,
}
}
@@ -70,7 +67,7 @@ impl<'m> OsuPP<'m> {
#[inline]
pub fn attributes(mut self, attributes: impl OsuAttributeProvider) -> Self {
if let Some(attributes) = attributes.attributes() {
self.attributes.replace(attributes);
self.attributes = Some(attributes);
}
self
@@ -88,58 +85,50 @@ impl<'m> OsuPP<'m> {
/// Specify the max combo of the play.
#[inline]
pub fn combo(mut self, combo: usize) -> Self {
self.combo.replace(combo);
pub fn combo(mut self, combo: u32) -> Self {
self.combo = Some(combo);
self
}
/// Specify the amount of 300s of a play.
#[inline]
pub fn n300(mut self, n300: usize) -> Self {
self.n300.replace(n300);
pub fn n300(mut self, n300: u32) -> Self {
self.n300 = Some(n300);
self
}
/// Specify the amount of 100s of a play.
#[inline]
pub fn n100(mut self, n100: usize) -> Self {
self.n100.replace(n100);
pub fn n100(mut self, n100: u32) -> Self {
self.n100 = Some(n100);
self
}
/// Specify the amount of 50s of a play.
#[inline]
pub fn n50(mut self, n50: usize) -> Self {
self.n50.replace(n50);
pub fn n50(mut self, n50: u32) -> Self {
self.n50 = Some(n50);
self
}
/// Specify the amount of misses of a play.
#[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
}
/// 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`.
///
/// Be sure to set `misses` beforehand!
/// In case of a partial play, be also sure to set `passed_objects` beforehand!
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;
@@ -150,7 +139,7 @@ impl<'m> OsuPP<'m> {
let placed_points = 2 * n100 + n50 + self.n_misses;
let missing_objects = n_objects - n100 - n50 - self.n_misses;
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);
n50 += missing_objects - n300;
@@ -170,7 +159,7 @@ impl<'m> OsuPP<'m> {
self.n50.replace(n50);
} else {
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 mut n300 = delta / 5;
@@ -198,7 +187,7 @@ impl<'m> OsuPP<'m> {
fn assert_hitresults(&mut self) {
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
.saturating_sub(self.n300.unwrap_or(0))
@@ -234,7 +223,7 @@ impl<'m> OsuPP<'m> {
/// containing stars and other attributes.
pub fn calculate(mut self) -> OsuPerformanceAttributes {
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);
}
@@ -481,8 +470,8 @@ impl<'m> OsuPP<'m> {
}
#[inline]
fn total_hits(&self) -> usize {
let n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len());
fn total_hits(&self) -> u32 {
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)
.min(n_objects)
@@ -501,7 +490,7 @@ impl<'m> OsuPP<'m> {
let mut combo_based_miss_count = 0.0;
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 n50 = self.n50.unwrap_or(0) as f32;
@@ -536,97 +525,3 @@ impl OsuAttributeProvider for OsuPerformanceAttributes {
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.
///
/// In case of a partial play, e.g. a fail, one can specify the amount of passed objects.
pub fn stars(
map: &Beatmap,
mods: GameMods,
passed_objects: Option<usize>,
) -> OsuDifficultyAttributes {
let take = passed_objects.unwrap_or(map.hit_objects.len());
pub fn stars(map: &Beatmap, mods: GameMods) -> OsuDifficultyAttributes {
let map_attributes = map.attributes().mods(mods).build();
let mut diff_attributes = OsuDifficultyAttributes {
@@ -38,7 +32,7 @@ pub fn stars(
..Default::default()
};
if take < 2 {
if map.hit_objects.len() < 2 {
return diff_attributes;
}
@@ -54,7 +48,7 @@ pub fn stars(
let mut ticks_buf = Vec::new();
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(
h,
map,