use the same PpResult type for all modes
This commit is contained in:
+2
-2
@@ -414,7 +414,7 @@ mod tests {
|
||||
|
||||
let result = FruitsPP::new(&map).mods(0).calculate();
|
||||
|
||||
println!("Stars: {}", result.attributes.stars);
|
||||
println!("PP: {}", result.pp);
|
||||
println!("Stars: {}", result.stars());
|
||||
println!("PP: {}", result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
+25
-15
@@ -1,12 +1,24 @@
|
||||
use super::{stars, DifficultyAttributes};
|
||||
use crate::{Beatmap, Mods};
|
||||
use crate::{Beatmap, Mods, PpResult};
|
||||
|
||||
/// Basic struct containing the result of a PP calculation.
|
||||
/// In osu!ctb's case, this will be the pp value and the
|
||||
/// difficulty attributes created by star calculation.
|
||||
pub struct PpResult {
|
||||
pub pp: f32,
|
||||
pub attributes: DifficultyAttributes,
|
||||
pub trait FruitsAttributeProvider {
|
||||
fn attributes(self) -> Option<DifficultyAttributes>;
|
||||
}
|
||||
|
||||
impl FruitsAttributeProvider for DifficultyAttributes {
|
||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FruitsAttributeProvider for PpResult {
|
||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
||||
if let PpResult::Fruits { attributes, .. } = self {
|
||||
Some(attributes)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculator for pp on osu!ctb maps.
|
||||
@@ -42,13 +54,11 @@ impl<'m> FruitsPP<'m> {
|
||||
}
|
||||
}
|
||||
|
||||
/// [`DifficultyAttributes`](crate::fruits::DifficultyAttributes)
|
||||
/// stay the same for each map-mod combination.
|
||||
/// If you already calculated them, be sure to put them in here so
|
||||
/// that they don't have to be recalculated.
|
||||
///
|
||||
/// The final object after calling `calculation` will contain the
|
||||
/// attributes again for later reuse.
|
||||
/// [`FruitsAttributeProvider`] is implemented by [`DifficultyAttributes`](crate::fruits::DifficultyAttributes)
|
||||
/// 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,
|
||||
/// be sure to put them in here so that they don't have to be recalculated.
|
||||
#[inline]
|
||||
pub fn attributes(mut self, attributes: DifficultyAttributes) -> Self {
|
||||
self.attributes.replace(attributes);
|
||||
@@ -227,7 +237,7 @@ impl<'m> FruitsPP<'m> {
|
||||
pp *= 0.9;
|
||||
}
|
||||
|
||||
PpResult { pp, attributes }
|
||||
PpResult::Fruits { pp, attributes }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
+56
-16
@@ -33,34 +33,34 @@
|
||||
//! // `no_leniency` is the suggested default
|
||||
//! .calculate(rosu_pp::osu::no_leniency::stars);
|
||||
//!
|
||||
//! println!("PP: {}", result.pp);
|
||||
//! println!("PP: {}", result.pp());
|
||||
//!
|
||||
//! // If you intend to reuse the current map-mod combination,
|
||||
//! // make use of the attributes in the result.
|
||||
//! // make use of the previous result!
|
||||
//! // If attributes are given, then stars & co don't have to be recalculated.
|
||||
//! let next_result = OsuPP::new(&map)
|
||||
//! .mods(24) // HDHR
|
||||
//! .attributes(result.attributes)
|
||||
//! .attributes(result)
|
||||
//! .combo(543)
|
||||
//! .misses(5)
|
||||
//! .n50(3)
|
||||
//! .passed_objects(612)
|
||||
//! .accuracy(97.5)
|
||||
//! .calculate(rosu_pp::osu::no_leniency::stars);
|
||||
//!
|
||||
//! println!("Next PP: {}", next_result.pp);
|
||||
//! println!("Next PP: {}", next_result.pp());
|
||||
//! },
|
||||
//! GameMode::TKO => {
|
||||
//! let result = TaikoPP::new(&map)
|
||||
//! .mods(64) // DT
|
||||
//! .combo(555)
|
||||
//! .misses(10)
|
||||
//! .passed_objects(600)
|
||||
//! .accuracy(95.12345)
|
||||
//! .calculate();
|
||||
//!
|
||||
//! println!("Stars: {} | PP: {}", result.stars, result.pp);
|
||||
//! println!("Stars: {} | PP: {}", result.stars(), result.pp());
|
||||
//! }
|
||||
//! GameMode::MNA | GameMode::CTB => unimplemented!(),
|
||||
//! GameMode::MNA | GameMode::CTB => panic!("do your thing"),
|
||||
//! }
|
||||
//!
|
||||
//! // If all you want is the map's stars or max pp,
|
||||
@@ -141,15 +141,55 @@ impl BeatmapExt for Beatmap {
|
||||
}
|
||||
fn max_pp(&self, mods: u32) -> f32 {
|
||||
match self.mode {
|
||||
GameMode::STD => {
|
||||
OsuPP::new(self)
|
||||
.mods(mods)
|
||||
.calculate(osu::no_leniency::stars)
|
||||
.pp
|
||||
}
|
||||
GameMode::MNA => ManiaPP::new(self).mods(mods).calculate().pp,
|
||||
GameMode::TKO => TaikoPP::new(self).mods(mods).calculate().pp,
|
||||
GameMode::CTB => FruitsPP::new(self).mods(mods).calculate().pp,
|
||||
GameMode::STD => OsuPP::new(self)
|
||||
.mods(mods)
|
||||
.calculate(osu::no_leniency::stars)
|
||||
.pp(),
|
||||
GameMode::MNA => ManiaPP::new(self).mods(mods).calculate().pp(),
|
||||
GameMode::TKO => TaikoPP::new(self).mods(mods).calculate().pp(),
|
||||
GameMode::CTB => FruitsPP::new(self).mods(mods).calculate().pp(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic enum containing the result of a PP calculation depending on the mode.
|
||||
pub enum PpResult {
|
||||
Fruits {
|
||||
pp: f32,
|
||||
attributes: fruits::DifficultyAttributes,
|
||||
},
|
||||
Mania {
|
||||
pp: f32,
|
||||
stars: f32,
|
||||
},
|
||||
Osu {
|
||||
pp: f32,
|
||||
attributes: osu::DifficultyAttributes,
|
||||
},
|
||||
Taiko {
|
||||
pp: f32,
|
||||
stars: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl PpResult {
|
||||
/// The final pp value.
|
||||
pub fn pp(&self) -> f32 {
|
||||
match self {
|
||||
Self::Fruits { pp, .. } => *pp,
|
||||
Self::Mania { pp, .. } => *pp,
|
||||
Self::Osu { pp, .. } => *pp,
|
||||
Self::Taiko { pp, .. } => *pp,
|
||||
}
|
||||
}
|
||||
|
||||
/// The final star value.
|
||||
pub fn stars(&self) -> f32 {
|
||||
match self {
|
||||
Self::Fruits { attributes, .. } => attributes.stars,
|
||||
Self::Mania { stars, .. } => *stars,
|
||||
Self::Osu { attributes, .. } => attributes.stars,
|
||||
Self::Taiko { stars, .. } => *stars,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -101,7 +101,7 @@ mod tests {
|
||||
|
||||
let result = ManiaPP::new(&map).mods(256).calculate();
|
||||
|
||||
println!("Stars: {}", result.stars);
|
||||
println!("PP: {}", result.pp);
|
||||
println!("Stars: {}", result.stars());
|
||||
println!("PP: {}", result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
+29
-12
@@ -1,11 +1,24 @@
|
||||
use super::stars;
|
||||
use crate::{Beatmap, Mods};
|
||||
use crate::{Beatmap, Mods, PpResult};
|
||||
|
||||
/// Basic struct containing the result of a PP calculation.
|
||||
/// In osu!mania's case, this will be just the pp value and the star value.
|
||||
pub struct PpResult {
|
||||
pub pp: f32,
|
||||
pub stars: f32,
|
||||
pub trait ManiaStarProvider {
|
||||
fn attributes(self) -> Option<f32>;
|
||||
}
|
||||
|
||||
impl ManiaStarProvider for f32 {
|
||||
fn attributes(self) -> Option<f32> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ManiaStarProvider for PpResult {
|
||||
fn attributes(self) -> Option<f32> {
|
||||
if let PpResult::Mania { stars, .. } = self {
|
||||
Some(stars)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculator for pp on osu!mania maps.
|
||||
@@ -29,12 +42,16 @@ impl<'m> ManiaPP<'m> {
|
||||
}
|
||||
}
|
||||
|
||||
/// If you already know the stars of the map with the current mods,
|
||||
/// you should specify them so that they don't have to be calculated
|
||||
/// again while calculating PP.
|
||||
/// [`ManiaStarsProvider`] is implemented by `f32`
|
||||
/// 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,
|
||||
/// be sure to put them in here so that they don't have to be recalculated.
|
||||
#[inline]
|
||||
pub fn stars(mut self, stars: f32) -> Self {
|
||||
self.stars.replace(stars);
|
||||
pub fn stars(mut self, stars: impl ManiaStarProvider) -> Self {
|
||||
if let Some(stars) = stars.attributes() {
|
||||
self.stars.replace(stars);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
@@ -116,7 +133,7 @@ impl<'m> ManiaPP<'m> {
|
||||
|
||||
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
|
||||
|
||||
PpResult { pp, stars }
|
||||
PpResult::Mania { pp, stars }
|
||||
}
|
||||
|
||||
fn compute_strain(&self, score: f32, stars: f32) -> f32 {
|
||||
|
||||
+32
-20
@@ -1,18 +1,30 @@
|
||||
use super::DifficultyAttributes as Attributes;
|
||||
use crate::{Beatmap, Mods};
|
||||
use super::DifficultyAttributes;
|
||||
use crate::{Beatmap, Mods, PpResult};
|
||||
|
||||
/// Basic struct containing the result of a PP calculation.
|
||||
/// In osu!standard's case, this will be the pp value and the
|
||||
/// difficulty attributes created by star calculation.
|
||||
pub struct PpResult {
|
||||
pub pp: f32,
|
||||
pub attributes: Attributes,
|
||||
pub trait OsuAttributeProvider {
|
||||
fn attributes(self) -> Option<DifficultyAttributes>;
|
||||
}
|
||||
|
||||
impl OsuAttributeProvider for DifficultyAttributes {
|
||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl OsuAttributeProvider for PpResult {
|
||||
fn attributes(self) -> Option<DifficultyAttributes> {
|
||||
if let PpResult::Osu { attributes, .. } = self {
|
||||
Some(attributes)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculator for pp on osu!standard maps.
|
||||
pub struct OsuPP<'m> {
|
||||
map: &'m Beatmap,
|
||||
attributes: Option<Attributes>,
|
||||
attributes: Option<DifficultyAttributes>,
|
||||
mods: u32,
|
||||
combo: Option<usize>,
|
||||
acc: Option<f32>,
|
||||
@@ -42,16 +54,16 @@ impl<'m> OsuPP<'m> {
|
||||
}
|
||||
}
|
||||
|
||||
/// [`DifficultyAttributes`](crate::osu::DifficultyAttributes)
|
||||
/// stay the same for each map-mod combination.
|
||||
/// If you already calculated them, be sure to put them in here so
|
||||
/// that they don't have to be recalculated.
|
||||
///
|
||||
/// The final object after calling `calculation` will contain the
|
||||
/// attributes again for later reuse.
|
||||
/// [`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.
|
||||
/// 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: Attributes) -> Self {
|
||||
self.attributes.replace(attributes);
|
||||
pub fn attributes(mut self, attributes: impl OsuAttributeProvider) -> Self {
|
||||
if let Some(attributes) = attributes.attributes() {
|
||||
self.attributes.replace(attributes);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
@@ -158,7 +170,7 @@ impl<'m> OsuPP<'m> {
|
||||
/// The default is suggested to be [`stars`](crate::osu::no_leniency::stars).
|
||||
pub fn calculate(
|
||||
mut self,
|
||||
stars_func: impl FnOnce(&Beatmap, u32, Option<usize>) -> Attributes,
|
||||
stars_func: impl FnOnce(&Beatmap, u32, Option<usize>) -> DifficultyAttributes,
|
||||
) -> PpResult {
|
||||
if self.attributes.is_none() {
|
||||
let attributes = stars_func(self.map, self.mods, self.passed_objects);
|
||||
@@ -215,7 +227,7 @@ impl<'m> OsuPP<'m> {
|
||||
.powf(1.0 / 1.1)
|
||||
* multiplier;
|
||||
|
||||
PpResult {
|
||||
PpResult::Osu {
|
||||
pp,
|
||||
attributes: self.attributes.unwrap(),
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ mod tests {
|
||||
|
||||
let result = OsuPP::new(&map).mods(64).calculate(stars);
|
||||
|
||||
println!("Stars: {}", result.attributes.stars);
|
||||
println!("PP: {}", result.pp);
|
||||
println!("Stars: {}", result.stars());
|
||||
println!("PP: {}", result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ mod tests {
|
||||
|
||||
let result = OsuPP::new(&map).mods(256).calculate(stars);
|
||||
|
||||
println!("Stars: {}", result.attributes.stars);
|
||||
println!("PP: {}", result.pp);
|
||||
println!("Stars: {}", result.stars());
|
||||
println!("PP: {}", result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ mod tests {
|
||||
|
||||
let result = OsuPP::new(&map).mods(2).calculate(stars);
|
||||
|
||||
println!("Stars: {}", result.attributes.stars);
|
||||
println!("PP: {}", result.pp);
|
||||
println!("Stars: {}", result.stars());
|
||||
println!("PP: {}", result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -190,7 +190,7 @@ mod tests {
|
||||
|
||||
let result = TaikoPP::new(&map).mods(64).calculate();
|
||||
|
||||
println!("Stars: {}", result.stars);
|
||||
println!("PP: {}", result.pp);
|
||||
println!("Stars: {}", result.stars());
|
||||
println!("PP: {}", result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
+29
-12
@@ -1,10 +1,23 @@
|
||||
use crate::{Beatmap, Mods};
|
||||
use crate::{Beatmap, Mods, PpResult};
|
||||
|
||||
/// Basic struct containing the result of a PP calculation.
|
||||
/// In osu!taiko's case, this will be just the pp value and the star value.
|
||||
pub struct PpResult {
|
||||
pub pp: f32,
|
||||
pub stars: f32,
|
||||
pub trait TaikoStarProvider {
|
||||
fn attributes(self) -> Option<f32>;
|
||||
}
|
||||
|
||||
impl TaikoStarProvider for f32 {
|
||||
fn attributes(self) -> Option<f32> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TaikoStarProvider for PpResult {
|
||||
fn attributes(self) -> Option<f32> {
|
||||
if let PpResult::Taiko { stars, .. } = self {
|
||||
Some(stars)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculator for pp on osu!taiko maps.
|
||||
@@ -36,12 +49,16 @@ impl<'m> TaikoPP<'m> {
|
||||
}
|
||||
}
|
||||
|
||||
/// If you already know the stars of the map with the current mods,
|
||||
/// you should specify them so that they don't have to be calculated
|
||||
/// again while calculating PP.
|
||||
/// [`TaikoStarProvider`] is implemented by `f32`
|
||||
/// and by [`PpResult`](crate::PpResult) meaning you can give the
|
||||
/// result of a star calculation or 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.
|
||||
#[inline]
|
||||
pub fn stars(mut self, stars: f32) -> Self {
|
||||
self.stars.replace(stars);
|
||||
pub fn stars(mut self, stars: impl TaikoStarProvider) -> Self {
|
||||
if let Some(stars) = stars.attributes() {
|
||||
self.stars.replace(stars);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
@@ -109,7 +126,7 @@ impl<'m> TaikoPP<'m> {
|
||||
|
||||
let pp = (strain_value.powf(1.1) + acc_value.powf(1.1)).powf(1.0 / 1.1) * multiplier;
|
||||
|
||||
PpResult { stars, pp }
|
||||
PpResult::Taiko { stars, pp }
|
||||
}
|
||||
|
||||
fn compute_strain_value(&self, stars: f32) -> f32 {
|
||||
|
||||
Reference in New Issue
Block a user