add max_combo to taiko difficulty attributes

This commit is contained in:
Yentis
2021-11-20 17:53:53 +01:00
parent 7b3294eda1
commit e6b1a5424f
2 changed files with 33 additions and 39 deletions
+6 -4
View File
@@ -38,9 +38,10 @@ pub fn stars(
passed_objects: Option<usize>,
) -> TaikoDifficultyAttributes {
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
let max_combo = map.n_circles as usize;
if take < 2 {
return TaikoDifficultyAttributes { stars: 0.0 };
return TaikoDifficultyAttributes { stars: 0.0, max_combo };
}
// True if the object at that index is stamina cheese
@@ -120,7 +121,7 @@ pub fn stars(
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
@@ -264,13 +265,14 @@ fn norm(p: f64, a: f64, b: f64, c: f64) -> f64 {
}
/// The result of a difficulty calculation on an osu!taiko map.
#[derive(Copy, Clone, Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct TaikoDifficultyAttributes {
pub stars: f64,
pub max_combo: usize,
}
/// The result of a performance calculation on an osu!taiko map.
#[derive(Copy, Clone, Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct TaikoPerformanceAttributes {
pub attributes: TaikoDifficultyAttributes,
pub pp: f64,
+27 -35
View File
@@ -32,9 +32,8 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
#[allow(clippy::upper_case_acronyms)]
pub struct TaikoPP<'m> {
map: &'m Beatmap,
stars: Option<f64>,
attributes: Option<TaikoDifficultyAttributes>,
mods: u32,
max_combo: usize,
combo: Option<usize>,
acc: f64,
n_misses: usize,
@@ -49,9 +48,8 @@ impl<'m> TaikoPP<'m> {
pub fn new(map: &'m Beatmap) -> Self {
Self {
map,
stars: None,
attributes: None,
mods: 0,
max_combo: map.n_circles as usize,
combo: None,
acc: 1.0,
n_misses: 0,
@@ -66,8 +64,8 @@ impl<'m> TaikoPP<'m> {
/// be sure to put them in here so that they don't have to be recalculated.
#[inline]
pub fn attributes(mut self, attributes: impl TaikoAttributeProvider) -> Self {
if let Some(stars) = attributes.attributes() {
self.stars = Some(stars);
if let Some(attributes) = attributes.attributes() {
self.attributes.replace(attributes);
}
self
@@ -135,9 +133,10 @@ impl<'m> TaikoPP<'m> {
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
let stars = self
.stars
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars);
let attributes = self
.attributes
.take()
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects));
if self.n300.or(self.n100).is_some() {
let total = self.map.n_circles as usize;
@@ -161,9 +160,8 @@ impl<'m> TaikoPP<'m> {
let inner = TaikoPPInner {
map: self.map,
stars,
attributes,
mods: self.mods,
max_combo: self.max_combo,
acc: self.acc,
n_misses: self.n_misses,
};
@@ -174,9 +172,8 @@ impl<'m> TaikoPP<'m> {
struct TaikoPPInner<'m> {
map: &'m Beatmap,
stars: f64,
attributes: TaikoDifficultyAttributes,
mods: u32,
max_combo: usize,
acc: f64,
n_misses: usize,
}
@@ -193,25 +190,26 @@ impl<'m> TaikoPPInner<'m> {
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 pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
TaikoPerformanceAttributes {
attributes: TaikoDifficultyAttributes { stars: self.stars },
attributes: self.attributes,
pp,
pp_acc: acc_value,
pp_strain: strain_value,
}
}
fn compute_strain_value(&self, stars: f64) -> f64 {
let exp_base = 5.0 * (stars / 0.0075).max(1.0) - 4.0;
fn compute_strain_value(&self) -> f64 {
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;
// 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;
// Penalize misses exponentially
@@ -242,11 +240,12 @@ impl<'m> TaikoPPInner<'m> {
}
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)
* self.acc.powi(15)
* 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)
}
}
@@ -257,36 +256,29 @@ fn difficulty_range_od(od: f64) -> f64 {
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait TaikoAttributeProvider {
fn attributes(self) -> Option<f64>;
}
impl TaikoAttributeProvider for f64 {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self)
}
fn attributes(self) -> Option<TaikoDifficultyAttributes>;
}
impl TaikoAttributeProvider for TaikoDifficultyAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self.stars)
fn attributes(self) -> Option<TaikoDifficultyAttributes> {
Some(self)
}
}
impl TaikoAttributeProvider for TaikoPerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self.attributes.stars)
fn attributes(self) -> Option<TaikoDifficultyAttributes> {
Some(self.attributes)
}
}
impl TaikoAttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
fn attributes(self) -> Option<TaikoDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Taiko(attributes) = self {
Some(attributes.stars)
Some(attributes)
} else {
None
}
@@ -295,10 +287,10 @@ impl TaikoAttributeProvider for DifficultyAttributes {
impl TaikoAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
fn attributes(self) -> Option<TaikoDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Taiko(attributes) = self {
Some(attributes.attributes.stars)
Some(attributes.attributes)
} else {
None
}