Skip to content

Commit

Permalink
refactor: create performance calculators through traits (#33)
Browse files Browse the repository at this point in the history
* create perf calc through IntoPerformance traits

* added tests

* adjusted documentation

* fix doctest

* simplify From impls
  • Loading branch information
MaxOhn authored Apr 1, 2024
1 parent 616f2b5 commit 9acf85e
Show file tree
Hide file tree
Showing 24 changed files with 772 additions and 446 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ let diff_attrs = rosu_pp::Difficulty::new()
let stars = diff_attrs.stars();

// Calculate performance attributes
let perf_attrs = rosu_pp::Performance::from_attributes(diff_attrs)
let perf_attrs = rosu_pp::Performance::new(diff_attrs)
// To speed up the calculation, we used the previous attributes.
// **Note** that this should only be done if the map and all difficulty
// settings stay the same, otherwise the final attributes will be incorrect!
Expand Down
50 changes: 3 additions & 47 deletions src/any/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
taiko::{TaikoDifficultyAttributes, TaikoPerformanceAttributes},
};

use super::performance::Performance;
use super::performance::{into::IntoPerformance, Performance};

/// The result of a difficulty calculation based on the mode.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -43,7 +43,7 @@ impl DifficultyAttributes {

/// Returns a builder for performance calculation.
pub fn performance<'a>(self) -> Performance<'a> {
self.into()
self.into_performance()
}
}

Expand Down Expand Up @@ -103,7 +103,7 @@ impl PerformanceAttributes {

/// Returns a builder for performance calculation.
pub fn performance<'a>(self) -> Performance<'a> {
self.into()
self.into_performance()
}
}

Expand All @@ -112,47 +112,3 @@ impl From<PerformanceAttributes> for DifficultyAttributes {
attrs.difficulty_attributes()
}
}

/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait AttributeProvider {
/// Provide the actual difficulty attributes.
fn attributes(self) -> DifficultyAttributes;
}

impl AttributeProvider for DifficultyAttributes {
fn attributes(self) -> DifficultyAttributes {
self
}
}

impl AttributeProvider for PerformanceAttributes {
fn attributes(self) -> DifficultyAttributes {
match self {
Self::Osu(attrs) => DifficultyAttributes::Osu(attrs.difficulty),
Self::Taiko(attrs) => DifficultyAttributes::Taiko(attrs.difficulty),
Self::Catch(attrs) => DifficultyAttributes::Catch(attrs.difficulty),
Self::Mania(attrs) => DifficultyAttributes::Mania(attrs.difficulty),
}
}
}

macro_rules! impl_attr_provider {
( $mode:ident: $difficulty:ident, $performance:ident ) => {
impl AttributeProvider for $difficulty {
fn attributes(self) -> DifficultyAttributes {
DifficultyAttributes::$mode(self)
}
}

impl AttributeProvider for $performance {
fn attributes(self) -> DifficultyAttributes {
DifficultyAttributes::$mode(self.difficulty)
}
}
};
}

impl_attr_provider!(Catch: CatchDifficultyAttributes, CatchPerformanceAttributes);
impl_attr_provider!(Mania: ManiaDifficultyAttributes, ManiaPerformanceAttributes);
impl_attr_provider!(Osu: OsuDifficultyAttributes, OsuPerformanceAttributes);
impl_attr_provider!(Taiko: TaikoDifficultyAttributes, TaikoPerformanceAttributes);
8 changes: 6 additions & 2 deletions src/any/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
pub use self::{
attributes::{AttributeProvider, DifficultyAttributes, PerformanceAttributes},
attributes::{DifficultyAttributes, PerformanceAttributes},
difficulty::{
converted::ConvertedDifficulty, gradual::GradualDifficulty, inspect::InspectDifficulty,
Difficulty, ModsDependent,
},
performance::{gradual::GradualPerformance, HitResultPriority, Performance},
performance::{
gradual::GradualPerformance,
into::{IntoModePerformance, IntoPerformance},
HitResultPriority, Performance,
},
score_state::ScoreState,
strains::Strains,
};
Expand Down
157 changes: 157 additions & 0 deletions src/any/performance/into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use std::borrow::Cow;

use rosu_map::section::general::GameMode;

use crate::{
any::{DifficultyAttributes, PerformanceAttributes},
model::mode::IGameMode,
Beatmap, Converted, Performance,
};

/// Turning a type into the generic [`IGameMode`]'s performance calculator.
pub trait IntoModePerformance<'map, M: IGameMode> {
fn into_performance(self) -> M::Performance<'map>;
}

