Merge branch 'Yentis-main' into next

This commit is contained in:
MaxOhn
2021-11-21 02:31:28 +01:00
3 changed files with 42 additions and 41 deletions
+1 -1
View File
@@ -407,7 +407,7 @@ impl PerformanceAttributes {
#[cfg(feature = "osu")] #[cfg(feature = "osu")]
Self::Osu(attributes) => DifficultyAttributes::Osu(attributes.attributes.clone()), Self::Osu(attributes) => DifficultyAttributes::Osu(attributes.attributes.clone()),
#[cfg(feature = "taiko")] #[cfg(feature = "taiko")]
Self::Taiko(attributes) => DifficultyAttributes::Taiko(attributes.attributes), Self::Taiko(attributes) => DifficultyAttributes::Taiko(attributes.attributes.clone()),
} }
} }
} }
+13 -4
View File
@@ -37,9 +37,16 @@ pub fn stars(
mods: impl Mods, mods: impl Mods,
passed_objects: Option<usize>, passed_objects: Option<usize>,
) -> TaikoDifficultyAttributes { ) -> TaikoDifficultyAttributes {
let max_combo = map.n_circles as usize;
let skills = match calculate_skills(map, mods, passed_objects) { let skills = match calculate_skills(map, mods, passed_objects) {
Some(skills) => skills, Some(skills) => skills,
None => return TaikoDifficultyAttributes { stars: 0.0 }, None => {
return TaikoDifficultyAttributes {
max_combo,
..Default::default()
}
}
}; };
let mut buf = vec![0.0; skills[0].strain_peaks.len()]; let mut buf = vec![0.0; skills[0].strain_peaks.len()];
@@ -59,7 +66,7 @@ pub fn stars(
let stars = rescale(1.4 * separate_rating + 0.5 * combined_rating); let stars = rescale(1.4 * separate_rating + 0.5 * combined_rating);
TaikoDifficultyAttributes { stars } TaikoDifficultyAttributes { stars, max_combo }
} }
/// Essentially the same as the [`stars`] function but instead of /// Essentially the same as the [`stars`] function but instead of
@@ -219,14 +226,16 @@ fn norm(p: f64, a: f64, b: f64, c: f64) -> f64 {
} }
/// The result of a difficulty calculation on an osu!taiko map. /// The result of a difficulty calculation on an osu!taiko map.
#[derive(Copy, Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct TaikoDifficultyAttributes { pub struct TaikoDifficultyAttributes {
/// The final star rating. /// The final star rating.
pub stars: f64, pub stars: f64,
/// The maximum combo.
pub max_combo: usize,
} }
/// The result of a performance calculation on an osu!taiko map. /// The result of a performance calculation on an osu!taiko map.
#[derive(Copy, Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct TaikoPerformanceAttributes { pub struct TaikoPerformanceAttributes {
/// The difficulty attributes that were used for the performance calculation /// The difficulty attributes that were used for the performance calculation
pub attributes: TaikoDifficultyAttributes, pub attributes: TaikoDifficultyAttributes,
+28 -36
View File
@@ -34,9 +34,8 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
pub struct TaikoPP<'map> { pub struct TaikoPP<'map> {
map: &'map Beatmap, map: &'map Beatmap,
stars: Option<f64>, attributes: Option<TaikoDifficultyAttributes>,
mods: u32, mods: u32,
max_combo: usize,
combo: Option<usize>, combo: Option<usize>,
acc: f64, acc: f64,
n_misses: usize, n_misses: usize,
@@ -52,9 +51,8 @@ impl<'map> TaikoPP<'map> {
pub fn new(map: &'map Beatmap) -> Self { pub fn new(map: &'map Beatmap) -> Self {
Self { Self {
map, map,
stars: None, attributes: None,
mods: 0, mods: 0,
max_combo: map.n_circles as usize,
combo: None, combo: None,
acc: 1.0, acc: 1.0,
n_misses: 0, n_misses: 0,
@@ -69,8 +67,8 @@ impl<'map> TaikoPP<'map> {
/// be sure to put them in here so that they don't have to be recalculated. /// be sure to put them in here so that they don't have to be recalculated.
#[inline] #[inline]
pub fn attributes(mut self, attributes: impl TaikoAttributeProvider) -> Self { pub fn attributes(mut self, attributes: impl TaikoAttributeProvider) -> Self {
if let Some(stars) = attributes.attributes() { if let Some(attributes) = attributes.attributes() {
self.stars = Some(stars); self.attributes.replace(attributes);
} }
self self
@@ -138,9 +136,10 @@ impl<'map> TaikoPP<'map> {
/// Calculate all performance related values, including pp and stars. /// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> TaikoPerformanceAttributes { pub fn calculate(mut self) -> TaikoPerformanceAttributes {
let stars = self let attributes = self
.stars .attributes
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars); .take()
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects));
if self.n300.or(self.n100).is_some() { if self.n300.or(self.n100).is_some() {
let total = self.map.n_circles as usize; let total = self.map.n_circles as usize;
@@ -164,9 +163,8 @@ impl<'map> TaikoPP<'map> {
let inner = TaikoPPInner { let inner = TaikoPPInner {
map: self.map, map: self.map,
stars, attributes,
mods: self.mods, mods: self.mods,
max_combo: self.max_combo,
acc: self.acc, acc: self.acc,
n_misses: self.n_misses, n_misses: self.n_misses,
}; };
@@ -177,9 +175,8 @@ impl<'map> TaikoPP<'map> {
struct TaikoPPInner<'map> { struct TaikoPPInner<'map> {
map: &'map Beatmap, map: &'map Beatmap,
stars: f64, attributes: TaikoDifficultyAttributes,
mods: u32, mods: u32,
max_combo: usize,
acc: f64, acc: f64,
n_misses: usize, n_misses: usize,
} }
@@ -196,25 +193,26 @@ impl<'map> TaikoPPInner<'map> {
multiplier *= 1.1; multiplier *= 1.1;
} }
let strain_value = self.compute_strain_value(self.stars); let strain_value = self.compute_strain_value();
let acc_value = self.compute_accuracy_value(); let acc_value = self.compute_accuracy_value();
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier; let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
TaikoPerformanceAttributes { TaikoPerformanceAttributes {
attributes: TaikoDifficultyAttributes { stars: self.stars }, attributes: self.attributes,
pp, pp,
pp_acc: acc_value, pp_acc: acc_value,
pp_strain: strain_value, pp_strain: strain_value,
} }
} }
fn compute_strain_value(&self, stars: f64) -> f64 { fn compute_strain_value(&self) -> f64 {
let exp_base = 5.0 * (stars / 0.0075).max(1.0) - 4.0; let attributes = &self.attributes;
let exp_base = 5.0 * (attributes.stars / 0.0075).max(1.0) - 4.0;
let mut strain = exp_base * exp_base / 100_000.0; let mut strain = exp_base * exp_base / 100_000.0;
// Longer maps are worth more // Longer maps are worth more
let len_bonus = 1.0 + 0.1 * (self.max_combo as f64 / 1500.0).min(1.0); let len_bonus = 1.0 + 0.1 * (attributes.max_combo as f64 / 1500.0).min(1.0);
strain *= len_bonus; strain *= len_bonus;
// Penalize misses exponentially // Penalize misses exponentially
@@ -245,11 +243,12 @@ impl<'map> TaikoPPInner<'map> {
} }
let hit_window = difficulty_range_od(od).floor() / self.mods.speed(); let hit_window = difficulty_range_od(od).floor() / self.mods.speed();
let max_combo = self.attributes.max_combo;
(150.0 / hit_window).powf(1.1) (150.0 / hit_window).powf(1.1)
* self.acc.powi(15) * self.acc.powi(15)
* 22.0 * 22.0
* (self.max_combo as f64 / 1500.0).powf(0.3).min(1.15) * (max_combo as f64 / 1500.0).powf(0.3).min(1.15)
} }
} }
@@ -260,37 +259,30 @@ fn difficulty_range_od(od: f64) -> f64 {
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation. /// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait TaikoAttributeProvider { pub trait TaikoAttributeProvider {
/// Provide the star rating (only difficulty attribute for osu!taiko). /// Provide the actual difficulty attributes.
fn attributes(self) -> Option<f64>; fn attributes(self) -> Option<TaikoDifficultyAttributes>;
}
impl TaikoAttributeProvider for f64 {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self)
}
} }
impl TaikoAttributeProvider for TaikoDifficultyAttributes { impl TaikoAttributeProvider for TaikoDifficultyAttributes {
#[inline] #[inline]
fn attributes(self) -> Option<f64> { fn attributes(self) -> Option<TaikoDifficultyAttributes> {
Some(self.stars) Some(self)
} }
} }
impl TaikoAttributeProvider for TaikoPerformanceAttributes { impl TaikoAttributeProvider for TaikoPerformanceAttributes {
#[inline] #[inline]
fn attributes(self) -> Option<f64> { fn attributes(self) -> Option<TaikoDifficultyAttributes> {
Some(self.attributes.stars) Some(self.attributes)
} }
} }
impl TaikoAttributeProvider for DifficultyAttributes { impl TaikoAttributeProvider for DifficultyAttributes {
#[inline] #[inline]
fn attributes(self) -> Option<f64> { fn attributes(self) -> Option<TaikoDifficultyAttributes> {
#[allow(irrefutable_let_patterns)] #[allow(irrefutable_let_patterns)]
if let Self::Taiko(attributes) = self { if let Self::Taiko(attributes) = self {
Some(attributes.stars) Some(attributes)
} else { } else {
None None
} }
@@ -299,10 +291,10 @@ impl TaikoAttributeProvider for DifficultyAttributes {
impl TaikoAttributeProvider for PerformanceAttributes { impl TaikoAttributeProvider for PerformanceAttributes {
#[inline] #[inline]
fn attributes(self) -> Option<f64> { fn attributes(self) -> Option<TaikoDifficultyAttributes> {
#[allow(irrefutable_let_patterns)] #[allow(irrefutable_let_patterns)]
if let Self::Taiko(attributes) = self { if let Self::Taiko(attributes) = self {
Some(attributes.attributes.stars) Some(attributes.attributes)
} else { } else {
None None
} }