refactor: store either map or attrs in perf calc
This commit is contained in:
+59
-51
@@ -1,5 +1,8 @@
|
||||
use super::{CatchDifficultyAttributes, CatchPerformanceAttributes, CatchScoreState, CatchStars};
|
||||
use crate::{Beatmap, DifficultyAttributes, Mods, OsuPP, PerformanceAttributes};
|
||||
use crate::{
|
||||
util::{MapOrElse, MapRef},
|
||||
Beatmap, DifficultyAttributes, Mods, OsuPP, PerformanceAttributes,
|
||||
};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Performance calculator on osu!catch maps.
|
||||
@@ -34,8 +37,7 @@ use std::cmp::Ordering;
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct CatchPP<'map> {
|
||||
pub(crate) map: &'map Beatmap,
|
||||
pub(crate) attributes: Option<CatchDifficultyAttributes>,
|
||||
pub(crate) map_or_attrs: MapOrElse<MapRef<'map>, CatchDifficultyAttributes>,
|
||||
pub(crate) mods: u32,
|
||||
pub(crate) acc: Option<f64>,
|
||||
pub(crate) combo: Option<usize>,
|
||||
@@ -54,8 +56,7 @@ impl<'map> CatchPP<'map> {
|
||||
#[inline]
|
||||
pub fn new(map: &'map Beatmap) -> Self {
|
||||
Self {
|
||||
map,
|
||||
attributes: None,
|
||||
map_or_attrs: MapOrElse::from(map),
|
||||
mods: 0,
|
||||
acc: None,
|
||||
combo: None,
|
||||
@@ -70,13 +71,15 @@ impl<'map> CatchPP<'map> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: new_with_attributes function
|
||||
|
||||
/// Provide the result of a previous 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 CatchAttributeProvider) -> Self {
|
||||
if let Some(attributes) = attributes.attributes() {
|
||||
self.attributes = Some(attributes);
|
||||
if let Some(attrs) = attributes.attributes() {
|
||||
self.map_or_attrs = MapOrElse::Else(attrs);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -195,9 +198,13 @@ impl<'map> CatchPP<'map> {
|
||||
|
||||
/// Create the [`CatchScoreState`] that will be used for performance calculation.
|
||||
pub fn generate_state(&mut self) -> CatchScoreState {
|
||||
let attrs = match self.attributes {
|
||||
Some(ref attrs) => attrs,
|
||||
None => self.attributes.insert(self.generate_attributes()),
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => {
|
||||
let attrs = self.generate_attributes(map.as_ref());
|
||||
|
||||
self.map_or_attrs.else_or_insert(attrs)
|
||||
}
|
||||
MapOrElse::Else(ref attrs) => attrs,
|
||||
};
|
||||
|
||||
let n_misses = self
|
||||
@@ -332,10 +339,10 @@ impl<'map> CatchPP<'map> {
|
||||
pub fn calculate(mut self) -> CatchPerformanceAttributes {
|
||||
let state = self.generate_state();
|
||||
|
||||
let attrs = self
|
||||
.attributes
|
||||
.take()
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => self.generate_attributes(map.as_ref()),
|
||||
MapOrElse::Else(attrs) => attrs,
|
||||
};
|
||||
|
||||
let inner = CatchPPInner {
|
||||
attrs,
|
||||
@@ -346,8 +353,8 @@ impl<'map> CatchPP<'map> {
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> CatchDifficultyAttributes {
|
||||
let mut calculator = CatchStars::new(self.map).mods(self.mods);
|
||||
fn generate_attributes(&self, map: &Beatmap) -> CatchDifficultyAttributes {
|
||||
let mut calculator = CatchStars::new(map).mods(self.mods);
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
@@ -359,6 +366,42 @@ impl<'map> CatchPP<'map> {
|
||||
|
||||
calculator.calculate()
|
||||
}
|
||||
|
||||
/// TODO: docs
|
||||
#[inline]
|
||||
pub fn try_from_osu(osu: OsuPP<'map>) -> Option<Self> {
|
||||
let OsuPP {
|
||||
map_or_attrs,
|
||||
mods,
|
||||
acc,
|
||||
combo,
|
||||
n300,
|
||||
n100,
|
||||
n50,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority: _,
|
||||
} = osu;
|
||||
|
||||
let MapOrElse::Map(map) = map_or_attrs else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
map_or_attrs: MapOrElse::Map(map),
|
||||
mods,
|
||||
acc,
|
||||
combo,
|
||||
n_fruits: n300,
|
||||
n_droplets: n100,
|
||||
n_tiny_droplets: n50,
|
||||
n_tiny_droplet_misses: None,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct CatchPPInner {
|
||||
@@ -442,41 +485,6 @@ impl CatchPPInner {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'map> From<OsuPP<'map>> for CatchPP<'map> {
|
||||
#[inline]
|
||||
fn from(osu: OsuPP<'map>) -> Self {
|
||||
let OsuPP {
|
||||
map,
|
||||
attributes: _,
|
||||
mods,
|
||||
acc,
|
||||
combo,
|
||||
n300,
|
||||
n100,
|
||||
n50,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority: _,
|
||||
} = osu;
|
||||
|
||||
Self {
|
||||
map,
|
||||
attributes: None,
|
||||
mods,
|
||||
acc,
|
||||
combo,
|
||||
n_fruits: n300,
|
||||
n_droplets: n100,
|
||||
n_tiny_droplets: n50,
|
||||
n_tiny_droplet_misses: None,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accuracy(
|
||||
n_fruits: usize,
|
||||
n_droplets: usize,
|
||||
|
||||
+63
-53
@@ -2,7 +2,8 @@ use std::borrow::Cow;
|
||||
|
||||
use super::{ManiaDifficultyAttributes, ManiaPerformanceAttributes, ManiaScoreState, ManiaStars};
|
||||
use crate::{
|
||||
Beatmap, DifficultyAttributes, GameMode, HitResultPriority, Mods, OsuPP, PerformanceAttributes,
|
||||
util::MapOrElse, Beatmap, DifficultyAttributes, GameMode, HitResultPriority, Mods, OsuPP,
|
||||
PerformanceAttributes,
|
||||
};
|
||||
|
||||
/// Performance calculator on osu!mania maps.
|
||||
@@ -41,9 +42,8 @@ use crate::{
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct ManiaPP<'map> {
|
||||
map: Cow<'map, Beatmap>,
|
||||
map_or_attrs: MapOrElse<Cow<'map, Beatmap>, ManiaDifficultyAttributes>,
|
||||
is_convert: bool,
|
||||
attributes: Option<ManiaDifficultyAttributes>,
|
||||
mods: u32,
|
||||
passed_objects: Option<usize>,
|
||||
clock_rate: Option<f64>,
|
||||
@@ -67,8 +67,7 @@ impl<'map> ManiaPP<'map> {
|
||||
|
||||
Self {
|
||||
is_convert: matches!(map, Cow::Owned(_)),
|
||||
map,
|
||||
attributes: None,
|
||||
map_or_attrs: MapOrElse::Map(map),
|
||||
mods: 0,
|
||||
passed_objects: None,
|
||||
clock_rate: None,
|
||||
@@ -89,7 +88,7 @@ impl<'map> ManiaPP<'map> {
|
||||
#[inline]
|
||||
pub fn attributes(mut self, attrs: impl ManiaAttributeProvider) -> Self {
|
||||
if let Some(attrs) = attrs.attributes() {
|
||||
self.attributes = Some(attrs);
|
||||
self.map_or_attrs = MapOrElse::Else(attrs);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -227,8 +226,17 @@ impl<'map> ManiaPP<'map> {
|
||||
}
|
||||
|
||||
/// Create the [`ManiaScoreState`] that will be used for performance calculation.
|
||||
pub fn generate_state(&self) -> ManiaScoreState {
|
||||
let n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len());
|
||||
pub fn generate_state(&mut self) -> ManiaScoreState {
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => {
|
||||
let attrs = self.generate_attributes(&map);
|
||||
|
||||
self.map_or_attrs.else_or_insert(attrs)
|
||||
}
|
||||
MapOrElse::Else(ref attrs) => attrs,
|
||||
};
|
||||
|
||||
let n_objects = attrs.n_objects();
|
||||
let priority = self.hitresult_priority.unwrap_or_default();
|
||||
|
||||
let n_misses = self.n_misses.map_or(0, |n| n.min(n_objects));
|
||||
@@ -700,12 +708,13 @@ impl<'map> ManiaPP<'map> {
|
||||
}
|
||||
|
||||
/// Calculate all performance related values, including pp and stars.
|
||||
pub fn calculate(self) -> ManiaPerformanceAttributes {
|
||||
pub fn calculate(mut self) -> ManiaPerformanceAttributes {
|
||||
let state = self.generate_state();
|
||||
|
||||
let attrs = self
|
||||
.attributes
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => self.generate_attributes(map),
|
||||
MapOrElse::Else(attrs) => attrs,
|
||||
};
|
||||
|
||||
let inner = ManiaPpInner {
|
||||
mods: self.mods,
|
||||
@@ -716,8 +725,8 @@ impl<'map> ManiaPP<'map> {
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> ManiaDifficultyAttributes {
|
||||
let mut calculator = ManiaStars::new(self.map.as_ref())
|
||||
fn generate_attributes(&self, map: &Beatmap) -> ManiaDifficultyAttributes {
|
||||
let mut calculator = ManiaStars::new(map)
|
||||
.mods(self.mods)
|
||||
.is_convert(self.is_convert);
|
||||
|
||||
@@ -731,6 +740,46 @@ impl<'map> ManiaPP<'map> {
|
||||
|
||||
calculator.calculate()
|
||||
}
|
||||
|
||||
/// TODO: docs
|
||||
#[inline]
|
||||
pub fn try_from_osu(osu: OsuPP<'map>) -> Option<Self> {
|
||||
let OsuPP {
|
||||
map_or_attrs,
|
||||
mods,
|
||||
acc,
|
||||
combo: _,
|
||||
n300,
|
||||
n100,
|
||||
n50,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority,
|
||||
} = osu;
|
||||
|
||||
let MapOrElse::Map(map) = map_or_attrs else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let map = map.into_inner().convert_mode(GameMode::Mania);
|
||||
|
||||
Some(Self {
|
||||
is_convert: matches!(map, Cow::Owned(_)),
|
||||
map_or_attrs: MapOrElse::Map(map),
|
||||
mods,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
n320: None,
|
||||
n300,
|
||||
n200: None,
|
||||
n100,
|
||||
n50,
|
||||
n_misses,
|
||||
acc,
|
||||
hitresult_priority,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ManiaPpInner {
|
||||
@@ -796,45 +845,6 @@ impl ManiaPpInner {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'map> From<OsuPP<'map>> for ManiaPP<'map> {
|
||||
#[inline]
|
||||
fn from(osu: OsuPP<'map>) -> Self {
|
||||
let OsuPP {
|
||||
map,
|
||||
attributes: _,
|
||||
mods,
|
||||
acc,
|
||||
combo: _,
|
||||
n300,
|
||||
n100,
|
||||
n50,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority,
|
||||
} = osu;
|
||||
|
||||
let map = map.convert_mode(GameMode::Mania);
|
||||
|
||||
Self {
|
||||
is_convert: matches!(map, Cow::Owned(_)),
|
||||
map,
|
||||
attributes: None,
|
||||
mods,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
n320: None,
|
||||
n300,
|
||||
n200: None,
|
||||
n100,
|
||||
n50,
|
||||
n_misses,
|
||||
acc,
|
||||
hitresult_priority,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn custom_accuracy(
|
||||
n320: usize,
|
||||
n300: usize,
|
||||
|
||||
+27
-23
@@ -2,8 +2,9 @@ use super::{
|
||||
OsuDifficultyAttributes, OsuPerformanceAttributes, OsuScoreState, PERFORMANCE_BASE_MULTIPLIER,
|
||||
};
|
||||
use crate::{
|
||||
AnyPP, Beatmap, DifficultyAttributes, GameMode, HitResultPriority, Mods, OsuStars,
|
||||
PerformanceAttributes,
|
||||
util::{MapOrElse, MapRef},
|
||||
AnyPP, Beatmap, CatchPP, DifficultyAttributes, GameMode, HitResultPriority, ManiaPP, Mods,
|
||||
OsuStars, PerformanceAttributes, TaikoPP,
|
||||
};
|
||||
|
||||
/// Performance calculator on osu!standard maps.
|
||||
@@ -38,8 +39,7 @@ use crate::{
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct OsuPP<'map> {
|
||||
pub(crate) map: &'map Beatmap,
|
||||
pub(crate) attributes: Option<OsuDifficultyAttributes>,
|
||||
pub(crate) map_or_attrs: MapOrElse<MapRef<'map>, OsuDifficultyAttributes>,
|
||||
pub(crate) mods: u32,
|
||||
pub(crate) acc: Option<f64>,
|
||||
pub(crate) combo: Option<usize>,
|
||||
@@ -58,8 +58,7 @@ impl<'map> OsuPP<'map> {
|
||||
#[inline]
|
||||
pub fn new(map: &'map Beatmap) -> Self {
|
||||
Self {
|
||||
map,
|
||||
attributes: None,
|
||||
map_or_attrs: MapOrElse::from(map),
|
||||
mods: 0,
|
||||
acc: None,
|
||||
combo: None,
|
||||
@@ -76,12 +75,12 @@ impl<'map> OsuPP<'map> {
|
||||
|
||||
/// Convert the map into another mode.
|
||||
#[inline]
|
||||
pub fn mode(self, mode: GameMode) -> AnyPP<'map> {
|
||||
pub fn try_mode(self, mode: GameMode) -> Option<AnyPP<'map>> {
|
||||
match mode {
|
||||
GameMode::Osu => AnyPP::Osu(self),
|
||||
GameMode::Taiko => AnyPP::Taiko(self.into()),
|
||||
GameMode::Catch => AnyPP::Catch(self.into()),
|
||||
GameMode::Mania => AnyPP::Mania(self.into()),
|
||||
GameMode::Osu => Some(AnyPP::Osu(self)),
|
||||
GameMode::Taiko => TaikoPP::try_from_osu(self).map(AnyPP::Taiko),
|
||||
GameMode::Catch => CatchPP::try_from_osu(self).map(AnyPP::Catch),
|
||||
GameMode::Mania => ManiaPP::try_from_osu(self).map(AnyPP::Mania),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +89,8 @@ impl<'map> OsuPP<'map> {
|
||||
/// be sure to put them in here so that they don't have to be recalculated.
|
||||
#[inline]
|
||||
pub fn attributes(mut self, attributes: impl OsuAttributeProvider) -> Self {
|
||||
if let Some(attributes) = attributes.attributes() {
|
||||
self.attributes = Some(attributes);
|
||||
if let Some(attrs) = attributes.attributes() {
|
||||
self.map_or_attrs = MapOrElse::Else(attrs);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -210,12 +209,17 @@ impl<'map> OsuPP<'map> {
|
||||
|
||||
/// Create the [`OsuScoreState`] that will be used for performance calculation.
|
||||
pub fn generate_state(&mut self) -> OsuScoreState {
|
||||
let max_combo = match self.attributes {
|
||||
Some(ref attrs) => attrs.max_combo,
|
||||
None => self.attributes.insert(self.generate_attributes()).max_combo,
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => {
|
||||
let attrs = self.generate_attributes(map.as_ref());
|
||||
|
||||
self.map_or_attrs.else_or_insert(attrs)
|
||||
}
|
||||
MapOrElse::Else(ref attrs) => attrs,
|
||||
};
|
||||
|
||||
let n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len());
|
||||
let max_combo = attrs.max_combo;
|
||||
let n_objects = self.passed_objects.unwrap_or(attrs.n_objects());
|
||||
let priority = self.hitresult_priority.unwrap_or_default();
|
||||
|
||||
let n_misses = self.n_misses.map_or(0, |n| n.min(n_objects));
|
||||
@@ -386,10 +390,10 @@ impl<'map> OsuPP<'map> {
|
||||
pub fn calculate(mut self) -> OsuPerformanceAttributes {
|
||||
let state = self.generate_state();
|
||||
|
||||
let attrs = self
|
||||
.attributes
|
||||
.take()
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => self.generate_attributes(map.as_ref()),
|
||||
MapOrElse::Else(attrs) => attrs,
|
||||
};
|
||||
|
||||
let effective_miss_count = calculate_effective_misses(&attrs, &state);
|
||||
|
||||
@@ -404,8 +408,8 @@ impl<'map> OsuPP<'map> {
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> OsuDifficultyAttributes {
|
||||
let mut calculator = OsuStars::new(self.map).mods(self.mods);
|
||||
fn generate_attributes(&self, map: &Beatmap) -> OsuDifficultyAttributes {
|
||||
let mut calculator = OsuStars::new(map).mods(self.mods);
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
|
||||
@@ -86,16 +86,12 @@ impl<'map> AnyPP<'map> {
|
||||
}
|
||||
|
||||
/// If the map is an osu!standard map, convert it to another mode.
|
||||
// TODO: option necessary?
|
||||
#[inline]
|
||||
pub fn mode(self, mode: GameMode) -> Self {
|
||||
pub fn try_mode(self, mode: GameMode) -> Option<Self> {
|
||||
match self {
|
||||
AnyPP::Osu(o) => match mode {
|
||||
GameMode::Osu => AnyPP::Osu(o),
|
||||
GameMode::Taiko => AnyPP::Taiko(o.into()),
|
||||
GameMode::Catch => AnyPP::Catch(o.into()),
|
||||
GameMode::Mania => AnyPP::Mania(o.into()),
|
||||
},
|
||||
other => other,
|
||||
AnyPP::Osu(o) => o.try_mode(mode),
|
||||
other => Some(other),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+59
-53
@@ -2,7 +2,8 @@ use std::borrow::Cow;
|
||||
|
||||
use super::{TaikoDifficultyAttributes, TaikoPerformanceAttributes, TaikoScoreState, TaikoStars};
|
||||
use crate::{
|
||||
Beatmap, DifficultyAttributes, GameMode, HitResultPriority, Mods, OsuPP, PerformanceAttributes,
|
||||
util::MapOrElse, Beatmap, DifficultyAttributes, GameMode, HitResultPriority, Mods, OsuPP,
|
||||
PerformanceAttributes,
|
||||
};
|
||||
|
||||
/// Performance calculator on osu!taiko maps.
|
||||
@@ -37,9 +38,8 @@ use crate::{
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct TaikoPP<'map> {
|
||||
pub(crate) map: Cow<'map, Beatmap>,
|
||||
pub(crate) map_or_attrs: MapOrElse<Cow<'map, Beatmap>, TaikoDifficultyAttributes>,
|
||||
is_convert: bool,
|
||||
attributes: Option<TaikoDifficultyAttributes>,
|
||||
mods: u32,
|
||||
combo: Option<usize>,
|
||||
acc: Option<f64>,
|
||||
@@ -60,8 +60,7 @@ impl<'map> TaikoPP<'map> {
|
||||
|
||||
Self {
|
||||
is_convert: matches!(map, Cow::Owned(_)),
|
||||
map,
|
||||
attributes: None,
|
||||
map_or_attrs: MapOrElse::Map(map),
|
||||
mods: 0,
|
||||
combo: None,
|
||||
acc: None,
|
||||
@@ -80,7 +79,7 @@ impl<'map> TaikoPP<'map> {
|
||||
#[inline]
|
||||
pub fn attributes(mut self, attrs: impl TaikoAttributeProvider) -> Self {
|
||||
if let Some(attrs) = attrs.attributes() {
|
||||
self.attributes = Some(attrs);
|
||||
self.map_or_attrs = MapOrElse::Else(attrs);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -133,7 +132,7 @@ impl<'map> TaikoPP<'map> {
|
||||
/// Specify the amount of misses of the play.
|
||||
#[inline]
|
||||
pub fn n_misses(mut self, n_misses: usize) -> Self {
|
||||
self.n_misses = Some(n_misses.min(self.map.n_circles as usize));
|
||||
self.n_misses = Some(n_misses);
|
||||
|
||||
self
|
||||
}
|
||||
@@ -199,11 +198,17 @@ impl<'map> TaikoPP<'map> {
|
||||
|
||||
/// Create the [`TaikoScoreState`] that will be used for performance calculation.
|
||||
pub fn generate_state(&mut self) -> TaikoScoreState {
|
||||
let max_combo = match self.attributes {
|
||||
Some(ref attrs) => attrs.max_combo,
|
||||
None => self.attributes.insert(self.generate_attributes()).max_combo,
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => {
|
||||
let attrs = self.generate_attributes(&map);
|
||||
|
||||
self.map_or_attrs.else_or_insert(attrs)
|
||||
}
|
||||
MapOrElse::Else(ref attrs) => attrs,
|
||||
};
|
||||
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let total_result_count = if let Some(passed_objects) = self.passed_objects {
|
||||
max_combo.min(passed_objects)
|
||||
} else {
|
||||
@@ -286,10 +291,10 @@ impl<'map> TaikoPP<'map> {
|
||||
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
|
||||
let state = self.generate_state();
|
||||
|
||||
let attrs = self
|
||||
.attributes
|
||||
.take()
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
let attrs = match self.map_or_attrs {
|
||||
MapOrElse::Map(ref map) => self.generate_attributes(map),
|
||||
MapOrElse::Else(attrs) => attrs,
|
||||
};
|
||||
|
||||
let inner = TaikoPpInner {
|
||||
mods: self.mods,
|
||||
@@ -300,8 +305,8 @@ impl<'map> TaikoPP<'map> {
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> TaikoDifficultyAttributes {
|
||||
let mut calculator = TaikoStars::new(self.map.as_ref())
|
||||
fn generate_attributes(&self, map: &Beatmap) -> TaikoDifficultyAttributes {
|
||||
let mut calculator = TaikoStars::new(map)
|
||||
.mods(self.mods)
|
||||
.is_convert(self.is_convert);
|
||||
|
||||
@@ -315,6 +320,44 @@ impl<'map> TaikoPP<'map> {
|
||||
|
||||
calculator.calculate()
|
||||
}
|
||||
|
||||
/// TODO: docs
|
||||
#[inline]
|
||||
pub fn try_from_osu(osu: OsuPP<'map>) -> Option<Self> {
|
||||
let OsuPP {
|
||||
map_or_attrs,
|
||||
mods,
|
||||
acc,
|
||||
combo,
|
||||
n300,
|
||||
n100,
|
||||
n50: _,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority,
|
||||
} = osu;
|
||||
|
||||
let MapOrElse::Map(map) = map_or_attrs else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let map = map.into_inner().convert_mode(GameMode::Taiko);
|
||||
|
||||
Some(Self {
|
||||
is_convert: matches!(map, Cow::Owned(_)),
|
||||
map_or_attrs: MapOrElse::Map(map),
|
||||
mods,
|
||||
combo,
|
||||
acc,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority,
|
||||
n300,
|
||||
n100,
|
||||
n_misses,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct TaikoPpInner {
|
||||
@@ -434,43 +477,6 @@ impl TaikoPpInner {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'map> From<OsuPP<'map>> for TaikoPP<'map> {
|
||||
#[inline]
|
||||
fn from(osu: OsuPP<'map>) -> Self {
|
||||
let OsuPP {
|
||||
map,
|
||||
attributes: _,
|
||||
mods,
|
||||
acc,
|
||||
combo,
|
||||
n300,
|
||||
n100,
|
||||
n50: _,
|
||||
n_misses,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority,
|
||||
} = osu;
|
||||
|
||||
let map = map.convert_mode(GameMode::Taiko);
|
||||
|
||||
Self {
|
||||
is_convert: matches!(map, Cow::Owned(_)),
|
||||
map,
|
||||
attributes: None,
|
||||
mods,
|
||||
combo,
|
||||
acc,
|
||||
passed_objects,
|
||||
clock_rate,
|
||||
hitresult_priority,
|
||||
n300,
|
||||
n100,
|
||||
n_misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accuracy(n300: usize, n100: usize, n_misses: usize) -> f64 {
|
||||
if n300 + n100 + n_misses == 0 {
|
||||
return 0.0;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
use crate::Beatmap;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum MapOrElse<M, E> {
|
||||
Map(M),
|
||||
Else(E),
|
||||
}
|
||||
|
||||
impl<M, E> MapOrElse<M, E>
|
||||
where
|
||||
M: AsRef<Beatmap>,
|
||||
{
|
||||
// /// Take out the `Else` value of `self` and return it.
|
||||
// ///
|
||||
// /// If `self` is of variant `Else`, `E::default()` will be left in place.
|
||||
// pub(crate) fn take(&mut self) -> MapOrElse<MapRef<'_>, E>
|
||||
// where
|
||||
// E: Default,
|
||||
// {
|
||||
// match self {
|
||||
// Self::Map(ref map) => MapOrElse::from(map.deref()),
|
||||
// Self::Else(ref mut other) => MapOrElse::Else(mem::take(other)),
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Return a mutable reference to `Else`.
|
||||
///
|
||||
/// If `self` is of variant `Map`, store `other` in `self`, and return a
|
||||
/// mutable reference to it.
|
||||
pub(crate) fn else_or_insert(&mut self, other: E) -> &mut E {
|
||||
match self {
|
||||
MapOrElse::Map(_) => {
|
||||
*self = Self::Else(other);
|
||||
|
||||
let Self::Else(ref mut other) = self else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
other
|
||||
}
|
||||
MapOrElse::Else(ref mut other) => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'map, E> From<&'map Beatmap> for MapOrElse<MapRef<'map>, E> {
|
||||
fn from(map: &'map Beatmap) -> Self {
|
||||
Self::Map(MapRef(map))
|
||||
}
|
||||
}
|
||||
|
||||
/// References don't implement [`Deref`] so we implement a wrapper type.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct MapRef<'map>(&'map Beatmap);
|
||||
|
||||
impl<'map> MapRef<'map> {
|
||||
pub(crate) fn into_inner(self) -> &'map Beatmap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Beatmap> for MapRef<'_> {
|
||||
fn as_ref(&self) -> &Beatmap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -2,9 +2,14 @@ mod byte_hasher;
|
||||
mod compact_vec;
|
||||
mod float_ext;
|
||||
mod limited_queue;
|
||||
mod map_or_else;
|
||||
mod tandem_sort;
|
||||
|
||||
pub(crate) use self::{
|
||||
byte_hasher::ByteHasher, compact_vec::CompactVec, float_ext::FloatExt,
|
||||
limited_queue::LimitedQueue, tandem_sort::TandemSorter,
|
||||
byte_hasher::ByteHasher,
|
||||
compact_vec::CompactVec,
|
||||
float_ext::FloatExt,
|
||||
limited_queue::LimitedQueue,
|
||||
map_or_else::{MapOrElse, MapRef},
|
||||
tandem_sort::TandemSorter,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user