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