renamed PpResult+StarResult, minor structural adjustments, documentation update

This commit is contained in:
MaxOhn
2021-11-13 17:16:31 +01:00
parent 092034a6d5
commit 8beabd4e11
14 changed files with 216 additions and 183 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
- [BREAKING] With the importance of sliders for osu!standard, the `no_sliders_no_leniency` feature became too inaccurate. Additionally, since considering sliders now inherently drags performance down a little more, the difference between `no_leniency` and `all_included` became too small. Hence, the three osu features `no_sliders_no_leniency`, `no_leniency`, and `all_included` were removed. When the `osu` feature is enabled, it will now essentially use `all_included` under the hood.
Additionally, instead of importing through `rosu_pp::osu::{version}`, you now have to import through `rosu_pp::osu`.
- [BREAKING] Instead of returning `PpResult`, performance calculations now return `PerformanceAttributes` depending on the mode.
- [BREAKING] Instead of returning `StarResult`, difficulty calculations now return `DifficultyAttributes` depending on the mode.
- [BREAKING] Instead of returning `PpResult`, performance calculations now return `{Mode}PerformanceAttributes` and `PpResult` has been renamed to `PerformanceAttributes`.
- [BREAKING] Instead of returning `StarResult`, difficulty calculations now return `{Mode}DifficultyAttributes` and `StarResult` has been renamed to `DifficultyAttributes`.
- [BREAKING] Various fields and methods now include `f64` instead of `f32` to stay true to osu!'s original code
- added internal binary crate `pp-gen` to calculate difficulty & pp values via `PerformanceCalculator.dll`
- osu: Updated up to commit [9fb2402781ad91c197d51aeec716b0000f52c4d1](https://github.com/ppy/osu/commit/9fb2402781ad91c197d51aeec716b0000f52c4d1) (2021-11-12)
-2
View File
@@ -27,8 +27,6 @@ let map = match Beatmap::parse(file) {
// If `BeatmapExt` is included, you can make use of
// some methods on `Beatmap` to make your life simpler.
// If the mode is known, it is recommended to use the
// mode's pp calculator, e.g. `TaikoPP`, manually.
let result = map.pp()
.mods(24) // HDHR
.combo(1234)
+11 -12
View File
@@ -27,16 +27,16 @@ const CATCHER_SIZE: f32 = 106.75;
const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
const BASE_SCORING_DISTANCE: f64 = 100.0;
/// Star calculation for osu!ctb maps
/// Difficulty calculation for osu!ctb maps.
///
/// In case of a partial play, e.g. a fail, one can specify the amount of passed objects.
pub fn stars(
map: &Beatmap,
mods: impl Mods,
passed_objects: Option<usize>,
) -> DifficultyAttributes {
) -> FruitsDifficultyAttributes {
if map.hit_objects.len() < 2 {
return DifficultyAttributes::default();
return FruitsDifficultyAttributes::default();
}
let take = passed_objects.unwrap_or(usize::MAX);
@@ -279,7 +279,7 @@ pub fn stars(
let stars = movement.difficulty_value().sqrt() * STAR_SCALING_FACTOR;
DifficultyAttributes {
FruitsDifficultyAttributes {
stars,
ar: attributes.ar,
n_fruits: fruits,
@@ -289,7 +289,7 @@ pub fn stars(
}
}
/// Essentially the same as the `stars` function but instead of
/// Essentially the same as the [`stars`] function but instead of
/// evaluating the final strains, it just returns them as is.
///
/// Suitable to plot the difficulty of a map over time.
@@ -629,10 +629,9 @@ impl<I: Iterator<Item = CatchObject>> Iterator for FruitOrJuice<I> {
}
}
/// Various data created through the star calculation.
/// This data is necessary to calculate PP.
/// The result of a difficulty calculation on an osu!ctb map.
#[derive(Clone, Debug, Default)]
pub struct DifficultyAttributes {
pub struct FruitsDifficultyAttributes {
pub stars: f64,
pub max_combo: usize,
pub ar: f64,
@@ -641,14 +640,14 @@ pub struct DifficultyAttributes {
pub n_tiny_droplets: usize,
}
/// Various data created through the pp calculation.
/// The result of a performance calculation on an osu!ctb map.
#[derive(Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub struct FruitsPerformanceAttributes {
pub attributes: FruitsDifficultyAttributes,
pub pp: f64,
}
impl PerformanceAttributes {
impl FruitsPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
+22 -23
View File
@@ -1,7 +1,7 @@
use super::{stars, DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
use super::{stars, FruitsDifficultyAttributes, FruitsPerformanceAttributes};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// Calculator for pp on osu!ctb maps.
/// Performance calculator on osu!ctb maps.
///
/// # Example
///
@@ -32,7 +32,7 @@ use crate::{Beatmap, Mods, PpResult, StarResult};
#[allow(clippy::upper_case_acronyms)]
pub struct FruitsPP<'m> {
map: &'m Beatmap,
attributes: Option<DifficultyAttributes>,
attributes: Option<FruitsDifficultyAttributes>,
mods: u32,
combo: Option<usize>,
@@ -62,9 +62,7 @@ impl<'m> FruitsPP<'m> {
}
}
/// [`FruitsAttributeProvider`] is implemented by [`DifficultyAttributes`](crate::fruits::DifficultyAttributes),
/// [`StarResult`](crate::StarResult), and by [`PpResult`](crate::PpResult) meaning you can give the
/// result of a star calculation or a pp calculation.
/// Provide the result of previous a difficulty or performance calculation.
/// If you already calculated the attributes for the current map-mod combination,
/// be sure to put them in here so that they don't have to be recalculated.
#[inline]
@@ -182,7 +180,7 @@ impl<'m> FruitsPP<'m> {
self
}
fn assert_hitresults(self, attributes: DifficultyAttributes) -> FruitsPPInner {
fn assert_hitresults(self, attributes: FruitsDifficultyAttributes) -> FruitsPPInner {
let correct_combo_hits = self
.n_fruits
.and_then(|f| self.n_droplets.map(|d| f + d + self.n_misses))
@@ -253,7 +251,7 @@ impl<'m> FruitsPP<'m> {
}
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> PerformanceAttributes {
pub fn calculate(mut self) -> FruitsPerformanceAttributes {
let attributes = self
.attributes
.take()
@@ -264,7 +262,7 @@ impl<'m> FruitsPP<'m> {
}
struct FruitsPPInner {
attributes: DifficultyAttributes,
attributes: FruitsDifficultyAttributes,
mods: u32,
combo: Option<usize>,
n_fruits: usize,
@@ -275,7 +273,7 @@ struct FruitsPPInner {
}
impl FruitsPPInner {
fn calculate(self) -> PerformanceAttributes {
fn calculate(self) -> FruitsPerformanceAttributes {
let attributes = &self.attributes;
let stars = attributes.stars;
@@ -337,7 +335,7 @@ impl FruitsPPInner {
pp *= 0.9;
}
PerformanceAttributes {
FruitsPerformanceAttributes {
attributes: self.attributes,
pp,
}
@@ -372,27 +370,28 @@ impl FruitsPPInner {
}
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait FruitsAttributeProvider {
fn attributes(self) -> Option<DifficultyAttributes>;
fn attributes(self) -> Option<FruitsDifficultyAttributes>;
}
impl FruitsAttributeProvider for DifficultyAttributes {
impl FruitsAttributeProvider for FruitsDifficultyAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
Some(self)
}
}
impl FruitsAttributeProvider for PerformanceAttributes {
impl FruitsAttributeProvider for FruitsPerformanceAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
Some(self.attributes)
}
}
impl FruitsAttributeProvider for StarResult {
impl FruitsAttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Fruits(attributes) = self {
Some(attributes)
@@ -402,9 +401,9 @@ impl FruitsAttributeProvider for StarResult {
}
}
impl FruitsAttributeProvider for PpResult {
impl FruitsAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Fruits(attributes) = self {
Some(attributes.attributes)
@@ -419,8 +418,8 @@ mod test {
use super::*;
use crate::Beatmap;
fn attributes() -> DifficultyAttributes {
DifficultyAttributes {
fn attributes() -> FruitsDifficultyAttributes {
FruitsDifficultyAttributes {
n_fruits: 1234,
n_droplets: 567,
n_tiny_droplets: 2345,
+46 -34
View File
@@ -25,8 +25,6 @@
//!
//! // If `BeatmapExt` is included, you can make use of
//! // some methods on `Beatmap` to make your life simpler.
//! // If the mode is known, it is recommended to use the
//! // mode's pp calculator, e.g. `TaikoPP`, manually.
//! let result = map.pp()
//! .mods(24) // HDHR
//! .combo(1234)
@@ -153,17 +151,17 @@ pub use taiko::TaikoPP;
pub use mods::Mods;
pub use parse::{Beatmap, BeatmapAttributes, GameMode, ParseError, ParseResult};
/// Provides some additional methods on [`Beatmap`](crate::Beatmap).
pub trait BeatmapExt {
/// Calculate the stars and other attributes of a beatmap which are required for pp calculation.
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> StarResult;
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> DifficultyAttributes;
/// Calculate the max pp of a beatmap.
///
/// If you seek more fine-tuning and options you need to match on the map's
/// mode and use the mode's corresponding calculator, e.g. [`TaikoPP`](crate::TaikoPP) for taiko.
fn max_pp(&self, mods: u32) -> PpResult;
/// If you seek more fine-tuning you can use the [`pp`](BeatmapExt::pp) method.
fn max_pp(&self, mods: u32) -> PerformanceAttributes;
/// Returns a builder to calculate pp and difficulty values.
/// Returns a builder for performance calculation.
///
/// Convenient method that matches on the map's mode to choose the appropriate calculator.
fn pp(&self) -> AnyPP<'_>;
@@ -177,68 +175,68 @@ pub trait BeatmapExt {
}
impl BeatmapExt for Beatmap {
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> StarResult {
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> DifficultyAttributes {
match self.mode {
GameMode::STD => {
#[cfg(not(feature = "osu"))]
panic!("`osu` feature is not enabled");
#[cfg(feature = "osu")]
StarResult::Osu(osu::stars(self, mods, passed_objects))
DifficultyAttributes::Osu(osu::stars(self, mods, passed_objects))
}
GameMode::MNA => {
#[cfg(not(feature = "mania"))]
panic!("`mania` feature is not enabled");
#[cfg(feature = "mania")]
StarResult::Mania(mania::stars(self, mods, passed_objects))
DifficultyAttributes::Mania(mania::stars(self, mods, passed_objects))
}
GameMode::TKO => {
#[cfg(not(feature = "taiko"))]
panic!("`taiko` feature is not enabled");
#[cfg(feature = "taiko")]
StarResult::Taiko(taiko::stars(self, mods, passed_objects))
DifficultyAttributes::Taiko(taiko::stars(self, mods, passed_objects))
}
GameMode::CTB => {
#[cfg(not(feature = "fruits"))]
panic!("`fruits` feature is not enabled");
#[cfg(feature = "fruits")]
StarResult::Fruits(fruits::stars(self, mods, passed_objects))
DifficultyAttributes::Fruits(fruits::stars(self, mods, passed_objects))
}
}
}
fn max_pp(&self, mods: u32) -> PpResult {
fn max_pp(&self, mods: u32) -> PerformanceAttributes {
match self.mode {
GameMode::STD => {
#[cfg(not(feature = "osu"))]
panic!("`osu` feature is not enabled");
#[cfg(feature = "osu")]
PpResult::Osu(OsuPP::new(self).mods(mods).calculate())
PerformanceAttributes::Osu(OsuPP::new(self).mods(mods).calculate())
}
GameMode::MNA => {
#[cfg(not(feature = "mania"))]
panic!("`mania` feature is not enabled");
#[cfg(feature = "mania")]
PpResult::Mania(ManiaPP::new(self).mods(mods).calculate())
PerformanceAttributes::Mania(ManiaPP::new(self).mods(mods).calculate())
}
GameMode::TKO => {
#[cfg(not(feature = "taiko"))]
panic!("`taiko` feature is not enabled");
#[cfg(feature = "taiko")]
PpResult::Taiko(TaikoPP::new(self).mods(mods).calculate())
PerformanceAttributes::Taiko(TaikoPP::new(self).mods(mods).calculate())
}
GameMode::CTB => {
#[cfg(not(feature = "fruits"))]
panic!("`fruits` feature is not enabled");
#[cfg(feature = "fruits")]
PpResult::Fruits(FruitsPP::new(self).mods(mods).calculate())
PerformanceAttributes::Fruits(FruitsPP::new(self).mods(mods).calculate())
}
}
}
@@ -294,21 +292,21 @@ pub struct Strains {
pub strains: Vec<f64>,
}
/// Basic enum containing the result of a star calculation based on the mode.
/// The result of a difficulty calculation based on the mode.
#[derive(Clone, Debug)]
pub enum StarResult {
pub enum DifficultyAttributes {
#[cfg(feature = "fruits")]
Fruits(fruits::DifficultyAttributes),
Fruits(fruits::FruitsDifficultyAttributes),
#[cfg(feature = "mania")]
Mania(mania::DifficultyAttributes),
Mania(mania::ManiaDifficultyAttributes),
#[cfg(feature = "osu")]
Osu(osu::DifficultyAttributes),
Osu(osu::OsuDifficultyAttributes),
#[cfg(feature = "taiko")]
Taiko(taiko::DifficultyAttributes),
Taiko(taiko::TaikoDifficultyAttributes),
}
impl StarResult {
/// The final star value.
impl DifficultyAttributes {
/// The star value.
#[inline]
pub fn stars(&self) -> f64 {
match self {
@@ -324,21 +322,21 @@ impl StarResult {
}
}
/// Basic struct containing the result of a PP calculation.
/// The result of a performance calculation based on the mode.
#[derive(Clone, Debug)]
pub enum PpResult {
pub enum PerformanceAttributes {
#[cfg(feature = "fruits")]
Fruits(fruits::PerformanceAttributes),
Fruits(fruits::FruitsPerformanceAttributes),
#[cfg(feature = "mania")]
Mania(mania::PerformanceAttributes),
Mania(mania::ManiaPerformanceAttributes),
#[cfg(feature = "osu")]
Osu(osu::PerformanceAttributes),
Osu(osu::OsuPerformanceAttributes),
#[cfg(feature = "taiko")]
Taiko(taiko::PerformanceAttributes),
Taiko(taiko::TaikoPerformanceAttributes),
}
impl PpResult {
/// The final pp value.
impl PerformanceAttributes {
/// The pp value.
#[inline]
pub fn pp(&self) -> f64 {
match self {
@@ -353,7 +351,7 @@ impl PpResult {
}
}
/// The final star value.
/// The star value.
#[inline]
pub fn stars(&self) -> f64 {
match self {
@@ -367,6 +365,20 @@ impl PpResult {
Self::Taiko(attributes) => attributes.stars(),
}
}
#[inline]
pub fn difficulty_attributes(&self) -> DifficultyAttributes {
match self {
#[cfg(feature = "fruits")]
Self::Fruits(attributes) => DifficultyAttributes::Fruits(attributes.attributes.clone()),
#[cfg(feature = "mania")]
Self::Mania(attributes) => DifficultyAttributes::Mania(attributes.attributes),
#[cfg(feature = "osu")]
Self::Osu(attributes) => DifficultyAttributes::Osu(attributes.attributes.clone()),
#[cfg(feature = "taiko")]
Self::Taiko(attributes) => DifficultyAttributes::Taiko(attributes.attributes),
}
}
}
#[cfg(any(feature = "osu", feature = "taiko"))]
+11 -12
View File
@@ -11,18 +11,18 @@ use crate::{parse::HitObject, Beatmap, GameMode, Mods, Strains};
const SECTION_LEN: f64 = 400.0;
const STAR_SCALING_FACTOR: f64 = 0.018;
/// Star calculation for osu!mania maps
/// Difficulty calculation for osu!mania maps.
///
/// In case of a partial play, e.g. a fail, one can specify the amount of passed objects.
pub fn stars(
map: &Beatmap,
mods: impl Mods,
passed_objects: Option<usize>,
) -> DifficultyAttributes {
) -> ManiaDifficultyAttributes {
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
if take < 2 {
return DifficultyAttributes::default();
return ManiaDifficultyAttributes::default();
}
let rounded_cs = map.cs.round();
@@ -90,10 +90,10 @@ pub fn stars(
let stars = strain.difficulty_value() * STAR_SCALING_FACTOR;
DifficultyAttributes { stars }
ManiaDifficultyAttributes { stars }
}
/// Essentially the same as the `stars` function but instead of
/// Essentially the same as the [`stars`] function but instead of
/// evaluating the final strains, it just returns them as is.
///
/// Suitable to plot the difficulty of a map over time.
@@ -169,23 +169,22 @@ impl<'o> DifficultyHitObject<'o> {
}
}
/// Various data created through the star calculation.
/// This data is necessary to calculate PP.
/// The result of a difficulty calculation on an osu!mania map.
#[derive(Copy, Clone, Debug, Default)]
pub struct DifficultyAttributes {
pub struct ManiaDifficultyAttributes {
pub stars: f64,
}
/// Various data created through the pp calculation.
/// The result of a performance calculation on an osu!mania map.
#[derive(Copy, Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub struct ManiaPerformanceAttributes {
pub attributes: ManiaDifficultyAttributes,
pub pp_acc: f64,
pub pp_strain: f64,
pub pp: f64,
}
impl PerformanceAttributes {
impl ManiaPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
+12 -13
View File
@@ -1,7 +1,7 @@
use super::{stars, DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
use super::{stars, ManiaDifficultyAttributes, ManiaPerformanceAttributes};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// Calculator for pp on osu!mania maps.
/// Performance calculator on osu!mania maps.
///
/// # Example
///
@@ -48,9 +48,7 @@ impl<'m> ManiaPP<'m> {
}
}
/// [`ManiaAttributeProvider`] is implemented by `f32`, [`StarResult`](crate::StarResult),
/// and by [`PpResult`](crate::PpResult) meaning you can give the star rating,
/// the result of a star calculation, or the result of a pp calculation.
/// Provide the result of previous a difficulty or performance calculation.
/// If you already calculated the attributes for the current map-mod combination,
/// be sure to put them in here so that they don't have to be recalculated.
#[inline]
@@ -90,7 +88,7 @@ impl<'m> ManiaPP<'m> {
}
/// Calculate all performance related values, including pp and stars.
pub fn calculate(self) -> PerformanceAttributes {
pub fn calculate(self) -> ManiaPerformanceAttributes {
let stars = self
.stars
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars);
@@ -131,8 +129,8 @@ impl<'m> ManiaPP<'m> {
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
PerformanceAttributes {
attributes: DifficultyAttributes { stars },
ManiaPerformanceAttributes {
attributes: ManiaDifficultyAttributes { stars },
pp_acc: acc_value,
pp_strain: strain_value,
pp,
@@ -169,6 +167,7 @@ impl<'m> ManiaPP<'m> {
}
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait ManiaAttributeProvider {
fn attributes(self) -> Option<f64>;
}
@@ -180,21 +179,21 @@ impl ManiaAttributeProvider for f64 {
}
}
impl ManiaAttributeProvider for DifficultyAttributes {
impl ManiaAttributeProvider for ManiaDifficultyAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self.stars)
}
}
impl ManiaAttributeProvider for PerformanceAttributes {
impl ManiaAttributeProvider for ManiaPerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self.attributes.stars)
}
}
impl ManiaAttributeProvider for StarResult {
impl ManiaAttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
#[allow(irrefutable_let_patterns)]
@@ -206,7 +205,7 @@ impl ManiaAttributeProvider for StarResult {
}
}
impl ManiaAttributeProvider for PpResult {
impl ManiaAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
#[allow(irrefutable_let_patterns)]
+1
View File
@@ -7,6 +7,7 @@ macro_rules! impl_mods {
};
}
/// Abstract type to define mods.
pub trait Mods: Copy {
const NF: u32 = 1 << 0;
const EZ: u32 = 1 << 1;
+11 -15
View File
@@ -23,17 +23,14 @@ const DIFFICULTY_MULTIPLIER: f64 = 0.0675;
const NORMALIZED_RADIUS: f32 = 50.0; // * diameter of 100; easier mental maths.
const STACK_DISTANCE: f32 = 3.0;
/// Star calculation for osu!standard maps.
///
/// Slider paths aswell as stack leniency are considered.
/// Both of these drag the performance down but in turn the values are much more accurate
/// Difficulty calculation for osu!standard maps.
///
/// In case of a partial play, e.g. a fail, one can specify the amount of passed objects.
pub fn stars(
map: &Beatmap,
mods: impl Mods,
passed_objects: Option<usize>,
) -> DifficultyAttributes {
) -> OsuDifficultyAttributes {
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
let map_attributes = map.attributes().mods(mods);
@@ -41,7 +38,7 @@ pub fn stars(
let od = (80.0 - hit_window) / 6.0;
if take < 2 {
return DifficultyAttributes {
return OsuDifficultyAttributes {
ar: map_attributes.ar,
hp: map_attributes.hp,
od,
@@ -223,7 +220,7 @@ pub fn stars(
0.0
};
DifficultyAttributes {
OsuDifficultyAttributes {
ar: map_attributes.ar,
hp: map_attributes.hp,
od,
@@ -239,7 +236,7 @@ pub fn stars(
}
}
/// Essentially the same as the `stars` function but instead of
/// Essentially the same as the [`stars`] function but instead of
/// evaluating the final strains, it just returns them as is.
///
/// Suitable to plot the difficulty of a map over time.
@@ -544,10 +541,9 @@ fn difficulty_range_ar(ar: f64) -> f64 {
crate::difficulty_range(ar, 450.0, 1200.0, 1800.0)
}
/// Various data created through the star calculation.
/// This data is necessary to calculate PP.
/// The result of a difficulty calculation on an osu!standard map.
#[derive(Clone, Debug, Default)]
pub struct DifficultyAttributes {
pub struct OsuDifficultyAttributes {
pub aim_strain: f64,
pub speed_strain: f64,
pub flashlight_rating: f64,
@@ -562,10 +558,10 @@ pub struct DifficultyAttributes {
pub max_combo: usize,
}
/// Various data created through the pp calculation.
/// The result of a performance calculation on an osu!standard map.
#[derive(Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub struct OsuPerformanceAttributes {
pub attributes: OsuDifficultyAttributes,
pub pp_acc: f64,
pub pp_aim: f64,
pub pp_flashlight: f64,
@@ -573,7 +569,7 @@ pub struct PerformanceAttributes {
pub pp: f64,
}
impl PerformanceAttributes {
impl OsuPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
+22 -23
View File
@@ -1,7 +1,7 @@
use super::{DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
use super::{OsuDifficultyAttributes, OsuPerformanceAttributes};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// Calculator for pp on osu!standard maps.
/// Performance calculator on osu!standard maps.
///
/// # Example
///
@@ -32,7 +32,7 @@ use crate::{Beatmap, Mods, PpResult, StarResult};
#[allow(clippy::upper_case_acronyms)]
pub struct OsuPP<'m> {
map: &'m Beatmap,
attributes: Option<DifficultyAttributes>,
attributes: Option<OsuDifficultyAttributes>,
mods: u32,
combo: Option<usize>,
acc: Option<f64>,
@@ -62,9 +62,7 @@ impl<'m> OsuPP<'m> {
}
}
/// [`OsuAttributeProvider`] is implemented by [`DifficultyAttributes`](crate::osu::DifficultyAttributes)
/// and by [`PpResult`](crate::PpResult) meaning you can give the
/// result of a star calculation or a pp calculation.
/// Provide the result of previous a difficulty or performance calculation.
/// If you already calculated the attributes for the current map-mod combination,
/// be sure to put them in here so that they don't have to be recalculated.
#[inline]
@@ -199,7 +197,7 @@ impl<'m> OsuPP<'m> {
self
}
fn assert_hitresults(self, attributes: DifficultyAttributes) -> OsuPPInner {
fn assert_hitresults(self, attributes: OsuDifficultyAttributes) -> OsuPPInner {
let mut n300 = self.n300;
let mut n100 = self.n100;
let mut n50 = self.n50;
@@ -281,7 +279,7 @@ impl<'m> OsuPP<'m> {
}
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> PerformanceAttributes {
pub fn calculate(mut self) -> OsuPerformanceAttributes {
let attributes = self
.attributes
.take()
@@ -292,7 +290,7 @@ impl<'m> OsuPP<'m> {
}
struct OsuPPInner {
attributes: DifficultyAttributes,
attributes: OsuDifficultyAttributes,
mods: u32,
combo: Option<usize>,
acc: f64,
@@ -306,7 +304,7 @@ struct OsuPPInner {
}
impl OsuPPInner {
fn calculate(mut self) -> PerformanceAttributes {
fn calculate(mut self) -> OsuPerformanceAttributes {
let mut multiplier = 1.12;
// NF penalty
@@ -343,7 +341,7 @@ impl OsuPPInner {
.powf(1.0 / 1.1)
* multiplier;
PerformanceAttributes {
OsuPerformanceAttributes {
attributes: self.attributes,
pp_acc: acc_value,
pp_aim: aim_value,
@@ -565,7 +563,7 @@ impl OsuPPInner {
}
fn calculate_effective_misses(
attributes: &DifficultyAttributes,
attributes: &OsuDifficultyAttributes,
combo: Option<usize>,
n_misses: usize,
total_hits: f64,
@@ -590,27 +588,28 @@ fn calculate_effective_misses(
n_misses.max(combo_based_misses.floor() as usize)
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait OsuAttributeProvider {
fn attributes(self) -> Option<DifficultyAttributes>;
fn attributes(self) -> Option<OsuDifficultyAttributes>;
}
impl OsuAttributeProvider for DifficultyAttributes {
impl OsuAttributeProvider for OsuDifficultyAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<OsuDifficultyAttributes> {
Some(self)
}
}
impl OsuAttributeProvider for PerformanceAttributes {
impl OsuAttributeProvider for OsuPerformanceAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<OsuDifficultyAttributes> {
Some(self.attributes)
}
}
impl OsuAttributeProvider for StarResult {
impl OsuAttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<OsuDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Osu(attributes) = self {
Some(attributes)
@@ -620,9 +619,9 @@ impl OsuAttributeProvider for StarResult {
}
}
impl OsuAttributeProvider for PpResult {
impl OsuAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<DifficultyAttributes> {
fn attributes(self) -> Option<OsuDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Osu(attributes) = self {
Some(attributes.attributes)
@@ -699,7 +698,7 @@ mod test {
#[test]
fn osu_missing_objects() {
let map = Beatmap::default();
let attributes = DifficultyAttributes::default();
let attributes = OsuDifficultyAttributes::default();
let total_objects = 1234;
let n300 = 1000;
+2
View File
@@ -13,8 +13,10 @@ use std::fmt;
use std::io::Error as IOError;
use std::num::{ParseFloatError, ParseIntError};
/// `Result<_, ParseError>`
pub type ParseResult<T> = Result<T, ParseError>;
/// Anything that could go wrong while parsing a [`Beatmap`](crate::Beatmap).
#[derive(Debug)]
#[allow(clippy::upper_case_acronyms)]
pub enum ParseError {
+51 -20
View File
@@ -1,4 +1,8 @@
use crate::{Beatmap, GameMode, PpResult, StarResult};
use crate::{
fruits::FruitsDifficultyAttributes, mania::ManiaDifficultyAttributes,
osu::OsuDifficultyAttributes, taiko::TaikoDifficultyAttributes, Beatmap, DifficultyAttributes,
GameMode, PerformanceAttributes,
};
#[cfg(feature = "fruits")]
use crate::FruitsPP;
@@ -12,7 +16,7 @@ use crate::OsuPP;
#[cfg(feature = "taiko")]
use crate::TaikoPP;
/// Calculator for pp on maps of any mode.
/// Performance calculator on maps of any mode.
#[allow(clippy::upper_case_acronyms)]
pub enum AnyPP<'m> {
#[cfg(feature = "fruits")]
@@ -43,23 +47,21 @@ impl<'m> AnyPP<'m> {
}
#[inline]
pub fn calculate(self) -> PpResult {
pub fn calculate(self) -> PerformanceAttributes {
match self {
#[cfg(feature = "fruits")]
Self::Fruits(f) => PpResult::Fruits(f.calculate()),
Self::Fruits(f) => PerformanceAttributes::Fruits(f.calculate()),
#[cfg(feature = "mania")]
Self::Mania(m) => PpResult::Mania(m.calculate()),
Self::Mania(m) => PerformanceAttributes::Mania(m.calculate()),
#[cfg(feature = "osu")]
Self::Osu(o) => PpResult::Osu(o.calculate()),
Self::Osu(o) => PerformanceAttributes::Osu(o.calculate()),
#[cfg(feature = "taiko")]
Self::Taiko(t) => PpResult::Taiko(t.calculate()),
Self::Taiko(t) => PerformanceAttributes::Taiko(t.calculate()),
}
}
/// [`AttributeProvider`] is implemented by [`StarResult`](crate::StarResult)
/// and [`PpResult`](crate::PpResult) meaning you can give the result of a \
/// star calculation or the result of a pp calculation.
/// If you already calculated the stars for the current map-mod combination,
/// Provide the result of previous a difficulty or performance calculation.
/// If you already calculated the attributes for the current map-mod combination,
/// be sure to put them in here so that they don't have to be recalculated.
#[inline]
pub fn attributes(self, attributes: impl AttributeProvider) -> Self {
@@ -258,29 +260,58 @@ impl<'m> AnyPP<'m> {
}
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait AttributeProvider {
fn attributes(self) -> StarResult;
fn attributes(self) -> DifficultyAttributes;
}
impl AttributeProvider for StarResult {
impl AttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> StarResult {
fn attributes(self) -> DifficultyAttributes {
self
}
}
impl AttributeProvider for PpResult {
impl AttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> StarResult {
fn attributes(self) -> DifficultyAttributes {
match self {
#[cfg(feature = "fruits")]
Self::Fruits(f) => StarResult::Fruits(f.attributes),
Self::Fruits(f) => DifficultyAttributes::Fruits(f.attributes),
#[cfg(feature = "mania")]
Self::Mania(m) => StarResult::Mania(m.attributes),
Self::Mania(m) => DifficultyAttributes::Mania(m.attributes),
#[cfg(feature = "osu")]
Self::Osu(o) => StarResult::Osu(o.attributes),
Self::Osu(o) => DifficultyAttributes::Osu(o.attributes),
#[cfg(feature = "taiko")]
Self::Taiko(t) => StarResult::Taiko(t.attributes),
Self::Taiko(t) => DifficultyAttributes::Taiko(t.attributes),
}
}
}
#[cfg(feature = "fruits")]
impl AttributeProvider for FruitsDifficultyAttributes {
fn attributes(self) -> DifficultyAttributes {
DifficultyAttributes::Fruits(self)
}
}
#[cfg(feature = "mania")]
impl AttributeProvider for ManiaDifficultyAttributes {
fn attributes(self) -> DifficultyAttributes {
DifficultyAttributes::Mania(self)
}
}
#[cfg(feature = "osu")]
impl AttributeProvider for OsuDifficultyAttributes {
fn attributes(self) -> DifficultyAttributes {
DifficultyAttributes::Osu(self)
}
}
#[cfg(feature = "taiko")]
impl AttributeProvider for TaikoDifficultyAttributes {
fn attributes(self) -> DifficultyAttributes {
DifficultyAttributes::Taiko(self)
}
}
+11 -12
View File
@@ -29,18 +29,18 @@ const COLOR_SKILL_MULTIPLIER: f64 = 0.01;
const RHYTHM_SKILL_MULTIPLIER: f64 = 0.014;
const STAMINA_SKILL_MULTIPLIER: f64 = 0.02;
/// Star calculation for osu!taiko maps.
/// Difficulty calculation for osu!taiko maps.
///
/// In case of a partial play, e.g. a fail, one can specify the amount of passed objects.
pub fn stars(
map: &Beatmap,
mods: impl Mods,
passed_objects: Option<usize>,
) -> DifficultyAttributes {
) -> TaikoDifficultyAttributes {
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
if take < 2 {
return DifficultyAttributes { stars: 0.0 };
return TaikoDifficultyAttributes { stars: 0.0 };
}
// True if the object at that index is stamina cheese
@@ -120,10 +120,10 @@ pub fn stars(
let stars = rescale(1.4 * separate_rating + 0.5 * combined_rating);
DifficultyAttributes { stars }
TaikoDifficultyAttributes { stars }
}
/// Essentially the same as the `stars` function but instead of
/// Essentially the same as the [`stars`] function but instead of
/// evaluating the final strains, it just returns them as is.
///
/// Suitable to plot the difficulty of a map over time.
@@ -263,23 +263,22 @@ fn norm(p: f64, a: f64, b: f64, c: f64) -> f64 {
(a.powf(p) + b.powf(p) + c.powf(p)).powf(p.recip())
}
/// Various data created through the star calculation.
/// This data is necessary to calculate PP.
/// The result of a difficulty calculation on an osu!taiko map.
#[derive(Copy, Clone, Debug, Default)]
pub struct DifficultyAttributes {
pub struct TaikoDifficultyAttributes {
pub stars: f64,
}
/// Various data created through the pp calculation.
/// The result of a performance calculation on an osu!taiko map.
#[derive(Copy, Clone, Debug, Default)]
pub struct PerformanceAttributes {
pub attributes: DifficultyAttributes,
pub struct TaikoPerformanceAttributes {
pub attributes: TaikoDifficultyAttributes,
pub pp: f64,
pub pp_acc: f64,
pub pp_strain: f64,
}
impl PerformanceAttributes {
impl TaikoPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
+14 -15
View File
@@ -1,7 +1,7 @@
use super::{stars, DifficultyAttributes, PerformanceAttributes};
use crate::{Beatmap, Mods, PpResult, StarResult};
use super::{stars, TaikoDifficultyAttributes, TaikoPerformanceAttributes};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// Calculator for pp on osu!taiko maps.
/// Performance calculator on osu!taiko maps.
///
/// # Example
///
@@ -61,10 +61,8 @@ impl<'m> TaikoPP<'m> {
}
}
/// [`TaikoAttributeProvider`] is implemented by `f32`, [`StarResult`](crate::StarResult),
/// and by [`PpResult`](crate::PpResult) meaning you can give the star rating,
/// the result of a star calculation, or the result of a pp calculation.
/// If you already calculated the stars for the current map-mod combination,
/// Provide the result of previous a difficulty or performance calculation.
/// If you already calculated the attributes for the current map-mod combination,
/// 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 {
@@ -136,7 +134,7 @@ impl<'m> TaikoPP<'m> {
}
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> PerformanceAttributes {
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
let stars = self
.stars
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars);
@@ -184,7 +182,7 @@ struct TaikoPPInner<'m> {
}
impl<'m> TaikoPPInner<'m> {
fn calculate(self) -> PerformanceAttributes {
fn calculate(self) -> TaikoPerformanceAttributes {
let mut multiplier = 1.1;
if self.mods.nf() {
@@ -200,8 +198,8 @@ impl<'m> TaikoPPInner<'m> {
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
PerformanceAttributes {
attributes: DifficultyAttributes { stars: self.stars },
TaikoPerformanceAttributes {
attributes: TaikoDifficultyAttributes { stars: self.stars },
pp,
pp_acc: acc_value,
pp_strain: strain_value,
@@ -257,6 +255,7 @@ fn difficulty_range_od(od: f64) -> f64 {
crate::difficulty_range(od, 20.0, 35.0, 50.0)
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait TaikoAttributeProvider {
fn attributes(self) -> Option<f64>;
}
@@ -268,21 +267,21 @@ impl TaikoAttributeProvider for f64 {
}
}
impl TaikoAttributeProvider for DifficultyAttributes {
impl TaikoAttributeProvider for TaikoDifficultyAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self.stars)
}
}
impl TaikoAttributeProvider for PerformanceAttributes {
impl TaikoAttributeProvider for TaikoPerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
Some(self.attributes.stars)
}
}
impl TaikoAttributeProvider for StarResult {
impl TaikoAttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
#[allow(irrefutable_let_patterns)]
@@ -294,7 +293,7 @@ impl TaikoAttributeProvider for StarResult {
}
}
impl TaikoAttributeProvider for PpResult {
impl TaikoAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<f64> {
#[allow(irrefutable_let_patterns)]