osu: finished all_included version

This commit is contained in:
MaxOhn
2021-01-26 18:01:47 +01:00
parent 7d8653d57d
commit 07bf677982
6 changed files with 85 additions and 32 deletions
@@ -24,9 +24,9 @@ impl<'h> DifficultyObject<'h> {
let delta = (base.time - prev.time) / clock_rate; let delta = (base.time - prev.time) / clock_rate;
let strain_time = delta.max(50.0); let strain_time = delta.max(50.0);
let pos = base.pos; let pos = base.pos; // stacked position
let travel_dist = prev.travel_dist(); let travel_dist = prev.travel_dist();
let prev_cursor_pos = prev.end_pos(); let prev_cursor_pos = prev.lazy_end_pos();
let jump_dist = if base.is_spinner() { let jump_dist = if base.is_spinner() {
0.0 0.0
@@ -35,9 +35,9 @@ impl<'h> DifficultyObject<'h> {
}; };
let angle = prev_prev.map(|prev_prev| { let angle = prev_prev.map(|prev_prev| {
let prev_prev_cursor_pos = prev_prev.end_pos(); let prev_prev_cursor_pos = prev_prev.lazy_end_pos();
let v1 = prev_prev_cursor_pos - prev_cursor_pos; let v1 = prev_prev_cursor_pos - prev.pos;
let v2 = pos - prev_cursor_pos; let v2 = pos - prev_cursor_pos;
let dot = v1.dot(v2); let dot = v1.dot(v2);
+45 -14
View File
@@ -25,7 +25,6 @@ const OBJECT_RADIUS: f32 = 64.0;
const SECTION_LEN: f32 = 400.0; const SECTION_LEN: f32 = 400.0;
const DIFFICULTY_MULTIPLIER: f32 = 0.0675; const DIFFICULTY_MULTIPLIER: f32 = 0.0675;
const NORMALIZED_RADIUS: f32 = 52.0; const NORMALIZED_RADIUS: f32 = 52.0;
const TIME_PREEMPT: f32 = 600.0; // TODO: Check mod influences
const STACK_DISTANCE: f32 = 3.0; const STACK_DISTANCE: f32 = 3.0;
/// Star calculation for osu!standard maps. /// Star calculation for osu!standard maps.
@@ -53,6 +52,16 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
return StarResult::Osu(diff_attributes); return StarResult::Osu(diff_attributes);
} }
let mut raw_ar = map.ar;
if mods.hr() {
raw_ar *= 1.4;
} else if mods.ez() {
raw_ar *= 0.5;
}
let time_preempt = difficulty_range_ar(raw_ar);
let section_len = SECTION_LEN * map_attributes.clock_rate; 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;
@@ -75,6 +84,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
h, h,
map, map,
radius, radius,
scaling_factor,
&mut ticks_buf, &mut ticks_buf,
&mut diff_attributes, &mut diff_attributes,
&mut slider_state, &mut slider_state,
@@ -82,10 +92,12 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
}) })
.collect(); .collect();
let stack_threshold = time_preempt * map.stack_leniency;
if map.version >= 6 { if map.version >= 6 {
stacking(&mut hit_objects, map.stack_leniency, TIME_PREEMPT); stacking(&mut hit_objects, stack_threshold);
} else { } else {
old_stacking(&mut hit_objects, map.stack_leniency, TIME_PREEMPT); old_stacking(&mut hit_objects, stack_threshold);
} }
let mut hit_objects = hit_objects.into_iter().map(|mut h| { let mut hit_objects = hit_objects.into_iter().map(|mut h| {
@@ -194,6 +206,16 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
return Strains::default(); return Strains::default();
} }
let mut raw_ar = map.ar;
if mods.hr() {
raw_ar *= 1.4;
} else if mods.ez() {
raw_ar *= 0.5;
}
let time_preempt = difficulty_range_ar(raw_ar);
let section_len = SECTION_LEN * map_attributes.clock_rate; 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;
@@ -215,6 +237,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
h, h,
map, map,
radius, radius,
scaling_factor,
&mut ticks_buf, &mut ticks_buf,
&mut diff_attributes, &mut diff_attributes,
&mut slider_state, &mut slider_state,
@@ -222,10 +245,12 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
}) })
.collect(); .collect();
let stack_threshold = time_preempt * map.stack_leniency;
if map.version >= 6 { if map.version >= 6 {
stacking(&mut hit_objects, map.stack_leniency, TIME_PREEMPT); stacking(&mut hit_objects, stack_threshold);
} else { } else {
old_stacking(&mut hit_objects, map.stack_leniency, TIME_PREEMPT); old_stacking(&mut hit_objects, stack_threshold);
} }
let mut hit_objects = hit_objects.into_iter().map(|mut h| { let mut hit_objects = hit_objects.into_iter().map(|mut h| {
@@ -316,12 +341,10 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
} }
} }
fn stacking(hit_objects: &mut [OsuObject], stack_leniency: f32, time_preempt: f32) { fn stacking(hit_objects: &mut [OsuObject], stack_threshold: f32) {
let mut extended_start_idx = 0; let mut extended_start_idx = 0;
let extended_end_idx = hit_objects.len() - 1; let extended_end_idx = hit_objects.len() - 1;
let stack_threshold = time_preempt * stack_leniency; // TODO: Calculate outside?
for mut i in (1..=extended_end_idx).rev() { for mut i in (1..=extended_end_idx).rev() {
let mut n = i; let mut n = i;
@@ -380,16 +403,15 @@ fn stacking(hit_objects: &mut [OsuObject], stack_leniency: f32, time_preempt: f3
} }
} }
fn old_stacking(hit_objects: &mut [OsuObject], stack_leniency: f32, time_preempt: f32) { fn old_stacking(hit_objects: &mut [OsuObject], stack_threshold: f32) {
let stack_threshold = time_preempt * stack_leniency; // TODO: Calculate outside?
for i in 0..hit_objects.len() { for i in 0..hit_objects.len() {
if hit_objects[i].stack_height != 0.0 && !hit_objects[i].is_slider() { if hit_objects[i].stack_height != 0.0 && !hit_objects[i].is_slider() {
continue; continue;
} }
let mut start_time = hit_objects[i].end_time(); let mut start_time = hit_objects[i].end_time();
let end_pos = hit_objects[i].end_pos(); // FIXME: Wrong for sliders? Position at 100% progress. let end_pos = hit_objects[i].end_pos();
let mut slider_stack = 0.0; let mut slider_stack = 0.0;
for j in i + 1..hit_objects.len() { for j in i + 1..hit_objects.len() {
@@ -409,6 +431,15 @@ fn old_stacking(hit_objects: &mut [OsuObject], stack_leniency: f32, time_preempt
} }
} }
const OSU_AR_MAX: f32 = 450.0;
const OSU_AR_AVG: f32 = 1200.0;
const OSU_AR_MIN: f32 = 1800.0;
#[inline]
fn difficulty_range_ar(ar: f32) -> f32 {
crate::difficulty_range(ar, OSU_AR_MAX, OSU_AR_AVG, OSU_AR_MIN)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::super::OsuPP; use super::super::super::OsuPP;
@@ -416,9 +447,9 @@ mod tests {
use std::fs::File; use std::fs::File;
#[test] #[test]
#[ignore] // #[ignore]
fn all_included_single() { fn all_included_single() {
let file = match File::open("./maps/295.osu") { let file = match File::open("./maps/2514909.osu") {
Ok(file) => file, Ok(file) => file,
Err(why) => panic!("Could not open file: {}", why), Err(why) => panic!("Could not open file: {}", why),
}; };
+18 -3
View File
@@ -17,6 +17,7 @@ enum OsuObjectKind {
Slider { Slider {
end_time: f32, end_time: f32,
end_pos: Pos2, end_pos: Pos2,
lazy_end_pos: Pos2,
travel_dist: f32, travel_dist: f32,
}, },
Spinner { Spinner {
@@ -29,6 +30,7 @@ impl OsuObject {
h: &HitObject, h: &HitObject,
map: &Beatmap, map: &Beatmap,
radius: f32, radius: f32,
scaling_factor: f32,
ticks: &mut Vec<f32>, ticks: &mut Vec<f32>,
attributes: &mut DifficultyAttributes, attributes: &mut DifficultyAttributes,
slider_state: &mut SliderState, slider_state: &mut SliderState,
@@ -54,7 +56,7 @@ impl OsuObject {
path_type, path_type,
} => { } => {
// Key values which are computed here // Key values which are computed here
let mut end_pos = h.pos; let mut lazy_end_pos = h.pos;
let mut travel_dist = 0.0; let mut travel_dist = 0.0;
// Responsible for timing point values // Responsible for timing point values
@@ -107,12 +109,12 @@ impl OsuObject {
let curr_dist = pixel_len * progress; let curr_dist = pixel_len * progress;
let curr_pos = curve.point_at_distance(curr_dist); let curr_pos = curve.point_at_distance(curr_dist);
let diff = curr_pos - end_pos; let diff = curr_pos - lazy_end_pos;
let mut dist = diff.length(); let mut dist = diff.length();
if dist > approx_follow_circle_radius { if dist > approx_follow_circle_radius {
dist -= approx_follow_circle_radius; dist -= approx_follow_circle_radius;
end_pos += diff.normalize() * dist; lazy_end_pos += diff.normalize() * dist;
travel_dist += dist; travel_dist += dist;
} }
}; };
@@ -163,6 +165,10 @@ impl OsuObject {
ticks.clear(); ticks.clear();
travel_dist *= scaling_factor;
let end_pos = curve.point_at_distance(*pixel_len);
Self { Self {
time: h.start_time, time: h.start_time,
pos: h.pos, pos: h.pos,
@@ -170,6 +176,7 @@ impl OsuObject {
kind: OsuObjectKind::Slider { kind: OsuObjectKind::Slider {
end_time: final_span_end_time, end_time: final_span_end_time,
end_pos, end_pos,
lazy_end_pos,
travel_dist, travel_dist,
}, },
} }
@@ -217,6 +224,14 @@ impl OsuObject {
} }
} }
#[inline]
pub(crate) fn lazy_end_pos(&self) -> Pos2 {
match &self.kind {
OsuObjectKind::Circle | OsuObjectKind::Spinner { .. } => self.pos,
OsuObjectKind::Slider { lazy_end_pos, .. } => *lazy_end_pos,
}
}
#[inline] #[inline]
pub(crate) fn is_circle(&self) -> bool { pub(crate) fn is_circle(&self) -> bool {
matches!(self.kind, OsuObjectKind::Circle) matches!(self.kind, OsuObjectKind::Circle)
+3 -1
View File
@@ -68,6 +68,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
h, h,
map, map,
radius, radius,
scaling_factor,
&mut ticks_buf, &mut ticks_buf,
&mut diff_attributes, &mut diff_attributes,
&mut slider_state, &mut slider_state,
@@ -186,6 +187,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
h, h,
map, map,
radius, radius,
scaling_factor,
&mut ticks_buf, &mut ticks_buf,
&mut diff_attributes, &mut diff_attributes,
&mut slider_state, &mut slider_state,
@@ -278,7 +280,7 @@ mod tests {
#[test] #[test]
#[ignore] #[ignore]
fn no_leniency_single() { fn no_leniency_single() {
let file = match File::open("./maps/295.osu") { let file = match File::open("./maps/2514909.osu") {
Ok(file) => file, Ok(file) => file,
Err(why) => panic!("Could not open file: {}", why), Err(why) => panic!("Could not open file: {}", why),
}; };
@@ -18,6 +18,7 @@ impl OsuObject {
h: &HitObject, h: &HitObject,
map: &Beatmap, map: &Beatmap,
radius: f32, radius: f32,
scaling_factor: f32,
ticks: &mut Vec<f32>, ticks: &mut Vec<f32>,
attributes: &mut DifficultyAttributes, attributes: &mut DifficultyAttributes,
slider_state: &mut SliderState, slider_state: &mut SliderState,
@@ -151,6 +152,8 @@ impl OsuObject {
ticks.clear(); ticks.clear();
travel_dist *= scaling_factor;
Self { Self {
time: h.start_time, time: h.start_time,
pos: h.pos, pos: h.pos,
+12 -10
View File
@@ -14,12 +14,14 @@ struct MapResult {
#[test] #[test]
fn osu() { fn osu() {
let margin = if cfg!(feature = "no_leniency") { let margin = if cfg!(feature = "no_sliders_no_leniency") {
0.0025
} else if cfg!(feature = "no_sliders_no_leniency") {
0.0075 0.0075
} else { } else if cfg!(feature = "no_leniency") {
0.0025
} else if cfg!(feature = "all_included") {
0.001 0.001
} else {
unreachable!()
}; };
let star_margin = margin; let star_margin = margin;
@@ -48,9 +50,9 @@ fn osu() {
assert!( assert!(
(result.stars() - stars).abs() < star_margin * stars, (result.stars() - stars).abs() < star_margin * stars,
"\nStars:\n\ "\nStars:\n\
Calculated: {calculated} | Expected: {expected}\n \ Calculated: {calculated} | Expected: {expected}\n \
=> {margin} margin ({allowed} allowed)\n\ => {margin} margin ({allowed} allowed)\n\
[map {map} | mods {mods}]\n", [map {map} | mods {mods}]\n",
calculated = result.stars(), calculated = result.stars(),
expected = stars, expected = stars,
margin = (result.stars() - stars).abs(), margin = (result.stars() - stars).abs(),
@@ -62,9 +64,9 @@ fn osu() {
assert!( assert!(
(result.pp() - pp).abs() < pp_margin * pp, (result.pp() - pp).abs() < pp_margin * pp,
"\nPP:\n\ "\nPP:\n\
Calculated: {calculated} | Expected: {expected}\n \ Calculated: {calculated} | Expected: {expected}\n \
=> {margin} margin ({allowed} allowed)\n\ => {margin} margin ({allowed} allowed)\n\
[map {map} | mods {mods}]\n", [map {map} | mods {mods}]\n",
calculated = result.pp(), calculated = result.pp(),
expected = pp, expected = pp,
margin = (result.pp() - pp).abs(), margin = (result.pp() - pp).abs(),