/// Turning a type into a performance calculator of any mode.
pub trait IntoPerformance<'a> {
fn into_performance(self) -> Performance<'a>;
}

macro_rules! impl_from_mode {
(
$(
$module:ident {
$mode:ident, $diff:ident, $perf:ident
}
,)*
) => {
$(
macro_rules! mode {
() => { crate::$module::$mode };
}

impl<'map> IntoModePerformance<'map, mode!()> for Converted<'map, mode!()> {
fn into_performance(self) -> <mode!() as IGameMode>::Performance<'map> {
<mode!() as IGameMode>::Performance::from_map_or_attrs(self.into())
}
}

impl<'map> IntoModePerformance<'map, mode!()> for &'map Converted<'_, mode!()> {
fn into_performance(self) -> <mode!() as IGameMode>::Performance<'map> {
<mode!() as IGameMode>::Performance::from_map_or_attrs(self.as_owned().into())
}
}

impl<'map> IntoModePerformance<'map, mode!()> for crate::$module::$diff {
fn into_performance(self) -> <mode!() as IGameMode>::Performance<'map> {
<mode!() as IGameMode>::Performance::from_map_or_attrs(self.into())
}
}

impl<'map> IntoModePerformance<'map, mode!()> for crate::$module::$perf {
fn into_performance(self) -> <mode!() as IGameMode>::Performance<'map> {
<mode!() as IGameMode>::Performance::from_map_or_attrs(self.difficulty.into())
}
}

impl<'map> IntoPerformance<'map> for Converted<'map, mode!()> {
fn into_performance(self) -> Performance<'map> {
Performance::$mode(
<Self as IntoModePerformance<'map, mode!()>>::into_performance(self)
)
}
}

impl<'map> IntoPerformance<'map> for &'map Converted<'_, mode!()> {
fn into_performance(self) -> Performance<'map> {
Performance::$mode(
<Self as IntoModePerformance<'map, mode!()>>::into_performance(self)
)
}
}

impl<'a> IntoPerformance<'a> for crate::$module::$diff {
fn into_performance(self) -> Performance<'a> {
Performance::$mode(
<Self as IntoModePerformance<'a, mode!()>>::into_performance(self)
)
}
}

impl<'a> IntoPerformance<'a> for crate::$module::$perf {
fn into_performance(self) -> Performance<'a> {
Performance::$mode(
<Self as IntoModePerformance<'a, mode!()>>::into_performance(self)
)
}
}
)*
};
}

impl_from_mode!(
osu {
Osu,
OsuDifficultyAttributes,
OsuPerformanceAttributes
},
taiko {
Taiko,
TaikoDifficultyAttributes,
TaikoPerformanceAttributes
},
catch {
Catch,
CatchDifficultyAttributes,
CatchPerformanceAttributes
},
mania {
Mania,
ManiaDifficultyAttributes,
ManiaPerformanceAttributes
},
);

impl<'a> IntoPerformance<'a> for Beatmap {
fn into_performance(self) -> Performance<'a> {
map_to_performance(self.mode, Cow::Owned(self))
}
}

impl<'map> IntoPerformance<'map> for &'map Beatmap {
fn into_performance(self) -> Performance<'map> {
map_to_performance(self.mode, Cow::Borrowed(self))
}
}

fn map_to_performance(mode: GameMode, map: Cow<'_, Beatmap>) -> Performance<'_> {
match mode {
GameMode::Osu => Performance::Osu(Converted::new(map).into()),
GameMode::Taiko => Performance::Taiko(Converted::new(map).into()),
GameMode::Catch => Performance::Catch(Converted::new(map).into()),
GameMode::Mania => Performance::Mania(Converted::new(map).into()),
}
}

impl<'a> IntoPerformance<'a> for DifficultyAttributes {
fn into_performance(self) -> Performance<'a> {
match self {
Self::Osu(attrs) => Performance::Osu(attrs.into()),
Self::Taiko(attrs) => Performance::Taiko(attrs.into()),
Self::Catch(attrs) => Performance::Catch(attrs.into()),
Self::Mania(attrs) => Performance::Mania(attrs.into()),
}
}
}

impl<'a> IntoPerformance<'a> for PerformanceAttributes {
fn into_performance(self) -> Performance<'a> {
match self {
Self::Osu(attrs) => Performance::Osu(attrs.difficulty.into()),
Self::Taiko(attrs) => Performance::Taiko(attrs.difficulty.into()),
Self::Catch(attrs) => Performance::Catch(attrs.difficulty.into()),
Self::Mania(attrs) => Performance::Mania(attrs.difficulty.into()),
}
}
}
Loading

0 comments on commit 9acf85e

Please sign in to comment.