updated osu tests & more bugfixing for osu

This commit is contained in:
MaxOhn
2021-07-24 18:18:18 +02:00
parent 9ffd6ad1b6
commit 61620d6b25
11 changed files with 186 additions and 270 deletions
+3 -4
View File
@@ -334,7 +334,7 @@ impl<'m> OsuPP<'m> {
} }
// AR bonus // AR bonus
let mut ar_factor = if attributes.ar > 10.33 { let ar_factor = if attributes.ar > 10.33 {
attributes.ar - 10.33 attributes.ar - 10.33
} else if attributes.ar < 8.0 { } else if attributes.ar < 8.0 {
0.025 * (8.0 - attributes.ar) 0.025 * (8.0 - attributes.ar)
@@ -343,8 +343,7 @@ impl<'m> OsuPP<'m> {
}; };
let ar_total_hits_factor = (1.0 + (-(0.007 * (total_hits - 400.0))).exp()).recip(); let ar_total_hits_factor = (1.0 + (-(0.007 * (total_hits - 400.0))).exp()).recip();
let ar_bonus = 1.0 + (0.03 + 0.37 * ar_total_hits_factor) * ar_factor;
ar_factor *= 1.0 + (0.03 + 0.37 * ar_total_hits_factor) * ar_factor;
// HD bonus // HD bonus
if self.mods.hd() { if self.mods.hd() {
@@ -360,7 +359,7 @@ impl<'m> OsuPP<'m> {
1.0 1.0
}; };
aim_value *= ar_factor.max(fl_bonus); aim_value *= ar_bonus.max(fl_bonus);
// Scale with accuracy // Scale with accuracy
aim_value *= 0.5 + self.acc.unwrap() / 2.0; aim_value *= 0.5 + self.acc.unwrap() / 2.0;
@@ -18,10 +18,9 @@ impl<'h> DifficultyObject<'h> {
prev: &OsuObject, prev: &OsuObject,
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time) prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
prev_prev: Option<OsuObject>, prev_prev: Option<OsuObject>,
clock_rate: f32,
scaling_factor: f32, scaling_factor: f32,
) -> Self { ) -> Self {
let delta = (base.time - prev.time) / clock_rate; let delta = base.time - prev.time;
let strain_time = delta.max(50.0); let strain_time = delta.max(50.0);
let pos = base.pos; // stacked position let pos = base.pos; // stacked position
+47 -82
View File
@@ -62,8 +62,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
} }
let time_preempt = difficulty_range_ar(raw_ar); let time_preempt = difficulty_range_ar(raw_ar);
let section_len = SECTION_LEN * map_attributes.clock_rate;
let scale = (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0; let scale = (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
let radius = OBJECT_RADIUS * scale; let radius = OBJECT_RADIUS * scale;
let mut scaling_factor = NORMALIZED_RADIUS / radius; let mut scaling_factor = NORMALIZED_RADIUS / radius;
@@ -76,23 +74,21 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
let mut slider_state = SliderState::new(map); let mut slider_state = SliderState::new(map);
let mut ticks_buf = Vec::new(); let mut ticks_buf = Vec::new();
let mut hit_objects: Vec<_> = map let hit_objects_iter = map.hit_objects.iter().take(take).filter_map(|h| {
.hit_objects OsuObject::new(
.iter() h,
.take(take) map,
.filter_map(|h| { radius,
OsuObject::new( scaling_factor,
h, hr,
map, &mut ticks_buf,
radius, &mut diff_attributes,
scaling_factor, &mut slider_state,
hr, )
&mut ticks_buf, });
&mut diff_attributes,
&mut slider_state, let mut hit_objects = Vec::with_capacity(take);
) hit_objects.extend(hit_objects_iter);
})
.collect();
let stack_threshold = time_preempt * map.stack_leniency; let stack_threshold = time_preempt * map.stack_leniency;
@@ -107,6 +103,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
let mut hit_objects = hit_objects.into_iter().map(|mut h| { let mut hit_objects = hit_objects.into_iter().map(|mut h| {
let stack_offset = h.stack_height * scale_factor; let stack_offset = h.stack_height * scale_factor;
h.time /= map_attributes.clock_rate;
h.pos += Pos2 { h.pos += Pos2 {
x: stack_offset, x: stack_offset,
y: stack_offset, y: stack_offset,
@@ -118,27 +115,19 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
let mut prev_prev = None; let mut prev_prev = None;
let mut prev = hit_objects.next().unwrap(); let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None; let mut prev_vals = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching // Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap(); let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -150,14 +139,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
// Handle all other objects // Handle all other objects
for curr in hit_objects { for curr in hit_objects {
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
aim.save_current_peak(); aim.save_current_peak();
@@ -165,7 +147,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
speed.save_current_peak(); speed.save_current_peak();
speed.start_new_section_from(current_section_end); speed.start_new_section_from(current_section_end);
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -222,8 +204,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
} }
let time_preempt = difficulty_range_ar(raw_ar); let time_preempt = difficulty_range_ar(raw_ar);
let section_len = SECTION_LEN * map_attributes.clock_rate;
let scale = (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0; let scale = (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
let radius = OBJECT_RADIUS * scale; let radius = OBJECT_RADIUS * scale;
let mut scaling_factor = NORMALIZED_RADIUS / radius; let mut scaling_factor = NORMALIZED_RADIUS / radius;
@@ -236,22 +216,21 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
let mut slider_state = SliderState::new(map); let mut slider_state = SliderState::new(map);
let mut ticks_buf = Vec::new(); let mut ticks_buf = Vec::new();
let mut hit_objects: Vec<_> = map let hit_objects_iter = map.hit_objects.iter().filter_map(|h| {
.hit_objects OsuObject::new(
.iter() h,
.filter_map(|h| { map,
OsuObject::new( radius,
h, scaling_factor,
map, hr,
radius, &mut ticks_buf,
scaling_factor, &mut diff_attributes,
hr, &mut slider_state,
&mut ticks_buf, )
&mut diff_attributes, });
&mut slider_state,
) let mut hit_objects = Vec::with_capacity(map.hit_objects.len());
}) hit_objects.extend(hit_objects_iter);
.collect();
let stack_threshold = time_preempt * map.stack_leniency; let stack_threshold = time_preempt * map.stack_leniency;
@@ -266,6 +245,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
let mut hit_objects = hit_objects.into_iter().map(|mut h| { let mut hit_objects = hit_objects.into_iter().map(|mut h| {
let stack_offset = h.stack_height * scale_factor; let stack_offset = h.stack_height * scale_factor;
h.time /= map_attributes.clock_rate;
h.pos += Pos2 { h.pos += Pos2 {
x: stack_offset, x: stack_offset,
y: stack_offset, y: stack_offset,
@@ -277,27 +257,19 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
let mut prev_prev = None; let mut prev_prev = None;
let mut prev = hit_objects.next().unwrap(); let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None; let mut prev_vals = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching // Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap(); let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -309,14 +281,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
// Handle all other objects // Handle all other objects
for curr in hit_objects { for curr in hit_objects {
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
aim.save_current_peak(); aim.save_current_peak();
@@ -324,7 +289,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
speed.save_current_peak(); speed.save_current_peak();
speed.start_new_section_from(current_section_end); speed.start_new_section_from(current_section_end);
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -346,7 +311,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
.collect(); .collect();
Strains { Strains {
section_length: section_len, section_length: SECTION_LEN,
strains, strains,
} }
} }
@@ -18,10 +18,9 @@ impl<'h> DifficultyObject<'h> {
prev: &OsuObject, prev: &OsuObject,
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time) prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
prev_prev: Option<OsuObject>, prev_prev: Option<OsuObject>,
clock_rate: f32,
scaling_factor: f32, scaling_factor: f32,
) -> Self { ) -> Self {
let delta = (base.time - prev.time) / clock_rate; let delta = base.time - prev.time;
let strain_time = delta.max(50.0); let strain_time = delta.max(50.0);
let pos = base.pos; let pos = base.pos;
+35 -58
View File
@@ -52,7 +52,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
return StarResult::Osu(diff_attributes); return StarResult::Osu(diff_attributes);
} }
let section_len = SECTION_LEN * map_attributes.clock_rate;
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0; let radius = OBJECT_RADIUS * (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
let mut scaling_factor = NORMALIZED_RADIUS / radius; let mut scaling_factor = NORMALIZED_RADIUS / radius;
@@ -64,42 +63,43 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
let mut slider_state = SliderState::new(map); let mut slider_state = SliderState::new(map);
let mut ticks_buf = Vec::new(); let mut ticks_buf = Vec::new();
let mut hit_objects = map.hit_objects.iter().take(take).filter_map(|h| { let mut hit_objects = map
OsuObject::new( .hit_objects
h, .iter()
map, .take(take)
radius, .filter_map(|h| {
scaling_factor, OsuObject::new(
&mut ticks_buf, h,
&mut diff_attributes, map,
&mut slider_state, radius,
) scaling_factor,
}); &mut ticks_buf,
&mut diff_attributes,
&mut slider_state,
)
})
.map(|mut h| {
h.time /= map_attributes.clock_rate;
h
});
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
let mut prev_prev = None; let mut prev_prev = None;
let mut prev = hit_objects.next().unwrap(); let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None; let mut prev_vals = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching // Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap(); let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -111,14 +111,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
// Handle all other objects // Handle all other objects
for curr in hit_objects { for curr in hit_objects {
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
aim.save_current_peak(); aim.save_current_peak();
@@ -126,7 +119,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
speed.save_current_peak(); speed.save_current_peak();
speed.start_new_section_from(current_section_end); speed.start_new_section_from(current_section_end);
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -173,7 +166,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
return Strains::default(); return Strains::default();
} }
let section_len = SECTION_LEN * map_attributes.clock_rate;
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0; let radius = OBJECT_RADIUS * (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
let mut scaling_factor = NORMALIZED_RADIUS / radius; let mut scaling_factor = NORMALIZED_RADIUS / radius;
@@ -200,27 +192,19 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
let mut prev_prev = None; let mut prev_prev = None;
let mut prev = hit_objects.next().unwrap(); let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None; let mut prev_vals = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching // Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap(); let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -232,14 +216,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
// Handle all other objects // Handle all other objects
for curr in hit_objects { for curr in hit_objects {
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
&curr,
&prev,
prev_vals,
prev_prev,
map_attributes.clock_rate,
scaling_factor,
);
while h.base.time > current_section_end { while h.base.time > current_section_end {
aim.save_current_peak(); aim.save_current_peak();
@@ -247,7 +224,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
speed.save_current_peak(); speed.save_current_peak();
speed.start_new_section_from(current_section_end); speed.start_new_section_from(current_section_end);
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -269,7 +246,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
.collect(); .collect();
Strains { Strains {
section_length: section_len, section_length: SECTION_LEN,
strains, strains,
} }
} }
@@ -1,9 +1,7 @@
use crate::parse::HitObject; use super::OsuObject;
use std::borrow::Cow;
pub(crate) struct DifficultyObject<'h> { pub(crate) struct DifficultyObject<'h> {
pub(crate) base: &'h HitObject, pub(crate) base: &'h OsuObject,
pub(crate) prev: Option<(f32, f32)>, // (jump_dist, strain_time) pub(crate) prev: Option<(f32, f32)>, // (jump_dist, strain_time)
pub(crate) jump_dist: f32, pub(crate) jump_dist: f32,
@@ -15,17 +13,16 @@ pub(crate) struct DifficultyObject<'h> {
impl<'h> DifficultyObject<'h> { impl<'h> DifficultyObject<'h> {
pub(crate) fn new( pub(crate) fn new(
base: &'h HitObject, base: &'h OsuObject,
prev: &HitObject, prev: &OsuObject,
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time) prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
prev_prev: Option<Cow<HitObject>>, prev_prev: Option<OsuObject>,
clock_rate: f32,
scaling_factor: f32, scaling_factor: f32,
) -> Self { ) -> Self {
let delta = (base.start_time - prev.start_time) / clock_rate; let delta = base.time - prev.time;
let strain_time = delta.max(50.0); let strain_time = delta.max(50.0);
let jump_dist = if base.is_spinner() { let jump_dist = if base.is_spinner {
0.0 0.0
} else { } else {
((base.pos - prev.pos) * scaling_factor).length() ((base.pos - prev.pos) * scaling_factor).length()
+35 -73
View File
@@ -7,21 +7,18 @@
use super::super::DifficultyAttributes; use super::super::DifficultyAttributes;
mod difficulty_object; mod difficulty_object;
mod osu_object;
mod skill; mod skill;
mod skill_kind; mod skill_kind;
mod slider_state; mod slider_state;
use difficulty_object::DifficultyObject; use difficulty_object::DifficultyObject;
use osu_object::OsuObject;
use skill::Skill; use skill::Skill;
use skill_kind::SkillKind; use skill_kind::SkillKind;
use slider_state::SliderState; use slider_state::SliderState;
use crate::{ use crate::{parse::HitObjectKind, Beatmap, Mods, StarResult, Strains};
parse::{HitObject, HitObjectKind},
Beatmap, Mods, StarResult, Strains,
};
use std::borrow::Cow;
const OBJECT_RADIUS: f32 = 64.0; const OBJECT_RADIUS: f32 = 64.0;
const SECTION_LEN: f32 = 400.0; const SECTION_LEN: f32 = 400.0;
@@ -50,7 +47,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
}); });
} }
let section_len = SECTION_LEN * attributes.clock_rate;
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0; let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0;
let mut scaling_factor = NORMALIZED_RADIUS / radius; let mut scaling_factor = NORMALIZED_RADIUS / radius;
@@ -59,6 +55,8 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
scaling_factor *= 1.0 + small_circle_bonus; scaling_factor *= 1.0 + small_circle_bonus;
} }
let clock_rate = attributes.clock_rate;
let mut max_combo = 0; let mut max_combo = 0;
let mut state = SliderState::new(&map); let mut state = SliderState::new(&map);
@@ -70,24 +68,19 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
HitObjectKind::Circle => { HitObjectKind::Circle => {
max_combo += 1; max_combo += 1;
Some(Cow::Borrowed(h)) Some(OsuObject::new(h.pos, h.start_time, false, clock_rate))
} }
HitObjectKind::Slider { HitObjectKind::Slider {
pixel_len, repeats, .. pixel_len, repeats, ..
} => { } => {
max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map); max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map);
Some(Cow::Owned(HitObject { Some(OsuObject::new(h.pos, h.start_time, false, clock_rate))
pos: h.pos,
start_time: h.start_time,
kind: HitObjectKind::Circle,
sound: h.sound,
}))
} }
HitObjectKind::Spinner { .. } => { HitObjectKind::Spinner { .. } => {
max_combo += 1; max_combo += 1;
Some(Cow::Borrowed(h)) Some(OsuObject::new(h.pos, h.start_time, true, clock_rate))
} }
HitObjectKind::Hold { .. } => None, HitObjectKind::Hold { .. } => None,
}); });
@@ -95,27 +88,19 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
let mut prev_prev = None; let mut prev_prev = None;
let mut prev = hit_objects.next().unwrap(); let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None; let mut prev_vals = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching // Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap(); let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
curr.as_ref(),
prev.as_ref(),
prev_vals,
prev_prev,
attributes.clock_rate,
scaling_factor,
);
while h.base.start_time > current_section_end { while h.base.time > current_section_end {
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -127,22 +112,15 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
// Handle all other objects // Handle all other objects
for curr in hit_objects { for curr in hit_objects {
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
curr.as_ref(),
prev.as_ref(),
prev_vals,
prev_prev,
attributes.clock_rate,
scaling_factor,
);
while h.base.start_time > current_section_end { while h.base.time > current_section_end {
aim.save_current_peak(); aim.save_current_peak();
aim.start_new_section_from(current_section_end); aim.start_new_section_from(current_section_end);
speed.save_current_peak(); speed.save_current_peak();
speed.start_new_section_from(current_section_end); speed.start_new_section_from(current_section_end);
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -184,7 +162,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
return Strains::default(); return Strains::default();
} }
let section_len = SECTION_LEN * attributes.clock_rate;
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0; let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0;
let mut scaling_factor = NORMALIZED_RADIUS / radius; let mut scaling_factor = NORMALIZED_RADIUS / radius;
@@ -193,42 +170,34 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
scaling_factor *= 1.0 + small_circle_bonus; scaling_factor *= 1.0 + small_circle_bonus;
} }
let clock_rate = attributes.clock_rate;
let mut hit_objects = map.hit_objects.iter().filter_map(|h| match &h.kind { let mut hit_objects = map.hit_objects.iter().filter_map(|h| match &h.kind {
HitObjectKind::Circle => Some(Cow::Borrowed(h)), HitObjectKind::Circle | HitObjectKind::Slider { .. } => {
HitObjectKind::Slider { .. } => Some(Cow::Owned(HitObject { Some(OsuObject::new(h.pos, h.start_time, false, clock_rate))
pos: h.pos, }
start_time: h.start_time, HitObjectKind::Spinner { .. } => {
kind: HitObjectKind::Circle, Some(OsuObject::new(h.pos, h.start_time, true, clock_rate))
sound: h.sound, }
})),
HitObjectKind::Spinner { .. } => Some(Cow::Borrowed(h)),
HitObjectKind::Hold { .. } => None, HitObjectKind::Hold { .. } => None,
}); });
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
let mut prev_prev = None; let mut prev_prev = None;
let mut prev = hit_objects.next().unwrap(); let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None; let mut prev_vals = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching // Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap(); let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
curr.as_ref(),
prev.as_ref(),
prev_vals,
prev_prev,
attributes.clock_rate,
scaling_factor,
);
while h.base.start_time > current_section_end { while h.base.time > current_section_end {
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -240,22 +209,15 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
// Handle all other objects // Handle all other objects
for curr in hit_objects { for curr in hit_objects {
let h = DifficultyObject::new( let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
curr.as_ref(),
prev.as_ref(),
prev_vals,
prev_prev,
attributes.clock_rate,
scaling_factor,
);
while h.base.start_time > current_section_end { while h.base.time > current_section_end {
aim.save_current_peak(); aim.save_current_peak();
aim.start_new_section_from(current_section_end); aim.start_new_section_from(current_section_end);
speed.save_current_peak(); speed.save_current_peak();
speed.start_new_section_from(current_section_end); speed.start_new_section_from(current_section_end);
current_section_end += section_len; current_section_end += SECTION_LEN;
} }
aim.process(&h); aim.process(&h);
@@ -277,7 +239,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
.collect(); .collect();
Strains { Strains {
section_length: section_len, section_length: SECTION_LEN,
strains, strains,
} }
} }
@@ -0,0 +1,18 @@
use crate::parse::Pos2;
pub(crate) struct OsuObject {
pub(crate) pos: Pos2,
pub(crate) time: f32,
pub(crate) is_spinner: bool,
}
impl OsuObject {
#[inline]
pub(crate) fn new(pos: Pos2, time: f32, is_spinner: bool, clock_rate: f32) -> Self {
Self {
pos,
time: time / clock_rate,
is_spinner,
}
}
}
@@ -52,7 +52,7 @@ impl Skill {
self.current_strain *= self.strain_decay(current.delta); self.current_strain *= self.strain_decay(current.delta);
self.current_strain += self.kind.strain_value_of(&current) * self.skill_multiplier(); self.current_strain += self.kind.strain_value_of(&current) * self.skill_multiplier();
self.current_section_peak = self.current_section_peak.max(self.current_strain); self.current_section_peak = self.current_section_peak.max(self.current_strain);
self.prev_time.replace(current.base.start_time); self.prev_time.replace(current.base.time);
} }
pub(crate) fn difficulty_value(&mut self) -> f32 { pub(crate) fn difficulty_value(&mut self) -> f32 {
@@ -28,7 +28,7 @@ impl SkillKind {
pub(crate) fn strain_value_of(self, current: &DifficultyObject) -> f32 { pub(crate) fn strain_value_of(self, current: &DifficultyObject) -> f32 {
match self { match self {
Self::Aim => { Self::Aim => {
if current.base.is_spinner() { if current.base.is_spinner {
return 0.0; return 0.0;
} }
@@ -54,7 +54,7 @@ impl SkillKind {
.max(jump_dist_exp / current.strain_time) .max(jump_dist_exp / current.strain_time)
} }
Self::Speed => { Self::Speed => {
if current.base.is_spinner() { if current.base.is_spinner {
return 0.0; return 0.0;
} }
+36 -36
View File
@@ -128,111 +128,111 @@ const RESULTS: &[MapResult] = &[
MapResult { MapResult {
map_id: 1851299, map_id: 1851299,
mods: 256, mods: 256,
stars: 4.23514130038547, stars: 4.19951953364192,
pp: 96.59532540603362, pp: 95.35544846090738,
}, },
MapResult { MapResult {
map_id: 1851299, map_id: 1851299,
mods: 0, mods: 0,
stars: 5.356786475158158, stars: 5.305946555352317,
pp: 191.72186087468594, pp: 188.7611225698759,
}, },
MapResult { MapResult {
map_id: 1851299, map_id: 1851299,
mods: 8, mods: 8,
stars: 5.356786475158158, stars: 5.305946555352317,
pp: 211.17333597663404, pp: 207.87782991080368,
}, },
MapResult { MapResult {
map_id: 1851299, map_id: 1851299,
mods: 64, mods: 64,
stars: 7.450616908751305, stars: 7.352573837272898,
pp: 476.39199443787675, pp: 465.60165096277717,
}, },
MapResult { MapResult {
map_id: 1851299, map_id: 1851299,
mods: 16, mods: 16,
stars: 5.6834681957637665, stars: 5.628029058321052,
pp: 243.32730989490153, pp: 239.33966091681467,
}, },
MapResult { MapResult {
map_id: 1851299, map_id: 1851299,
mods: 2, mods: 2,
stars: 4.937817303399699, stars: 4.892665488817249,
pp: 110.44350158714633, pp: 108.66545494037493,
}, },
// ----- // -----
MapResult { MapResult {
map_id: 70090, map_id: 70090,
mods: 256, mods: 256,
stars: 2.2929922580201803, stars: 2.2531214736733975,
pp: 17.530571082228978, pp: 17.064864347414005,
}, },
MapResult { MapResult {
map_id: 70090, map_id: 70090,
mods: 0, mods: 0,
stars: 2.8322940761833983, stars: 2.7853401027561353,
pp: 40.611760049886534, pp: 39.80360462535964,
}, },
MapResult { MapResult {
map_id: 70090, map_id: 70090,
mods: 8, mods: 8,
stars: 2.8322940761833983, stars: 2.7853401027561353,
pp: 46.252172598153074, pp: 45.27724541951056,
}, },
MapResult { MapResult {
map_id: 70090, map_id: 70090,
mods: 64, mods: 64,
stars: 3.8338563325375485, stars: 3.7775299223395877,
pp: 110.32389105793393, pp: 108.27132697867293,
}, },
MapResult { MapResult {
map_id: 70090, map_id: 70090,
mods: 16, mods: 16,
stars: 3.0617492228478174, stars: 3.0128373294988626,
pp: 84.67846960014381, pp: 83.70428900396428,
}, },
MapResult { MapResult {
map_id: 70090, map_id: 70090,
mods: 2, mods: 2,
stars: 2.698823231324141, stars: 2.673167484837261,
pp: 21.861918597227252, pp: 21.254867316318986,
}, },
// ----- // -----
MapResult { MapResult {
map_id: 1241370, map_id: 1241370,
mods: 256, mods: 256,
stars: 5.662809600985943, stars: 5.558026586611704,
pp: 346.1069865511771, pp: 334.51647189180727,
}, },
MapResult { MapResult {
map_id: 1241370, map_id: 1241370,
mods: 0, mods: 0,
stars: 7.0367002127481975, stars: 6.983848076867755,
pp: 658.4944314112954, pp: 649.1653315906666,
}, },
MapResult { MapResult {
map_id: 1241370, map_id: 1241370,
mods: 8, mods: 8,
stars: 7.0367002127481975, stars: 6.983848076867755,
pp: 720.8614284211293, pp: 710.594432790455,
}, },
MapResult { MapResult {
map_id: 1241370, map_id: 1241370,
mods: 64, mods: 64,
stars: 11.144720506574934, stars: 11.076248857241552,
pp: 2414.665180655108, pp: 2378.593642686663,
}, },
MapResult { MapResult {
map_id: 1241370, map_id: 1241370,
mods: 16, mods: 16,
stars: 7.641688110458715, stars: 7.616427878599069,
pp: 853.7411405318841, pp: 843.421001465897,
}, },
MapResult { MapResult {
map_id: 1241370, map_id: 1241370,
mods: 2, mods: 2,
stars: 6.316288616688052, stars: 6.289772601786212,
pp: 357.3089261481221, pp: 354.960619132034,
}, },
]; ];