renamed PpResult+StarResult, minor structural adjustments, documentation update
This commit is contained in:
+2
-2
@@ -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.
|
- [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`.
|
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 `PpResult`, performance calculations now return `{Mode}PerformanceAttributes` and `PpResult` has been renamed to `PerformanceAttributes`.
|
||||||
- [BREAKING] Instead of returning `StarResult`, difficulty calculations now return `DifficultyAttributes` depending on the mode.
|
- [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
|
- [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`
|
- 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)
|
- osu: Updated up to commit [9fb2402781ad91c197d51aeec716b0000f52c4d1](https://github.com/ppy/osu/commit/9fb2402781ad91c197d51aeec716b0000f52c4d1) (2021-11-12)
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ let map = match Beatmap::parse(file) {
|
|||||||
|
|
||||||
// If `BeatmapExt` is included, you can make use of
|
// If `BeatmapExt` is included, you can make use of
|
||||||
// some methods on `Beatmap` to make your life simpler.
|
// 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()
|
let result = map.pp()
|
||||||
.mods(24) // HDHR
|
.mods(24) // HDHR
|
||||||
.combo(1234)
|
.combo(1234)
|
||||||
|
|||||||
+11
-12
@@ -27,16 +27,16 @@ const CATCHER_SIZE: f32 = 106.75;
|
|||||||
const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
|
const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
|
||||||
const BASE_SCORING_DISTANCE: f64 = 100.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.
|
/// 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,
|
map: &Beatmap,
|
||||||
mods: impl Mods,
|
mods: impl Mods,
|
||||||
passed_objects: Option<usize>,
|
passed_objects: Option<usize>,
|
||||||
) -> DifficultyAttributes {
|
) -> FruitsDifficultyAttributes {
|
||||||
if map.hit_objects.len() < 2 {
|
if map.hit_objects.len() < 2 {
|
||||||
return DifficultyAttributes::default();
|
return FruitsDifficultyAttributes::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let take = passed_objects.unwrap_or(usize::MAX);
|
let take = passed_objects.unwrap_or(usize::MAX);
|
||||||
@@ -279,7 +279,7 @@ pub fn stars(
|
|||||||
|
|
||||||
let stars = movement.difficulty_value().sqrt() * STAR_SCALING_FACTOR;
|
let stars = movement.difficulty_value().sqrt() * STAR_SCALING_FACTOR;
|
||||||
|
|
||||||
DifficultyAttributes {
|
FruitsDifficultyAttributes {
|
||||||
stars,
|
stars,
|
||||||
ar: attributes.ar,
|
ar: attributes.ar,
|
||||||
n_fruits: fruits,
|
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.
|
/// evaluating the final strains, it just returns them as is.
|
||||||
///
|
///
|
||||||
/// Suitable to plot the difficulty of a map over time.
|
/// 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.
|
/// The result of a difficulty calculation on an osu!ctb map.
|
||||||
/// This data is necessary to calculate PP.
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct DifficultyAttributes {
|
pub struct FruitsDifficultyAttributes {
|
||||||
pub stars: f64,
|
pub stars: f64,
|
||||||
pub max_combo: usize,
|
pub max_combo: usize,
|
||||||
pub ar: f64,
|
pub ar: f64,
|
||||||
@@ -641,14 +640,14 @@ pub struct DifficultyAttributes {
|
|||||||
pub n_tiny_droplets: usize,
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct PerformanceAttributes {
|
pub struct FruitsPerformanceAttributes {
|
||||||
pub attributes: DifficultyAttributes,
|
pub attributes: FruitsDifficultyAttributes,
|
||||||
pub pp: f64,
|
pub pp: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceAttributes {
|
impl FruitsPerformanceAttributes {
|
||||||
/// Return the star value.
|
/// Return the star value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
|
|||||||
+22
-23
@@ -1,7 +1,7 @@
|
|||||||
use super::{stars, DifficultyAttributes, PerformanceAttributes};
|
use super::{stars, FruitsDifficultyAttributes, FruitsPerformanceAttributes};
|
||||||
use crate::{Beatmap, Mods, PpResult, StarResult};
|
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
|
||||||
|
|
||||||
/// Calculator for pp on osu!ctb maps.
|
/// Performance calculator on osu!ctb maps.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -32,7 +32,7 @@ use crate::{Beatmap, Mods, PpResult, StarResult};
|
|||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub struct FruitsPP<'m> {
|
pub struct FruitsPP<'m> {
|
||||||
map: &'m Beatmap,
|
map: &'m Beatmap,
|
||||||
attributes: Option<DifficultyAttributes>,
|
attributes: Option<FruitsDifficultyAttributes>,
|
||||||
mods: u32,
|
mods: u32,
|
||||||
combo: Option<usize>,
|
combo: Option<usize>,
|
||||||
|
|
||||||
@@ -62,9 +62,7 @@ impl<'m> FruitsPP<'m> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`FruitsAttributeProvider`] is implemented by [`DifficultyAttributes`](crate::fruits::DifficultyAttributes),
|
/// Provide the result of previous a difficulty or performance calculation.
|
||||||
/// [`StarResult`](crate::StarResult), and by [`PpResult`](crate::PpResult) meaning you can give the
|
|
||||||
/// result of a star calculation or a pp calculation.
|
|
||||||
/// If you already calculated the attributes for the current map-mod combination,
|
/// 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.
|
/// be sure to put them in here so that they don't have to be recalculated.
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -182,7 +180,7 @@ impl<'m> FruitsPP<'m> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_hitresults(self, attributes: DifficultyAttributes) -> FruitsPPInner {
|
fn assert_hitresults(self, attributes: FruitsDifficultyAttributes) -> FruitsPPInner {
|
||||||
let correct_combo_hits = self
|
let correct_combo_hits = self
|
||||||
.n_fruits
|
.n_fruits
|
||||||
.and_then(|f| self.n_droplets.map(|d| f + d + self.n_misses))
|
.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.
|
/// Calculate all performance related values, including pp and stars.
|
||||||
pub fn calculate(mut self) -> PerformanceAttributes {
|
pub fn calculate(mut self) -> FruitsPerformanceAttributes {
|
||||||
let attributes = self
|
let attributes = self
|
||||||
.attributes
|
.attributes
|
||||||
.take()
|
.take()
|
||||||
@@ -264,7 +262,7 @@ impl<'m> FruitsPP<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct FruitsPPInner {
|
struct FruitsPPInner {
|
||||||
attributes: DifficultyAttributes,
|
attributes: FruitsDifficultyAttributes,
|
||||||
mods: u32,
|
mods: u32,
|
||||||
combo: Option<usize>,
|
combo: Option<usize>,
|
||||||
n_fruits: usize,
|
n_fruits: usize,
|
||||||
@@ -275,7 +273,7 @@ struct FruitsPPInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FruitsPPInner {
|
impl FruitsPPInner {
|
||||||
fn calculate(self) -> PerformanceAttributes {
|
fn calculate(self) -> FruitsPerformanceAttributes {
|
||||||
let attributes = &self.attributes;
|
let attributes = &self.attributes;
|
||||||
let stars = attributes.stars;
|
let stars = attributes.stars;
|
||||||
|
|
||||||
@@ -337,7 +335,7 @@ impl FruitsPPInner {
|
|||||||
pp *= 0.9;
|
pp *= 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
PerformanceAttributes {
|
FruitsPerformanceAttributes {
|
||||||
attributes: self.attributes,
|
attributes: self.attributes,
|
||||||
pp,
|
pp,
|
||||||
}
|
}
|
||||||
@@ -372,27 +370,28 @@ impl FruitsPPInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
|
||||||
pub trait FruitsAttributeProvider {
|
pub trait FruitsAttributeProvider {
|
||||||
fn attributes(self) -> Option<DifficultyAttributes>;
|
fn attributes(self) -> Option<FruitsDifficultyAttributes>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FruitsAttributeProvider for DifficultyAttributes {
|
impl FruitsAttributeProvider for FruitsDifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FruitsAttributeProvider for PerformanceAttributes {
|
impl FruitsAttributeProvider for FruitsPerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
|
||||||
Some(self.attributes)
|
Some(self.attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FruitsAttributeProvider for StarResult {
|
impl FruitsAttributeProvider for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
if let Self::Fruits(attributes) = self {
|
if let Self::Fruits(attributes) = self {
|
||||||
Some(attributes)
|
Some(attributes)
|
||||||
@@ -402,9 +401,9 @@ impl FruitsAttributeProvider for StarResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FruitsAttributeProvider for PpResult {
|
impl FruitsAttributeProvider for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
if let Self::Fruits(attributes) = self {
|
if let Self::Fruits(attributes) = self {
|
||||||
Some(attributes.attributes)
|
Some(attributes.attributes)
|
||||||
@@ -419,8 +418,8 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::Beatmap;
|
use crate::Beatmap;
|
||||||
|
|
||||||
fn attributes() -> DifficultyAttributes {
|
fn attributes() -> FruitsDifficultyAttributes {
|
||||||
DifficultyAttributes {
|
FruitsDifficultyAttributes {
|
||||||
n_fruits: 1234,
|
n_fruits: 1234,
|
||||||
n_droplets: 567,
|
n_droplets: 567,
|
||||||
n_tiny_droplets: 2345,
|
n_tiny_droplets: 2345,
|
||||||
|
|||||||
+46
-34
@@ -25,8 +25,6 @@
|
|||||||
//!
|
//!
|
||||||
//! // If `BeatmapExt` is included, you can make use of
|
//! // If `BeatmapExt` is included, you can make use of
|
||||||
//! // some methods on `Beatmap` to make your life simpler.
|
//! // 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()
|
//! let result = map.pp()
|
||||||
//! .mods(24) // HDHR
|
//! .mods(24) // HDHR
|
||||||
//! .combo(1234)
|
//! .combo(1234)
|
||||||
@@ -153,17 +151,17 @@ pub use taiko::TaikoPP;
|
|||||||
pub use mods::Mods;
|
pub use mods::Mods;
|
||||||
pub use parse::{Beatmap, BeatmapAttributes, GameMode, ParseError, ParseResult};
|
pub use parse::{Beatmap, BeatmapAttributes, GameMode, ParseError, ParseResult};
|
||||||
|
|
||||||
|
/// Provides some additional methods on [`Beatmap`](crate::Beatmap).
|
||||||
pub trait BeatmapExt {
|
pub trait BeatmapExt {
|
||||||
/// Calculate the stars and other attributes of a beatmap which are required for pp calculation.
|
/// 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.
|
/// Calculate the max pp of a beatmap.
|
||||||
///
|
///
|
||||||
/// If you seek more fine-tuning and options you need to match on the map's
|
/// If you seek more fine-tuning you can use the [`pp`](BeatmapExt::pp) method.
|
||||||
/// mode and use the mode's corresponding calculator, e.g. [`TaikoPP`](crate::TaikoPP) for taiko.
|
fn max_pp(&self, mods: u32) -> PerformanceAttributes;
|
||||||
fn max_pp(&self, mods: u32) -> PpResult;
|
|
||||||
|
|
||||||
/// 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.
|
/// Convenient method that matches on the map's mode to choose the appropriate calculator.
|
||||||
fn pp(&self) -> AnyPP<'_>;
|
fn pp(&self) -> AnyPP<'_>;
|
||||||
@@ -177,68 +175,68 @@ pub trait BeatmapExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BeatmapExt for Beatmap {
|
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 {
|
match self.mode {
|
||||||
GameMode::STD => {
|
GameMode::STD => {
|
||||||
#[cfg(not(feature = "osu"))]
|
#[cfg(not(feature = "osu"))]
|
||||||
panic!("`osu` feature is not enabled");
|
panic!("`osu` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
#[cfg(feature = "osu")]
|
||||||
StarResult::Osu(osu::stars(self, mods, passed_objects))
|
DifficultyAttributes::Osu(osu::stars(self, mods, passed_objects))
|
||||||
}
|
}
|
||||||
GameMode::MNA => {
|
GameMode::MNA => {
|
||||||
#[cfg(not(feature = "mania"))]
|
#[cfg(not(feature = "mania"))]
|
||||||
panic!("`mania` feature is not enabled");
|
panic!("`mania` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
#[cfg(feature = "mania")]
|
||||||
StarResult::Mania(mania::stars(self, mods, passed_objects))
|
DifficultyAttributes::Mania(mania::stars(self, mods, passed_objects))
|
||||||
}
|
}
|
||||||
GameMode::TKO => {
|
GameMode::TKO => {
|
||||||
#[cfg(not(feature = "taiko"))]
|
#[cfg(not(feature = "taiko"))]
|
||||||
panic!("`taiko` feature is not enabled");
|
panic!("`taiko` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
#[cfg(feature = "taiko")]
|
||||||
StarResult::Taiko(taiko::stars(self, mods, passed_objects))
|
DifficultyAttributes::Taiko(taiko::stars(self, mods, passed_objects))
|
||||||
}
|
}
|
||||||
GameMode::CTB => {
|
GameMode::CTB => {
|
||||||
#[cfg(not(feature = "fruits"))]
|
#[cfg(not(feature = "fruits"))]
|
||||||
panic!("`fruits` feature is not enabled");
|
panic!("`fruits` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
#[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 {
|
match self.mode {
|
||||||
GameMode::STD => {
|
GameMode::STD => {
|
||||||
#[cfg(not(feature = "osu"))]
|
#[cfg(not(feature = "osu"))]
|
||||||
panic!("`osu` feature is not enabled");
|
panic!("`osu` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
#[cfg(feature = "osu")]
|
||||||
PpResult::Osu(OsuPP::new(self).mods(mods).calculate())
|
PerformanceAttributes::Osu(OsuPP::new(self).mods(mods).calculate())
|
||||||
}
|
}
|
||||||
GameMode::MNA => {
|
GameMode::MNA => {
|
||||||
#[cfg(not(feature = "mania"))]
|
#[cfg(not(feature = "mania"))]
|
||||||
panic!("`mania` feature is not enabled");
|
panic!("`mania` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
#[cfg(feature = "mania")]
|
||||||
PpResult::Mania(ManiaPP::new(self).mods(mods).calculate())
|
PerformanceAttributes::Mania(ManiaPP::new(self).mods(mods).calculate())
|
||||||
}
|
}
|
||||||
GameMode::TKO => {
|
GameMode::TKO => {
|
||||||
#[cfg(not(feature = "taiko"))]
|
#[cfg(not(feature = "taiko"))]
|
||||||
panic!("`taiko` feature is not enabled");
|
panic!("`taiko` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
#[cfg(feature = "taiko")]
|
||||||
PpResult::Taiko(TaikoPP::new(self).mods(mods).calculate())
|
PerformanceAttributes::Taiko(TaikoPP::new(self).mods(mods).calculate())
|
||||||
}
|
}
|
||||||
GameMode::CTB => {
|
GameMode::CTB => {
|
||||||
#[cfg(not(feature = "fruits"))]
|
#[cfg(not(feature = "fruits"))]
|
||||||
panic!("`fruits` feature is not enabled");
|
panic!("`fruits` feature is not enabled");
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
#[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>,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum StarResult {
|
pub enum DifficultyAttributes {
|
||||||
#[cfg(feature = "fruits")]
|
#[cfg(feature = "fruits")]
|
||||||
Fruits(fruits::DifficultyAttributes),
|
Fruits(fruits::FruitsDifficultyAttributes),
|
||||||
#[cfg(feature = "mania")]
|
#[cfg(feature = "mania")]
|
||||||
Mania(mania::DifficultyAttributes),
|
Mania(mania::ManiaDifficultyAttributes),
|
||||||
#[cfg(feature = "osu")]
|
#[cfg(feature = "osu")]
|
||||||
Osu(osu::DifficultyAttributes),
|
Osu(osu::OsuDifficultyAttributes),
|
||||||
#[cfg(feature = "taiko")]
|
#[cfg(feature = "taiko")]
|
||||||
Taiko(taiko::DifficultyAttributes),
|
Taiko(taiko::TaikoDifficultyAttributes),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StarResult {
|
impl DifficultyAttributes {
|
||||||
/// The final star value.
|
/// The star value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
match self {
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PpResult {
|
pub enum PerformanceAttributes {
|
||||||
#[cfg(feature = "fruits")]
|
#[cfg(feature = "fruits")]
|
||||||
Fruits(fruits::PerformanceAttributes),
|
Fruits(fruits::FruitsPerformanceAttributes),
|
||||||
#[cfg(feature = "mania")]
|
#[cfg(feature = "mania")]
|
||||||
Mania(mania::PerformanceAttributes),
|
Mania(mania::ManiaPerformanceAttributes),
|
||||||
#[cfg(feature = "osu")]
|
#[cfg(feature = "osu")]
|
||||||
Osu(osu::PerformanceAttributes),
|
Osu(osu::OsuPerformanceAttributes),
|
||||||
#[cfg(feature = "taiko")]
|
#[cfg(feature = "taiko")]
|
||||||
Taiko(taiko::PerformanceAttributes),
|
Taiko(taiko::TaikoPerformanceAttributes),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PpResult {
|
impl PerformanceAttributes {
|
||||||
/// The final pp value.
|
/// The pp value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pp(&self) -> f64 {
|
pub fn pp(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
@@ -353,7 +351,7 @@ impl PpResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The final star value.
|
/// The star value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
@@ -367,6 +365,20 @@ impl PpResult {
|
|||||||
Self::Taiko(attributes) => attributes.stars(),
|
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"))]
|
#[cfg(any(feature = "osu", feature = "taiko"))]
|
||||||
|
|||||||
+11
-12
@@ -11,18 +11,18 @@ use crate::{parse::HitObject, Beatmap, GameMode, Mods, Strains};
|
|||||||
const SECTION_LEN: f64 = 400.0;
|
const SECTION_LEN: f64 = 400.0;
|
||||||
const STAR_SCALING_FACTOR: f64 = 0.018;
|
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.
|
/// 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,
|
map: &Beatmap,
|
||||||
mods: impl Mods,
|
mods: impl Mods,
|
||||||
passed_objects: Option<usize>,
|
passed_objects: Option<usize>,
|
||||||
) -> DifficultyAttributes {
|
) -> ManiaDifficultyAttributes {
|
||||||
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
|
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
|
||||||
|
|
||||||
if take < 2 {
|
if take < 2 {
|
||||||
return DifficultyAttributes::default();
|
return ManiaDifficultyAttributes::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let rounded_cs = map.cs.round();
|
let rounded_cs = map.cs.round();
|
||||||
@@ -90,10 +90,10 @@ pub fn stars(
|
|||||||
|
|
||||||
let stars = strain.difficulty_value() * STAR_SCALING_FACTOR;
|
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.
|
/// evaluating the final strains, it just returns them as is.
|
||||||
///
|
///
|
||||||
/// Suitable to plot the difficulty of a map over time.
|
/// 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.
|
/// The result of a difficulty calculation on an osu!mania map.
|
||||||
/// This data is necessary to calculate PP.
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct DifficultyAttributes {
|
pub struct ManiaDifficultyAttributes {
|
||||||
pub stars: f64,
|
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)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct PerformanceAttributes {
|
pub struct ManiaPerformanceAttributes {
|
||||||
pub attributes: DifficultyAttributes,
|
pub attributes: ManiaDifficultyAttributes,
|
||||||
pub pp_acc: f64,
|
pub pp_acc: f64,
|
||||||
pub pp_strain: f64,
|
pub pp_strain: f64,
|
||||||
pub pp: f64,
|
pub pp: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceAttributes {
|
impl ManiaPerformanceAttributes {
|
||||||
/// Return the star value.
|
/// Return the star value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
|
|||||||
+12
-13
@@ -1,7 +1,7 @@
|
|||||||
use super::{stars, DifficultyAttributes, PerformanceAttributes};
|
use super::{stars, ManiaDifficultyAttributes, ManiaPerformanceAttributes};
|
||||||
use crate::{Beatmap, Mods, PpResult, StarResult};
|
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
|
||||||
|
|
||||||
/// Calculator for pp on osu!mania maps.
|
/// Performance calculator on osu!mania maps.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -48,9 +48,7 @@ impl<'m> ManiaPP<'m> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`ManiaAttributeProvider`] is implemented by `f32`, [`StarResult`](crate::StarResult),
|
/// Provide the result of previous a difficulty or performance calculation.
|
||||||
/// 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 attributes for the current map-mod combination,
|
/// 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.
|
/// be sure to put them in here so that they don't have to be recalculated.
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -90,7 +88,7 @@ impl<'m> ManiaPP<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate all performance related values, including pp and stars.
|
/// Calculate all performance related values, including pp and stars.
|
||||||
pub fn calculate(self) -> PerformanceAttributes {
|
pub fn calculate(self) -> ManiaPerformanceAttributes {
|
||||||
let stars = self
|
let stars = self
|
||||||
.stars
|
.stars
|
||||||
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).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;
|
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
|
||||||
|
|
||||||
PerformanceAttributes {
|
ManiaPerformanceAttributes {
|
||||||
attributes: DifficultyAttributes { stars },
|
attributes: ManiaDifficultyAttributes { stars },
|
||||||
pp_acc: acc_value,
|
pp_acc: acc_value,
|
||||||
pp_strain: strain_value,
|
pp_strain: strain_value,
|
||||||
pp,
|
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 {
|
pub trait ManiaAttributeProvider {
|
||||||
fn attributes(self) -> Option<f64>;
|
fn attributes(self) -> Option<f64>;
|
||||||
}
|
}
|
||||||
@@ -180,21 +179,21 @@ impl ManiaAttributeProvider for f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManiaAttributeProvider for DifficultyAttributes {
|
impl ManiaAttributeProvider for ManiaDifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
Some(self.stars)
|
Some(self.stars)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManiaAttributeProvider for PerformanceAttributes {
|
impl ManiaAttributeProvider for ManiaPerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
Some(self.attributes.stars)
|
Some(self.attributes.stars)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManiaAttributeProvider for StarResult {
|
impl ManiaAttributeProvider for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
@@ -206,7 +205,7 @@ impl ManiaAttributeProvider for StarResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManiaAttributeProvider for PpResult {
|
impl ManiaAttributeProvider for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ macro_rules! impl_mods {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Abstract type to define mods.
|
||||||
pub trait Mods: Copy {
|
pub trait Mods: Copy {
|
||||||
const NF: u32 = 1 << 0;
|
const NF: u32 = 1 << 0;
|
||||||
const EZ: u32 = 1 << 1;
|
const EZ: u32 = 1 << 1;
|
||||||
|
|||||||
+11
-15
@@ -23,17 +23,14 @@ const DIFFICULTY_MULTIPLIER: f64 = 0.0675;
|
|||||||
const NORMALIZED_RADIUS: f32 = 50.0; // * diameter of 100; easier mental maths.
|
const NORMALIZED_RADIUS: f32 = 50.0; // * diameter of 100; easier mental maths.
|
||||||
const STACK_DISTANCE: f32 = 3.0;
|
const STACK_DISTANCE: f32 = 3.0;
|
||||||
|
|
||||||
/// Star calculation for osu!standard maps.
|
/// Difficulty 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
|
|
||||||
///
|
///
|
||||||
/// 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,
|
map: &Beatmap,
|
||||||
mods: impl Mods,
|
mods: impl Mods,
|
||||||
passed_objects: Option<usize>,
|
passed_objects: Option<usize>,
|
||||||
) -> DifficultyAttributes {
|
) -> OsuDifficultyAttributes {
|
||||||
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
|
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
|
||||||
|
|
||||||
let map_attributes = map.attributes().mods(mods);
|
let map_attributes = map.attributes().mods(mods);
|
||||||
@@ -41,7 +38,7 @@ pub fn stars(
|
|||||||
let od = (80.0 - hit_window) / 6.0;
|
let od = (80.0 - hit_window) / 6.0;
|
||||||
|
|
||||||
if take < 2 {
|
if take < 2 {
|
||||||
return DifficultyAttributes {
|
return OsuDifficultyAttributes {
|
||||||
ar: map_attributes.ar,
|
ar: map_attributes.ar,
|
||||||
hp: map_attributes.hp,
|
hp: map_attributes.hp,
|
||||||
od,
|
od,
|
||||||
@@ -223,7 +220,7 @@ pub fn stars(
|
|||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
DifficultyAttributes {
|
OsuDifficultyAttributes {
|
||||||
ar: map_attributes.ar,
|
ar: map_attributes.ar,
|
||||||
hp: map_attributes.hp,
|
hp: map_attributes.hp,
|
||||||
od,
|
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.
|
/// evaluating the final strains, it just returns them as is.
|
||||||
///
|
///
|
||||||
/// Suitable to plot the difficulty of a map over time.
|
/// 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)
|
crate::difficulty_range(ar, 450.0, 1200.0, 1800.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various data created through the star calculation.
|
/// The result of a difficulty calculation on an osu!standard map.
|
||||||
/// This data is necessary to calculate PP.
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct DifficultyAttributes {
|
pub struct OsuDifficultyAttributes {
|
||||||
pub aim_strain: f64,
|
pub aim_strain: f64,
|
||||||
pub speed_strain: f64,
|
pub speed_strain: f64,
|
||||||
pub flashlight_rating: f64,
|
pub flashlight_rating: f64,
|
||||||
@@ -562,10 +558,10 @@ pub struct DifficultyAttributes {
|
|||||||
pub max_combo: usize,
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct PerformanceAttributes {
|
pub struct OsuPerformanceAttributes {
|
||||||
pub attributes: DifficultyAttributes,
|
pub attributes: OsuDifficultyAttributes,
|
||||||
pub pp_acc: f64,
|
pub pp_acc: f64,
|
||||||
pub pp_aim: f64,
|
pub pp_aim: f64,
|
||||||
pub pp_flashlight: f64,
|
pub pp_flashlight: f64,
|
||||||
@@ -573,7 +569,7 @@ pub struct PerformanceAttributes {
|
|||||||
pub pp: f64,
|
pub pp: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceAttributes {
|
impl OsuPerformanceAttributes {
|
||||||
/// Return the star value.
|
/// Return the star value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
|
|||||||
+22
-23
@@ -1,7 +1,7 @@
|
|||||||
use super::{DifficultyAttributes, PerformanceAttributes};
|
use super::{OsuDifficultyAttributes, OsuPerformanceAttributes};
|
||||||
use crate::{Beatmap, Mods, PpResult, StarResult};
|
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
|
||||||
|
|
||||||
/// Calculator for pp on osu!standard maps.
|
/// Performance calculator on osu!standard maps.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -32,7 +32,7 @@ use crate::{Beatmap, Mods, PpResult, StarResult};
|
|||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub struct OsuPP<'m> {
|
pub struct OsuPP<'m> {
|
||||||
map: &'m Beatmap,
|
map: &'m Beatmap,
|
||||||
attributes: Option<DifficultyAttributes>,
|
attributes: Option<OsuDifficultyAttributes>,
|
||||||
mods: u32,
|
mods: u32,
|
||||||
combo: Option<usize>,
|
combo: Option<usize>,
|
||||||
acc: Option<f64>,
|
acc: Option<f64>,
|
||||||
@@ -62,9 +62,7 @@ impl<'m> OsuPP<'m> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`OsuAttributeProvider`] is implemented by [`DifficultyAttributes`](crate::osu::DifficultyAttributes)
|
/// Provide the result of previous a difficulty or performance calculation.
|
||||||
/// and by [`PpResult`](crate::PpResult) meaning you can give the
|
|
||||||
/// result of a star calculation or a pp calculation.
|
|
||||||
/// If you already calculated the attributes for the current map-mod combination,
|
/// 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.
|
/// be sure to put them in here so that they don't have to be recalculated.
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -199,7 +197,7 @@ impl<'m> OsuPP<'m> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_hitresults(self, attributes: DifficultyAttributes) -> OsuPPInner {
|
fn assert_hitresults(self, attributes: OsuDifficultyAttributes) -> OsuPPInner {
|
||||||
let mut n300 = self.n300;
|
let mut n300 = self.n300;
|
||||||
let mut n100 = self.n100;
|
let mut n100 = self.n100;
|
||||||
let mut n50 = self.n50;
|
let mut n50 = self.n50;
|
||||||
@@ -281,7 +279,7 @@ impl<'m> OsuPP<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate all performance related values, including pp and stars.
|
/// Calculate all performance related values, including pp and stars.
|
||||||
pub fn calculate(mut self) -> PerformanceAttributes {
|
pub fn calculate(mut self) -> OsuPerformanceAttributes {
|
||||||
let attributes = self
|
let attributes = self
|
||||||
.attributes
|
.attributes
|
||||||
.take()
|
.take()
|
||||||
@@ -292,7 +290,7 @@ impl<'m> OsuPP<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct OsuPPInner {
|
struct OsuPPInner {
|
||||||
attributes: DifficultyAttributes,
|
attributes: OsuDifficultyAttributes,
|
||||||
mods: u32,
|
mods: u32,
|
||||||
combo: Option<usize>,
|
combo: Option<usize>,
|
||||||
acc: f64,
|
acc: f64,
|
||||||
@@ -306,7 +304,7 @@ struct OsuPPInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OsuPPInner {
|
impl OsuPPInner {
|
||||||
fn calculate(mut self) -> PerformanceAttributes {
|
fn calculate(mut self) -> OsuPerformanceAttributes {
|
||||||
let mut multiplier = 1.12;
|
let mut multiplier = 1.12;
|
||||||
|
|
||||||
// NF penalty
|
// NF penalty
|
||||||
@@ -343,7 +341,7 @@ impl OsuPPInner {
|
|||||||
.powf(1.0 / 1.1)
|
.powf(1.0 / 1.1)
|
||||||
* multiplier;
|
* multiplier;
|
||||||
|
|
||||||
PerformanceAttributes {
|
OsuPerformanceAttributes {
|
||||||
attributes: self.attributes,
|
attributes: self.attributes,
|
||||||
pp_acc: acc_value,
|
pp_acc: acc_value,
|
||||||
pp_aim: aim_value,
|
pp_aim: aim_value,
|
||||||
@@ -565,7 +563,7 @@ impl OsuPPInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_effective_misses(
|
fn calculate_effective_misses(
|
||||||
attributes: &DifficultyAttributes,
|
attributes: &OsuDifficultyAttributes,
|
||||||
combo: Option<usize>,
|
combo: Option<usize>,
|
||||||
n_misses: usize,
|
n_misses: usize,
|
||||||
total_hits: f64,
|
total_hits: f64,
|
||||||
@@ -590,27 +588,28 @@ fn calculate_effective_misses(
|
|||||||
n_misses.max(combo_based_misses.floor() as usize)
|
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 {
|
pub trait OsuAttributeProvider {
|
||||||
fn attributes(self) -> Option<DifficultyAttributes>;
|
fn attributes(self) -> Option<OsuDifficultyAttributes>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsuAttributeProvider for DifficultyAttributes {
|
impl OsuAttributeProvider for OsuDifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<OsuDifficultyAttributes> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsuAttributeProvider for PerformanceAttributes {
|
impl OsuAttributeProvider for OsuPerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<OsuDifficultyAttributes> {
|
||||||
Some(self.attributes)
|
Some(self.attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsuAttributeProvider for StarResult {
|
impl OsuAttributeProvider for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<OsuDifficultyAttributes> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
if let Self::Osu(attributes) = self {
|
if let Self::Osu(attributes) = self {
|
||||||
Some(attributes)
|
Some(attributes)
|
||||||
@@ -620,9 +619,9 @@ impl OsuAttributeProvider for StarResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsuAttributeProvider for PpResult {
|
impl OsuAttributeProvider for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
fn attributes(self) -> Option<OsuDifficultyAttributes> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
if let Self::Osu(attributes) = self {
|
if let Self::Osu(attributes) = self {
|
||||||
Some(attributes.attributes)
|
Some(attributes.attributes)
|
||||||
@@ -699,7 +698,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn osu_missing_objects() {
|
fn osu_missing_objects() {
|
||||||
let map = Beatmap::default();
|
let map = Beatmap::default();
|
||||||
let attributes = DifficultyAttributes::default();
|
let attributes = OsuDifficultyAttributes::default();
|
||||||
|
|
||||||
let total_objects = 1234;
|
let total_objects = 1234;
|
||||||
let n300 = 1000;
|
let n300 = 1000;
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ use std::fmt;
|
|||||||
use std::io::Error as IOError;
|
use std::io::Error as IOError;
|
||||||
use std::num::{ParseFloatError, ParseIntError};
|
use std::num::{ParseFloatError, ParseIntError};
|
||||||
|
|
||||||
|
/// `Result<_, ParseError>`
|
||||||
pub type ParseResult<T> = Result<T, ParseError>;
|
pub type ParseResult<T> = Result<T, ParseError>;
|
||||||
|
|
||||||
|
/// Anything that could go wrong while parsing a [`Beatmap`](crate::Beatmap).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
|
|||||||
@@ -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")]
|
#[cfg(feature = "fruits")]
|
||||||
use crate::FruitsPP;
|
use crate::FruitsPP;
|
||||||
@@ -12,7 +16,7 @@ use crate::OsuPP;
|
|||||||
#[cfg(feature = "taiko")]
|
#[cfg(feature = "taiko")]
|
||||||
use crate::TaikoPP;
|
use crate::TaikoPP;
|
||||||
|
|
||||||
/// Calculator for pp on maps of any mode.
|
/// Performance calculator on maps of any mode.
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub enum AnyPP<'m> {
|
pub enum AnyPP<'m> {
|
||||||
#[cfg(feature = "fruits")]
|
#[cfg(feature = "fruits")]
|
||||||
@@ -43,23 +47,21 @@ impl<'m> AnyPP<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn calculate(self) -> PpResult {
|
pub fn calculate(self) -> PerformanceAttributes {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
#[cfg(feature = "fruits")]
|
||||||
Self::Fruits(f) => PpResult::Fruits(f.calculate()),
|
Self::Fruits(f) => PerformanceAttributes::Fruits(f.calculate()),
|
||||||
#[cfg(feature = "mania")]
|
#[cfg(feature = "mania")]
|
||||||
Self::Mania(m) => PpResult::Mania(m.calculate()),
|
Self::Mania(m) => PerformanceAttributes::Mania(m.calculate()),
|
||||||
#[cfg(feature = "osu")]
|
#[cfg(feature = "osu")]
|
||||||
Self::Osu(o) => PpResult::Osu(o.calculate()),
|
Self::Osu(o) => PerformanceAttributes::Osu(o.calculate()),
|
||||||
#[cfg(feature = "taiko")]
|
#[cfg(feature = "taiko")]
|
||||||
Self::Taiko(t) => PpResult::Taiko(t.calculate()),
|
Self::Taiko(t) => PerformanceAttributes::Taiko(t.calculate()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`AttributeProvider`] is implemented by [`StarResult`](crate::StarResult)
|
/// Provide the result of previous a difficulty or performance calculation.
|
||||||
/// and [`PpResult`](crate::PpResult) meaning you can give the result of a \
|
/// If you already calculated the attributes for the current map-mod combination,
|
||||||
/// star calculation or the result of a pp calculation.
|
|
||||||
/// If you already calculated the stars for the current map-mod combination,
|
|
||||||
/// 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(self, attributes: impl AttributeProvider) -> Self {
|
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 {
|
pub trait AttributeProvider {
|
||||||
fn attributes(self) -> StarResult;
|
fn attributes(self) -> DifficultyAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeProvider for StarResult {
|
impl AttributeProvider for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> StarResult {
|
fn attributes(self) -> DifficultyAttributes {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeProvider for PpResult {
|
impl AttributeProvider for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> StarResult {
|
fn attributes(self) -> DifficultyAttributes {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
#[cfg(feature = "fruits")]
|
||||||
Self::Fruits(f) => StarResult::Fruits(f.attributes),
|
Self::Fruits(f) => DifficultyAttributes::Fruits(f.attributes),
|
||||||
#[cfg(feature = "mania")]
|
#[cfg(feature = "mania")]
|
||||||
Self::Mania(m) => StarResult::Mania(m.attributes),
|
Self::Mania(m) => DifficultyAttributes::Mania(m.attributes),
|
||||||
#[cfg(feature = "osu")]
|
#[cfg(feature = "osu")]
|
||||||
Self::Osu(o) => StarResult::Osu(o.attributes),
|
Self::Osu(o) => DifficultyAttributes::Osu(o.attributes),
|
||||||
#[cfg(feature = "taiko")]
|
#[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
@@ -29,18 +29,18 @@ const COLOR_SKILL_MULTIPLIER: f64 = 0.01;
|
|||||||
const RHYTHM_SKILL_MULTIPLIER: f64 = 0.014;
|
const RHYTHM_SKILL_MULTIPLIER: f64 = 0.014;
|
||||||
const STAMINA_SKILL_MULTIPLIER: f64 = 0.02;
|
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.
|
/// 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,
|
map: &Beatmap,
|
||||||
mods: impl Mods,
|
mods: impl Mods,
|
||||||
passed_objects: Option<usize>,
|
passed_objects: Option<usize>,
|
||||||
) -> DifficultyAttributes {
|
) -> TaikoDifficultyAttributes {
|
||||||
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
|
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
|
||||||
|
|
||||||
if take < 2 {
|
if take < 2 {
|
||||||
return DifficultyAttributes { stars: 0.0 };
|
return TaikoDifficultyAttributes { stars: 0.0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// True if the object at that index is stamina cheese
|
// 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);
|
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.
|
/// evaluating the final strains, it just returns them as is.
|
||||||
///
|
///
|
||||||
/// Suitable to plot the difficulty of a map over time.
|
/// 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())
|
(a.powf(p) + b.powf(p) + c.powf(p)).powf(p.recip())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various data created through the star calculation.
|
/// The result of a difficulty calculation on an osu!taiko map.
|
||||||
/// This data is necessary to calculate PP.
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct DifficultyAttributes {
|
pub struct TaikoDifficultyAttributes {
|
||||||
pub stars: f64,
|
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)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct PerformanceAttributes {
|
pub struct TaikoPerformanceAttributes {
|
||||||
pub attributes: DifficultyAttributes,
|
pub attributes: TaikoDifficultyAttributes,
|
||||||
pub pp: f64,
|
pub pp: f64,
|
||||||
pub pp_acc: f64,
|
pub pp_acc: f64,
|
||||||
pub pp_strain: f64,
|
pub pp_strain: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceAttributes {
|
impl TaikoPerformanceAttributes {
|
||||||
/// Return the star value.
|
/// Return the star value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
|
|||||||
+14
-15
@@ -1,7 +1,7 @@
|
|||||||
use super::{stars, DifficultyAttributes, PerformanceAttributes};
|
use super::{stars, TaikoDifficultyAttributes, TaikoPerformanceAttributes};
|
||||||
use crate::{Beatmap, Mods, PpResult, StarResult};
|
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
|
||||||
|
|
||||||
/// Calculator for pp on osu!taiko maps.
|
/// Performance calculator on osu!taiko maps.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -61,10 +61,8 @@ impl<'m> TaikoPP<'m> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`TaikoAttributeProvider`] is implemented by `f32`, [`StarResult`](crate::StarResult),
|
/// Provide the result of previous a difficulty or performance calculation.
|
||||||
/// and by [`PpResult`](crate::PpResult) meaning you can give the star rating,
|
/// If you already calculated the attributes for the current map-mod combination,
|
||||||
/// 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,
|
|
||||||
/// 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 {
|
||||||
@@ -136,7 +134,7 @@ impl<'m> TaikoPP<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate all performance related values, including pp and stars.
|
/// Calculate all performance related values, including pp and stars.
|
||||||
pub fn calculate(mut self) -> PerformanceAttributes {
|
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
|
||||||
let stars = self
|
let stars = self
|
||||||
.stars
|
.stars
|
||||||
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars);
|
.unwrap_or_else(|| stars(self.map, self.mods, self.passed_objects).stars);
|
||||||
@@ -184,7 +182,7 @@ struct TaikoPPInner<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'m> TaikoPPInner<'m> {
|
impl<'m> TaikoPPInner<'m> {
|
||||||
fn calculate(self) -> PerformanceAttributes {
|
fn calculate(self) -> TaikoPerformanceAttributes {
|
||||||
let mut multiplier = 1.1;
|
let mut multiplier = 1.1;
|
||||||
|
|
||||||
if self.mods.nf() {
|
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;
|
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
|
||||||
|
|
||||||
PerformanceAttributes {
|
TaikoPerformanceAttributes {
|
||||||
attributes: DifficultyAttributes { stars: self.stars },
|
attributes: TaikoDifficultyAttributes { stars: self.stars },
|
||||||
pp,
|
pp,
|
||||||
pp_acc: acc_value,
|
pp_acc: acc_value,
|
||||||
pp_strain: strain_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)
|
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 {
|
pub trait TaikoAttributeProvider {
|
||||||
fn attributes(self) -> Option<f64>;
|
fn attributes(self) -> Option<f64>;
|
||||||
}
|
}
|
||||||
@@ -268,21 +267,21 @@ impl TaikoAttributeProvider for f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaikoAttributeProvider for DifficultyAttributes {
|
impl TaikoAttributeProvider for TaikoDifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
Some(self.stars)
|
Some(self.stars)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaikoAttributeProvider for PerformanceAttributes {
|
impl TaikoAttributeProvider for TaikoPerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
Some(self.attributes.stars)
|
Some(self.attributes.stars)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaikoAttributeProvider for StarResult {
|
impl TaikoAttributeProvider for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
@@ -294,7 +293,7 @@ impl TaikoAttributeProvider for StarResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaikoAttributeProvider for PpResult {
|
impl TaikoAttributeProvider for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> Option<f64> {
|
fn attributes(self) -> Option<f64> {
|
||||||
#[allow(irrefutable_let_patterns)]
|
#[allow(irrefutable_let_patterns)]
|
||||||
|
|||||||
Reference in New Issue
Block a user