apply some rosu-pp-older changes
This commit is contained in:
+23
-128
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user