fix: adjust hitresult gen for mania on lazer
This commit is contained in:
@@ -274,8 +274,6 @@ impl Difficulty {
|
|||||||
/// score.
|
/// score.
|
||||||
///
|
///
|
||||||
/// Defaults to `true`.
|
/// Defaults to `true`.
|
||||||
///
|
|
||||||
/// Only relevant for osu!standard performance calculation.
|
|
||||||
pub const fn lazer(mut self, lazer: bool) -> Self {
|
pub const fn lazer(mut self, lazer: bool) -> Self {
|
||||||
self.lazer = Some(lazer);
|
self.lazer = Some(lazer);
|
||||||
|
|
||||||
|
|||||||
@@ -307,12 +307,12 @@ impl<'map> Performance<'map> {
|
|||||||
/// This affects internal accuracy calculation because lazer considers
|
/// This affects internal accuracy calculation because lazer considers
|
||||||
/// slider heads for accuracy whereas stable does not.
|
/// slider heads for accuracy whereas stable does not.
|
||||||
///
|
///
|
||||||
/// Only relevant for osu!standard.
|
/// Only relevant for osu!standard and osu!mania.
|
||||||
pub fn lazer(self, lazer: bool) -> Self {
|
pub fn lazer(self, lazer: bool) -> Self {
|
||||||
if let Self::Osu(osu) = self {
|
match self {
|
||||||
Self::Osu(osu.lazer(lazer))
|
Self::Osu(o) => Self::Osu(o.lazer(lazer)),
|
||||||
} else {
|
Self::Taiko(_) | Self::Catch(_) => self,
|
||||||
self
|
Self::Mania(m) => Self::Mania(m.lazer(lazer)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ pub struct ManiaDifficultyAttributes {
|
|||||||
pub hit_window: f64,
|
pub hit_window: f64,
|
||||||
/// The amount of hitobjects in the map.
|
/// The amount of hitobjects in the map.
|
||||||
pub n_objects: u32,
|
pub n_objects: u32,
|
||||||
|
/// The amount of hold notes in the map.
|
||||||
|
pub n_hold_notes: u32,
|
||||||
/// The maximum achievable combo.
|
/// The maximum achievable combo.
|
||||||
pub max_combo: u32,
|
pub max_combo: u32,
|
||||||
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
|
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
|
||||||
|
|||||||
@@ -54,7 +54,13 @@ pub struct ManiaGradualDifficulty {
|
|||||||
strain: Strain,
|
strain: Strain,
|
||||||
diff_objects: Box<[ManiaDifficultyObject]>,
|
diff_objects: Box<[ManiaDifficultyObject]>,
|
||||||
hit_window: f64,
|
hit_window: f64,
|
||||||
|
note_state: NoteState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct NoteState {
|
||||||
curr_combo: u32,
|
curr_combo: u32,
|
||||||
|
n_hold_notes: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManiaGradualDifficulty {
|
impl ManiaGradualDifficulty {
|
||||||
@@ -80,7 +86,7 @@ impl ManiaGradualDifficulty {
|
|||||||
|
|
||||||
let strain = Strain::new(total_columns as usize);
|
let strain = Strain::new(total_columns as usize);
|
||||||
|
|
||||||
let mut curr_combo = 0;
|
let mut note_state = NoteState::default();
|
||||||
|
|
||||||
let objects_is_circle: Box<[_]> = converted
|
let objects_is_circle: Box<[_]> = converted
|
||||||
.hit_objects
|
.hit_objects
|
||||||
@@ -95,7 +101,7 @@ impl ManiaGradualDifficulty {
|
|||||||
objects_is_circle[0],
|
objects_is_circle[0],
|
||||||
hit_object.start_time,
|
hit_object.start_time,
|
||||||
hit_object.end_time,
|
hit_object.end_time,
|
||||||
&mut curr_combo,
|
&mut note_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +113,7 @@ impl ManiaGradualDifficulty {
|
|||||||
strain,
|
strain,
|
||||||
diff_objects,
|
diff_objects,
|
||||||
hit_window,
|
hit_window,
|
||||||
curr_combo,
|
note_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,7 +134,7 @@ impl Iterator for ManiaGradualDifficulty {
|
|||||||
increment_combo(
|
increment_combo(
|
||||||
is_circle,
|
is_circle,
|
||||||
curr,
|
curr,
|
||||||
&mut self.curr_combo,
|
&mut self.note_state,
|
||||||
self.difficulty.get_clock_rate(),
|
self.difficulty.get_clock_rate(),
|
||||||
);
|
);
|
||||||
} else if self.objects_is_circle.is_empty() {
|
} else if self.objects_is_circle.is_empty() {
|
||||||
@@ -140,8 +146,9 @@ impl Iterator for ManiaGradualDifficulty {
|
|||||||
Some(ManiaDifficultyAttributes {
|
Some(ManiaDifficultyAttributes {
|
||||||
stars: self.strain.as_difficulty_value() * DIFFICULTY_MULTIPLIER,
|
stars: self.strain.as_difficulty_value() * DIFFICULTY_MULTIPLIER,
|
||||||
hit_window: self.hit_window,
|
hit_window: self.hit_window,
|
||||||
max_combo: self.curr_combo,
|
max_combo: self.note_state.curr_combo,
|
||||||
n_objects: self.idx as u32,
|
n_objects: self.idx as u32,
|
||||||
|
n_hold_notes: self.note_state.n_hold_notes,
|
||||||
is_convert: self.is_convert,
|
is_convert: self.is_convert,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -171,7 +178,7 @@ impl Iterator for ManiaGradualDifficulty {
|
|||||||
let clock_rate = self.difficulty.get_clock_rate();
|
let clock_rate = self.difficulty.get_clock_rate();
|
||||||
|
|
||||||
for (curr, is_circle) in skip_iter.take(take) {
|
for (curr, is_circle) in skip_iter.take(take) {
|
||||||
increment_combo(*is_circle, curr, &mut self.curr_combo, clock_rate);
|
increment_combo(*is_circle, curr, &mut self.note_state, clock_rate);
|
||||||
strain.process(curr);
|
strain.process(curr);
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
}
|
}
|
||||||
@@ -189,22 +196,23 @@ impl ExactSizeIterator for ManiaGradualDifficulty {
|
|||||||
fn increment_combo(
|
fn increment_combo(
|
||||||
is_circle: bool,
|
is_circle: bool,
|
||||||
diff_obj: &ManiaDifficultyObject,
|
diff_obj: &ManiaDifficultyObject,
|
||||||
curr_combo: &mut u32,
|
state: &mut NoteState,
|
||||||
clock_rate: f64,
|
clock_rate: f64,
|
||||||
) {
|
) {
|
||||||
increment_combo_raw(
|
increment_combo_raw(
|
||||||
is_circle,
|
is_circle,
|
||||||
diff_obj.start_time * clock_rate,
|
diff_obj.start_time * clock_rate,
|
||||||
diff_obj.end_time * clock_rate,
|
diff_obj.end_time * clock_rate,
|
||||||
curr_combo,
|
state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_combo_raw(is_circle: bool, start_time: f64, end_time: f64, curr_combo: &mut u32) {
|
fn increment_combo_raw(is_circle: bool, start_time: f64, end_time: f64, state: &mut NoteState) {
|
||||||
if is_circle {
|
if is_circle {
|
||||||
*curr_combo += 1;
|
state.curr_combo += 1;
|
||||||
} else {
|
} else {
|
||||||
*curr_combo += 1 + ((end_time - start_time) / 100.0) as u32;
|
state.curr_combo += 1 + ((end_time - start_time) / 100.0) as u32;
|
||||||
|
state.n_hold_notes += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ pub fn difficulty(
|
|||||||
hit_window,
|
hit_window,
|
||||||
max_combo: values.max_combo,
|
max_combo: values.max_combo,
|
||||||
n_objects,
|
n_objects,
|
||||||
|
n_hold_notes: values.n_hold_notes,
|
||||||
is_convert: converted.is_convert,
|
is_convert: converted.is_convert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +43,7 @@ pub fn difficulty(
|
|||||||
pub struct DifficultyValues {
|
pub struct DifficultyValues {
|
||||||
pub strain: Strain,
|
pub strain: Strain,
|
||||||
pub max_combo: u32,
|
pub max_combo: u32,
|
||||||
|
pub n_hold_notes: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DifficultyValues {
|
impl DifficultyValues {
|
||||||
@@ -71,7 +73,8 @@ impl DifficultyValues {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
strain,
|
strain,
|
||||||
max_combo: params.into_max_combo(),
|
max_combo: params.max_combo(),
|
||||||
|
n_hold_notes: params.n_hold_notes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -47,6 +47,7 @@ impl ManiaObject {
|
|||||||
let duration = (slider.span_count() as f64) * dist / velocity;
|
let duration = (slider.span_count() as f64) * dist / velocity;
|
||||||
|
|
||||||
params.max_combo += (duration / 100.0) as u32;
|
params.max_combo += (duration / 100.0) as u32;
|
||||||
|
params.n_hold_notes += 1;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
start_time: h.start_time,
|
start_time: h.start_time,
|
||||||
@@ -57,6 +58,7 @@ impl ManiaObject {
|
|||||||
HitObjectKind::Spinner(Spinner { duration })
|
HitObjectKind::Spinner(Spinner { duration })
|
||||||
| HitObjectKind::Hold(HoldNote { duration }) => {
|
| HitObjectKind::Hold(HoldNote { duration }) => {
|
||||||
params.max_combo += (duration / 100.0) as u32;
|
params.max_combo += (duration / 100.0) as u32;
|
||||||
|
params.n_hold_notes += 1;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
start_time: h.start_time,
|
start_time: h.start_time,
|
||||||
@@ -77,6 +79,7 @@ impl ManiaObject {
|
|||||||
pub struct ObjectParams<'a> {
|
pub struct ObjectParams<'a> {
|
||||||
map: &'a Beatmap,
|
map: &'a Beatmap,
|
||||||
max_combo: u32,
|
max_combo: u32,
|
||||||
|
n_hold_notes: u32,
|
||||||
curve_bufs: CurveBuffers,
|
curve_bufs: CurveBuffers,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,11 +88,16 @@ impl<'a> ObjectParams<'a> {
|
|||||||
Self {
|
Self {
|
||||||
map,
|
map,
|
||||||
max_combo: 0,
|
max_combo: 0,
|
||||||
|
n_hold_notes: 0,
|
||||||
curve_bufs: CurveBuffers::default(),
|
curve_bufs: CurveBuffers::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_max_combo(self) -> u32 {
|
pub fn max_combo(&self) -> u32 {
|
||||||
self.max_combo
|
self.max_combo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn n_hold_notes(&self) -> u32 {
|
||||||
|
self.n_hold_notes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,13 @@ mod tests {
|
|||||||
for i in 1.. {
|
for i in 1.. {
|
||||||
state.misses += 1;
|
state.misses += 1;
|
||||||
|
|
||||||
|
// Hold notes award two hitresults in lazer
|
||||||
|
if let Some(h) = converted.hit_objects.get(i - 1) {
|
||||||
|
if !h.is_circle() {
|
||||||
|
state.n320 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let Some(next_gradual) = gradual.next(state.clone()) else {
|
let Some(next_gradual) = gradual.next(state.clone()) else {
|
||||||
assert_eq!(i, hit_objects_len + 1);
|
assert_eq!(i, hit_objects_len + 1);
|
||||||
assert!(gradual_2nd.last(state.clone()).is_some() || hit_objects_len % 2 == 0);
|
assert!(gradual_2nd.last(state.clone()).is_some() || hit_objects_len % 2 == 0);
|
||||||
|
|||||||
@@ -169,6 +169,19 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the calculated attributes belong to an osu!lazer or osu!stable
|
||||||
|
/// score.
|
||||||
|
///
|
||||||
|
/// Defaults to `true`.
|
||||||
|
///
|
||||||
|
/// This affects internal hitresult generation because lazer gives two
|
||||||
|
/// hitresults per hold note whereas stable only gives one.
|
||||||
|
pub fn lazer(mut self, lazer: bool) -> Self {
|
||||||
|
self.difficulty = self.difficulty.lazer(lazer);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Specify the amount of 320s of a play.
|
/// Specify the amount of 320s of a play.
|
||||||
pub const fn n320(mut self, n320: u32) -> Self {
|
pub const fn n320(mut self, n320: u32) -> Self {
|
||||||
self.n320 = Some(n320);
|
self.n320 = Some(n320);
|
||||||
@@ -245,11 +258,16 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
MapOrAttrs::Attrs(ref attrs) => attrs,
|
MapOrAttrs::Attrs(ref attrs) => attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
let n_objects = cmp::min(self.difficulty.get_passed_objects() as u32, attrs.n_objects);
|
let mut n_objects = cmp::min(self.difficulty.get_passed_objects() as u32, attrs.n_objects);
|
||||||
|
|
||||||
let priority = self.hitresult_priority;
|
let priority = self.hitresult_priority;
|
||||||
|
|
||||||
let misses = self.misses.map_or(0, |n| cmp::min(n, n_objects));
|
let misses = self.misses.map_or(0, |n| cmp::min(n, n_objects));
|
||||||
|
|
||||||
|
if self.difficulty.get_lazer() {
|
||||||
|
n_objects += attrs.n_hold_notes;
|
||||||
|
}
|
||||||
|
|
||||||
let n_remaining = n_objects - misses;
|
let n_remaining = n_objects - misses;
|
||||||
|
|
||||||
let mut n320 = self.n320.map_or(0, |n| cmp::min(n, n_remaining));
|
let mut n320 = self.n320.map_or(0, |n| cmp::min(n, n_remaining));
|
||||||
@@ -950,6 +968,7 @@ mod tests {
|
|||||||
static ATTRS: OnceLock<ManiaDifficultyAttributes> = OnceLock::new();
|
static ATTRS: OnceLock<ManiaDifficultyAttributes> = OnceLock::new();
|
||||||
|
|
||||||
const N_OBJECTS: u32 = 594;
|
const N_OBJECTS: u32 = 594;
|
||||||
|
const N_HOLD_NOTES: u32 = 121;
|
||||||
|
|
||||||
fn beatmap() -> Beatmap {
|
fn beatmap() -> Beatmap {
|
||||||
Beatmap::from_path("./resources/1638954.osu").unwrap()
|
Beatmap::from_path("./resources/1638954.osu").unwrap()
|
||||||
@@ -962,6 +981,14 @@ mod tests {
|
|||||||
let attrs = Difficulty::new().with_mode().calculate(&converted);
|
let attrs = Difficulty::new().with_mode().calculate(&converted);
|
||||||
|
|
||||||
assert_eq!(N_OBJECTS, converted.hit_objects.len() as u32);
|
assert_eq!(N_OBJECTS, converted.hit_objects.len() as u32);
|
||||||
|
assert_eq!(
|
||||||
|
N_HOLD_NOTES,
|
||||||
|
converted
|
||||||
|
.hit_objects
|
||||||
|
.iter()
|
||||||
|
.filter(|h| !h.is_circle())
|
||||||
|
.count() as u32
|
||||||
|
);
|
||||||
|
|
||||||
attrs
|
attrs
|
||||||
})
|
})
|
||||||
@@ -975,6 +1002,7 @@ mod tests {
|
|||||||
/// that it doesn't run unreasonably long.
|
/// that it doesn't run unreasonably long.
|
||||||
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
|
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
|
||||||
fn brute_force_best(
|
fn brute_force_best(
|
||||||
|
lazer: bool,
|
||||||
acc: f64,
|
acc: f64,
|
||||||
n320: Option<u32>,
|
n320: Option<u32>,
|
||||||
n300: Option<u32>,
|
n300: Option<u32>,
|
||||||
@@ -994,7 +1022,11 @@ mod tests {
|
|||||||
let mut best_dist = f64::INFINITY;
|
let mut best_dist = f64::INFINITY;
|
||||||
let mut best_custom_acc = 0.0;
|
let mut best_custom_acc = 0.0;
|
||||||
|
|
||||||
let n_remaining = N_OBJECTS - misses;
|
let mut n_remaining = N_OBJECTS - misses;
|
||||||
|
|
||||||
|
if lazer {
|
||||||
|
n_remaining += N_HOLD_NOTES;
|
||||||
|
}
|
||||||
|
|
||||||
let multiple_given = (usize::from(n320.is_some())
|
let multiple_given = (usize::from(n320.is_some())
|
||||||
+ usize::from(n300.is_some())
|
+ usize::from(n300.is_some())
|
||||||
@@ -1003,17 +1035,23 @@ mod tests {
|
|||||||
+ usize::from(n50.is_some()))
|
+ usize::from(n50.is_some()))
|
||||||
> 1;
|
> 1;
|
||||||
|
|
||||||
let max_left = N_OBJECTS
|
let mut n_objects = N_OBJECTS;
|
||||||
|
|
||||||
|
if lazer {
|
||||||
|
n_objects += N_HOLD_NOTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_left = n_objects
|
||||||
.saturating_sub(n200.unwrap_or(0) + n100.unwrap_or(0) + n50.unwrap_or(0) + misses);
|
.saturating_sub(n200.unwrap_or(0) + n100.unwrap_or(0) + n50.unwrap_or(0) + misses);
|
||||||
|
|
||||||
let min_n3x0 = cmp::min(
|
let min_n3x0 = cmp::min(
|
||||||
max_left,
|
max_left,
|
||||||
(acc * f64::from(3 * N_OBJECTS) - f64::from(2 * n_remaining)).floor() as u32,
|
(acc * f64::from(3 * n_objects) - f64::from(2 * n_remaining)).floor() as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let max_n3x0 = cmp::min(
|
let max_n3x0 = cmp::min(
|
||||||
max_left,
|
max_left,
|
||||||
((acc * f64::from(6 * N_OBJECTS) - f64::from(n_remaining)) / 5.0).ceil() as u32,
|
((acc * f64::from(6 * n_objects) - f64::from(n_remaining)) / 5.0).ceil() as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (min_n3x0, max_n3x0) = match (n320, n300) {
|
let (min_n3x0, max_n3x0) = match (n320, n300) {
|
||||||
@@ -1086,9 +1124,9 @@ mod tests {
|
|||||||
let curr_dist = (acc - curr_acc).abs();
|
let curr_dist = (acc - curr_acc).abs();
|
||||||
|
|
||||||
let curr_custom_acc =
|
let curr_custom_acc =
|
||||||
custom_accuracy(new320, new300, new200, new100, new50, N_OBJECTS);
|
custom_accuracy(new320, new300, new200, new100, new50, n_objects);
|
||||||
|
|
||||||
match curr_dist.partial_cmp(&best_dist).expect("non-NaN") {
|
match curr_dist.total_cmp(&best_dist) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
best_dist = curr_dist;
|
best_dist = curr_dist;
|
||||||
best_custom_acc = curr_custom_acc;
|
best_custom_acc = curr_custom_acc;
|
||||||
@@ -1194,13 +1232,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mania_hitresults(
|
fn mania_hitresults(
|
||||||
acc in 0.0..=1.0,
|
lazer in prop::bool::ANY,
|
||||||
n320 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + 10),
|
acc in 0.0_f64..=1.0,
|
||||||
n300 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + 10),
|
n320 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + N_HOLD_NOTES + 10),
|
||||||
n200 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + 10),
|
n300 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + N_HOLD_NOTES + 10),
|
||||||
n100 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + 10),
|
n200 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + N_HOLD_NOTES + 10),
|
||||||
n50 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + 10),
|
n100 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + N_HOLD_NOTES + 10),
|
||||||
n_misses in prop::option::weighted(0.15, 0_u32..=N_OBJECTS + 10),
|
n50 in prop::option::weighted(0.10, 0_u32..=N_OBJECTS + N_HOLD_NOTES + 10),
|
||||||
|
n_misses in prop::option::weighted(0.15, 0_u32..=N_OBJECTS + N_HOLD_NOTES + 10),
|
||||||
best_case in prop::bool::ANY,
|
best_case in prop::bool::ANY,
|
||||||
) {
|
) {
|
||||||
let priority = if best_case {
|
let priority = if best_case {
|
||||||
@@ -1211,6 +1250,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut state = ManiaPerformance::from(attrs())
|
let mut state = ManiaPerformance::from(attrs())
|
||||||
.accuracy(acc * 100.0)
|
.accuracy(acc * 100.0)
|
||||||
|
.lazer(lazer)
|
||||||
.hitresult_priority(priority);
|
.hitresult_priority(priority);
|
||||||
|
|
||||||
if let Some(n320) = n320 {
|
if let Some(n320) = n320 {
|
||||||
@@ -1242,6 +1282,7 @@ mod tests {
|
|||||||
assert_eq!(first, state);
|
assert_eq!(first, state);
|
||||||
|
|
||||||
let expected = brute_force_best(
|
let expected = brute_force_best(
|
||||||
|
lazer,
|
||||||
acc,
|
acc,
|
||||||
n320,
|
n320,
|
||||||
n300,
|
n300,
|
||||||
@@ -1259,6 +1300,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hitresults_n320_misses_best() {
|
fn hitresults_n320_misses_best() {
|
||||||
let state = ManiaPerformance::from(attrs())
|
let state = ManiaPerformance::from(attrs())
|
||||||
|
.lazer(false)
|
||||||
.n320(500)
|
.n320(500)
|
||||||
.misses(2)
|
.misses(2)
|
||||||
.hitresult_priority(HitResultPriority::BestCase)
|
.hitresult_priority(HitResultPriority::BestCase)
|
||||||
@@ -1279,6 +1321,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hitresults_n100_n50_misses_worst() {
|
fn hitresults_n100_n50_misses_worst() {
|
||||||
let state = ManiaPerformance::from(attrs())
|
let state = ManiaPerformance::from(attrs())
|
||||||
|
.lazer(false)
|
||||||
.n100(200)
|
.n100(200)
|
||||||
.n50(50)
|
.n50(50)
|
||||||
.misses(2)
|
.misses(2)
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ macro_rules! test_cases {
|
|||||||
stars: $stars:literal,
|
stars: $stars:literal,
|
||||||
hit_window: $hit_window:literal,
|
hit_window: $hit_window:literal,
|
||||||
n_objects: $n_objects:literal,
|
n_objects: $n_objects:literal,
|
||||||
|
n_hold_notes: $n_hold_notes:literal,
|
||||||
max_combo: $max_combo:literal,
|
max_combo: $max_combo:literal,
|
||||||
is_convert: $is_convert:literal,
|
is_convert: $is_convert:literal,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -119,6 +120,7 @@ macro_rules! test_cases {
|
|||||||
stars: $stars,
|
stars: $stars,
|
||||||
hit_window: $hit_window,
|
hit_window: $hit_window,
|
||||||
n_objects: $n_objects,
|
n_objects: $n_objects,
|
||||||
|
n_hold_notes: $n_hold_notes,
|
||||||
max_combo: $max_combo,
|
max_combo: $max_combo,
|
||||||
is_convert: $is_convert,
|
is_convert: $is_convert,
|
||||||
}
|
}
|
||||||
@@ -531,6 +533,7 @@ fn basic_mania() {
|
|||||||
stars: 3.358304846842773,
|
stars: 3.358304846842773,
|
||||||
hit_window: 40.0,
|
hit_window: 40.0,
|
||||||
n_objects: 594,
|
n_objects: 594,
|
||||||
|
n_hold_notes: 121,
|
||||||
max_combo: 956,
|
max_combo: 956,
|
||||||
is_convert: false,
|
is_convert: false,
|
||||||
};
|
};
|
||||||
@@ -538,6 +541,7 @@ fn basic_mania() {
|
|||||||
stars: 4.6072892053157295,
|
stars: 4.6072892053157295,
|
||||||
hit_window: 40.0,
|
hit_window: 40.0,
|
||||||
n_objects: 594,
|
n_objects: 594,
|
||||||
|
n_hold_notes: 121,
|
||||||
max_combo: 956,
|
max_combo: 956,
|
||||||
is_convert: false,
|
is_convert: false,
|
||||||
};
|
};
|
||||||
@@ -553,6 +557,7 @@ fn convert_mania() {
|
|||||||
stars: 3.2033142085672255,
|
stars: 3.2033142085672255,
|
||||||
hit_window: 34.0,
|
hit_window: 34.0,
|
||||||
n_objects: 1046,
|
n_objects: 1046,
|
||||||
|
n_hold_notes: 293,
|
||||||
max_combo: 1381,
|
max_combo: 1381,
|
||||||
is_convert: true,
|
is_convert: true,
|
||||||
};
|
};
|
||||||
@@ -560,6 +565,7 @@ fn convert_mania() {
|
|||||||
stars: 4.2934063021960185,
|
stars: 4.2934063021960185,
|
||||||
hit_window: 34.0,
|
hit_window: 34.0,
|
||||||
n_objects: 1046,
|
n_objects: 1046,
|
||||||
|
n_hold_notes: 293,
|
||||||
max_combo: 1381,
|
max_combo: 1381,
|
||||||
is_convert: true,
|
is_convert: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -359,9 +359,9 @@ fn convert_catch() {
|
|||||||
fn basic_mania() {
|
fn basic_mania() {
|
||||||
test_cases! {
|
test_cases! {
|
||||||
Mania: MANIA {
|
Mania: MANIA {
|
||||||
NM => { pp: 108.08430593303873, pp_difficulty: 108.08430593303873 };
|
NM => { pp: 108.92297471705167, pp_difficulty: 108.92297471705167 };
|
||||||
EZ => { pp: 54.04215296651937, pp_difficulty: 108.08430593303873 };
|
EZ => { pp: 54.46148735852584, pp_difficulty: 108.92297471705167 };
|
||||||
DT => { pp: 222.79838979800365, pp_difficulty: 222.79838979800365 };
|
DT => { pp: 224.52717042937203, pp_difficulty: 224.52717042937203 };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -370,9 +370,9 @@ fn basic_mania() {
|
|||||||
fn convert_mania() {
|
fn convert_mania() {
|
||||||
test_cases! {
|
test_cases! {
|
||||||
Mania: OSU {
|
Mania: OSU {
|
||||||
NM => { pp: 99.73849552661329, pp_difficulty: 99.73849552661329 };
|
NM => { pp: 101.39189449271568, pp_difficulty: 101.39189449271568 };
|
||||||
EZ => { pp: 49.869247763306646, pp_difficulty: 99.73849552661329 };
|
EZ => { pp: 50.69594724635784, pp_difficulty: 101.39189449271568 };
|
||||||
DT => { pp: 195.23247718805612, pp_difficulty: 195.23247718805612 };
|
DT => { pp: 198.46891237015896, pp_difficulty: 198.46891237015896 };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user