feat: add methods ModePP::generate_state
This commit is contained in:
+3
-3
@@ -193,7 +193,7 @@ impl<'map> CatchPP<'map> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the hitresults that will be used for performance calculation.
|
||||
/// 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,
|
||||
@@ -737,7 +737,7 @@ mod test {
|
||||
state = state.misses(n_misses);
|
||||
}
|
||||
|
||||
let hitresults = state.generate_hitresults();
|
||||
let state = state.generate_state();
|
||||
|
||||
let expected = brute_force_best(
|
||||
acc,
|
||||
@@ -748,7 +748,7 @@ mod test {
|
||||
n_misses.unwrap_or(0),
|
||||
);
|
||||
|
||||
assert_eq!(hitresults, expected);
|
||||
assert_eq!(state, expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,66 @@ impl Iterator for OwnedGradualDifficulty {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsuScoreState> for ScoreState {
|
||||
#[inline]
|
||||
fn from(state: OsuScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: state.max_combo,
|
||||
n_geki: 0,
|
||||
n_katu: 0,
|
||||
n300: state.n300,
|
||||
n100: state.n100,
|
||||
n50: state.n50,
|
||||
n_misses: state.n_misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TaikoScoreState> for ScoreState {
|
||||
#[inline]
|
||||
fn from(state: TaikoScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: state.max_combo,
|
||||
n_geki: 0,
|
||||
n_katu: 0,
|
||||
n300: state.n300,
|
||||
n100: state.n100,
|
||||
n50: 0,
|
||||
n_misses: state.n_misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CatchScoreState> for ScoreState {
|
||||
#[inline]
|
||||
fn from(state: CatchScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: state.max_combo,
|
||||
n_geki: 0,
|
||||
n_katu: state.n_tiny_droplet_misses,
|
||||
n300: state.n_fruits,
|
||||
n100: state.n_droplets,
|
||||
n50: state.n_tiny_droplets,
|
||||
n_misses: state.n_misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ManiaScoreState> for ScoreState {
|
||||
#[inline]
|
||||
fn from(state: ManiaScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: 0,
|
||||
n_geki: state.n320,
|
||||
n_katu: state.n200,
|
||||
n300: state.n300,
|
||||
n100: state.n100,
|
||||
n50: state.n50,
|
||||
n_misses: state.n_misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradually calculate the performance attributes on maps of any mode.
|
||||
///
|
||||
/// After each hit object you can call [`next`](`GradualPerformance::next`)
|
||||
|
||||
+39
-32
@@ -226,34 +226,8 @@ impl<'map> ManiaPP<'map> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Calculate all performance related values, including pp and stars.
|
||||
pub fn calculate(self) -> ManiaPerformanceAttributes {
|
||||
let attrs = self.attributes.unwrap_or_else(|| {
|
||||
let mut calculator = ManiaStars::new(self.map.as_ref())
|
||||
.mods(self.mods)
|
||||
.is_convert(matches!(self.map, Cow::Owned(_)));
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
}
|
||||
|
||||
if let Some(clock_rate) = self.clock_rate {
|
||||
calculator = calculator.clock_rate(clock_rate);
|
||||
}
|
||||
|
||||
calculator.calculate()
|
||||
});
|
||||
|
||||
let inner = ManiaPpInner {
|
||||
attrs,
|
||||
mods: self.mods,
|
||||
state: self.generate_hitresults(),
|
||||
};
|
||||
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_hitresults(&self) -> ManiaScoreState {
|
||||
/// 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());
|
||||
let priority = self.hitresult_priority.unwrap_or_default();
|
||||
|
||||
@@ -732,6 +706,39 @@ impl<'map> ManiaPP<'map> {
|
||||
n_misses,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate all performance related values, including pp and stars.
|
||||
pub fn calculate(self) -> ManiaPerformanceAttributes {
|
||||
let state = self.generate_state();
|
||||
|
||||
let attrs = self
|
||||
.attributes
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
|
||||
let inner = ManiaPpInner {
|
||||
mods: self.mods,
|
||||
attrs,
|
||||
state,
|
||||
};
|
||||
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> ManiaDifficultyAttributes {
|
||||
let mut calculator = ManiaStars::new(self.map.as_ref())
|
||||
.mods(self.mods)
|
||||
.is_convert(self.is_convert);
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
}
|
||||
|
||||
if let Some(clock_rate) = self.clock_rate {
|
||||
calculator = calculator.clock_rate(clock_rate);
|
||||
}
|
||||
|
||||
calculator.calculate()
|
||||
}
|
||||
}
|
||||
|
||||
struct ManiaPpInner {
|
||||
@@ -1203,7 +1210,7 @@ mod tests {
|
||||
state = state.n_misses(n_misses);
|
||||
}
|
||||
|
||||
let hitresults = state.generate_hitresults();
|
||||
let state = state.generate_state();
|
||||
|
||||
let expected = brute_force_best(
|
||||
acc,
|
||||
@@ -1216,7 +1223,7 @@ mod tests {
|
||||
best_case,
|
||||
);
|
||||
|
||||
assert_eq!(hitresults, expected);
|
||||
assert_eq!(state, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1229,7 +1236,7 @@ mod tests {
|
||||
.n320(500)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::BestCase)
|
||||
.generate_hitresults();
|
||||
.generate_state();
|
||||
|
||||
let expected = ManiaScoreState {
|
||||
n320: 500,
|
||||
@@ -1254,7 +1261,7 @@ mod tests {
|
||||
.n50(50)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::WorstCase)
|
||||
.generate_hitresults();
|
||||
.generate_state();
|
||||
|
||||
let expected = ManiaScoreState {
|
||||
n320: 0,
|
||||
|
||||
+32
-24
@@ -208,7 +208,13 @@ impl<'map> OsuPP<'map> {
|
||||
self
|
||||
}
|
||||
|
||||
fn generate_hitresults(&self, max_combo: usize) -> OsuScoreState {
|
||||
/// 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 n_objects = self.passed_objects.unwrap_or(self.map.hit_objects.len());
|
||||
let priority = self.hitresult_priority.unwrap_or_default();
|
||||
|
||||
@@ -378,21 +384,13 @@ impl<'map> OsuPP<'map> {
|
||||
|
||||
/// Calculate all performance related values, including pp and stars.
|
||||
pub fn calculate(mut self) -> OsuPerformanceAttributes {
|
||||
let attrs = self.attributes.take().unwrap_or_else(|| {
|
||||
let mut calculator = OsuStars::new(self.map).mods(self.mods);
|
||||
let state = self.generate_state();
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
}
|
||||
let attrs = self
|
||||
.attributes
|
||||
.take()
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
|
||||
if let Some(clock_rate) = self.clock_rate {
|
||||
calculator = calculator.clock_rate(clock_rate);
|
||||
}
|
||||
|
||||
calculator.calculate()
|
||||
});
|
||||
|
||||
let state = self.generate_hitresults(attrs.max_combo);
|
||||
let effective_miss_count = calculate_effective_misses(&attrs, &state);
|
||||
|
||||
let inner = OsuPpInner {
|
||||
@@ -405,6 +403,20 @@ impl<'map> OsuPP<'map> {
|
||||
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> OsuDifficultyAttributes {
|
||||
let mut calculator = OsuStars::new(self.map).mods(self.mods);
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
}
|
||||
|
||||
if let Some(clock_rate) = self.clock_rate {
|
||||
calculator = calculator.clock_rate(clock_rate);
|
||||
}
|
||||
|
||||
calculator.calculate()
|
||||
}
|
||||
}
|
||||
|
||||
struct OsuPpInner {
|
||||
@@ -953,7 +965,7 @@ mod test {
|
||||
state = state.n_misses(n_misses);
|
||||
}
|
||||
|
||||
let hitresults = state.generate_hitresults(max_combo);
|
||||
let state = state.generate_state();
|
||||
|
||||
let mut expected = brute_force_best(
|
||||
acc,
|
||||
@@ -965,14 +977,13 @@ mod test {
|
||||
);
|
||||
expected.max_combo = max_combo.saturating_sub(n_misses.unwrap_or(0));
|
||||
|
||||
assert_eq!(hitresults, expected);
|
||||
assert_eq!(state, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hitresults_n300_n100_n_misses_best() {
|
||||
let (map, attrs) = test_data();
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let state = OsuPP::new(map)
|
||||
.attributes(attrs)
|
||||
@@ -981,7 +992,7 @@ mod test {
|
||||
.n100(20)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::BestCase)
|
||||
.generate_hitresults(max_combo);
|
||||
.generate_state();
|
||||
|
||||
let expected = OsuScoreState {
|
||||
max_combo: 500,
|
||||
@@ -997,7 +1008,6 @@ mod test {
|
||||
#[test]
|
||||
fn hitresults_n300_n50_n_misses_best() {
|
||||
let (map, attrs) = test_data();
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let state = OsuPP::new(map)
|
||||
.attributes(attrs)
|
||||
@@ -1006,7 +1016,7 @@ mod test {
|
||||
.n50(10)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::BestCase)
|
||||
.generate_hitresults(max_combo);
|
||||
.generate_state();
|
||||
|
||||
let expected = OsuScoreState {
|
||||
max_combo: 500,
|
||||
@@ -1022,7 +1032,6 @@ mod test {
|
||||
#[test]
|
||||
fn hitresults_n50_n_misses_worst() {
|
||||
let (map, attrs) = test_data();
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let state = OsuPP::new(map)
|
||||
.attributes(attrs)
|
||||
@@ -1030,7 +1039,7 @@ mod test {
|
||||
.n50(10)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::WorstCase)
|
||||
.generate_hitresults(max_combo);
|
||||
.generate_state();
|
||||
|
||||
let expected = OsuScoreState {
|
||||
max_combo: 500,
|
||||
@@ -1046,7 +1055,6 @@ mod test {
|
||||
#[test]
|
||||
fn hitresults_n300_n100_n50_n_misses_worst() {
|
||||
let (map, attrs) = test_data();
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let state = OsuPP::new(map)
|
||||
.attributes(attrs)
|
||||
@@ -1056,7 +1064,7 @@ mod test {
|
||||
.n50(10)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::WorstCase)
|
||||
.generate_hitresults(max_combo);
|
||||
.generate_state();
|
||||
|
||||
let expected = OsuScoreState {
|
||||
max_combo: 500,
|
||||
|
||||
@@ -274,6 +274,17 @@ impl<'map> AnyPP<'map> {
|
||||
Self::Mania(m) => Self::Mania(m.n320(n_geki)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the [`ScoreState`] that will be used for performance calculation.
|
||||
#[inline]
|
||||
pub fn generate_state(&mut self) -> ScoreState {
|
||||
match self {
|
||||
Self::Osu(o) => o.generate_state().into(),
|
||||
Self::Taiko(t) => t.generate_state().into(),
|
||||
Self::Catch(f) => f.generate_state().into(),
|
||||
Self::Mania(m) => m.generate_state().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// While generating remaining hitresults, decide how they should be distributed.
|
||||
|
||||
+44
-33
@@ -197,34 +197,13 @@ impl<'map> TaikoPP<'map> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Calculate all performance related values, including pp and stars.
|
||||
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
|
||||
let attrs = self.attributes.take().unwrap_or_else(|| {
|
||||
let mut calculator = TaikoStars::new(self.map.as_ref())
|
||||
.mods(self.mods)
|
||||
.is_convert(self.is_convert);
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
}
|
||||
|
||||
if let Some(clock_rate) = self.clock_rate {
|
||||
calculator = calculator.clock_rate(clock_rate);
|
||||
}
|
||||
|
||||
calculator.calculate()
|
||||
});
|
||||
|
||||
let inner = TaikoPpInner {
|
||||
mods: self.mods,
|
||||
state: self.generate_hitresults(attrs.max_combo),
|
||||
attrs,
|
||||
/// 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,
|
||||
};
|
||||
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_hitresults(&self, max_combo: usize) -> TaikoScoreState {
|
||||
let total_result_count = if let Some(passed_objects) = self.passed_objects {
|
||||
max_combo.min(passed_objects)
|
||||
} else {
|
||||
@@ -302,6 +281,40 @@ impl<'map> TaikoPP<'map> {
|
||||
n_misses,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate all performance related values, including pp and stars.
|
||||
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
|
||||
let state = self.generate_state();
|
||||
|
||||
let attrs = self
|
||||
.attributes
|
||||
.take()
|
||||
.unwrap_or_else(|| self.generate_attributes());
|
||||
|
||||
let inner = TaikoPpInner {
|
||||
mods: self.mods,
|
||||
state,
|
||||
attrs,
|
||||
};
|
||||
|
||||
inner.calculate()
|
||||
}
|
||||
|
||||
fn generate_attributes(&self) -> TaikoDifficultyAttributes {
|
||||
let mut calculator = TaikoStars::new(self.map.as_ref())
|
||||
.mods(self.mods)
|
||||
.is_convert(self.is_convert);
|
||||
|
||||
if let Some(passed_objects) = self.passed_objects {
|
||||
calculator = calculator.passed_objects(passed_objects);
|
||||
}
|
||||
|
||||
if let Some(clock_rate) = self.clock_rate {
|
||||
calculator = calculator.clock_rate(clock_rate);
|
||||
}
|
||||
|
||||
calculator.calculate()
|
||||
}
|
||||
}
|
||||
|
||||
struct TaikoPpInner {
|
||||
@@ -609,7 +622,7 @@ mod test {
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#![proptest_config(ProptestConfig::with_cases(10_000))]
|
||||
#![proptest_config(ProptestConfig::with_cases(20_000))]
|
||||
#[test]
|
||||
fn taiko_hitresults(
|
||||
acc in 0.0..=1.0,
|
||||
@@ -643,7 +656,7 @@ mod test {
|
||||
state = state.n_misses(n_misses);
|
||||
}
|
||||
|
||||
let hitresults = state.generate_hitresults(MAX_COMBO);
|
||||
let state = state.generate_state();
|
||||
|
||||
let mut expected = brute_force_best(
|
||||
acc,
|
||||
@@ -654,14 +667,13 @@ mod test {
|
||||
);
|
||||
expected.max_combo = MAX_COMBO.saturating_sub(n_misses.unwrap_or(0));
|
||||
|
||||
assert_eq!(hitresults, expected);
|
||||
assert_eq!(state, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hitresults_n300_n_misses_best() {
|
||||
let (map, attrs) = test_data();
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let state = TaikoPP::new(map)
|
||||
.attributes(attrs)
|
||||
@@ -669,7 +681,7 @@ mod test {
|
||||
.n300(150)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::BestCase)
|
||||
.generate_hitresults(max_combo);
|
||||
.generate_state();
|
||||
|
||||
let expected = TaikoScoreState {
|
||||
max_combo: 100,
|
||||
@@ -684,14 +696,13 @@ mod test {
|
||||
#[test]
|
||||
fn hitresults_n_misses_best() {
|
||||
let (map, attrs) = test_data();
|
||||
let max_combo = attrs.max_combo();
|
||||
|
||||
let state = TaikoPP::new(map)
|
||||
.attributes(attrs)
|
||||
.combo(100)
|
||||
.n_misses(2)
|
||||
.hitresult_priority(HitResultPriority::BestCase)
|
||||
.generate_hitresults(max_combo);
|
||||
.generate_state();
|
||||
|
||||
let expected = TaikoScoreState {
|
||||
max_combo: 100,
|
||||
|
||||
Reference in New Issue
Block a user