impl c#'s unstable sort & refactor sort things
This commit is contained in:
@@ -8,7 +8,7 @@ use crate::{
|
||||
hit_object::{HitObject, HitObjectKind, HoldNote, Spinner},
|
||||
mode::ConvertStatus,
|
||||
},
|
||||
util::{float_ext::FloatExt, random::Random},
|
||||
util::{float_ext::FloatExt, random::Random, sort},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -75,7 +75,9 @@ pub fn convert_objects(
|
||||
palpable_objects.extend(new_objects);
|
||||
}
|
||||
|
||||
palpable_objects.sort_by(|a, b| a.start_time.total_cmp(&b.start_time));
|
||||
sort::csharp(&mut palpable_objects, |a, b| {
|
||||
a.start_time.total_cmp(&b.start_time)
|
||||
});
|
||||
|
||||
initialize_hyper_dash(cs, &mut palpable_objects);
|
||||
|
||||
|
||||
@@ -11,9 +11,7 @@ use crate::{
|
||||
hit_object::{HitObjectKind, HoldNote, Spinner},
|
||||
mode::ConvertStatus,
|
||||
},
|
||||
util::{
|
||||
float_ext::FloatExt, legacy_sort::legacy_sort, limited_queue::LimitedQueue, random::Random,
|
||||
},
|
||||
util::{float_ext::FloatExt, limited_queue::LimitedQueue, random::Random, sort},
|
||||
};
|
||||
|
||||
use self::{
|
||||
@@ -161,7 +159,7 @@ fn convert(map: &mut Beatmap) {
|
||||
map.hit_objects = new_hit_objects;
|
||||
map.hit_objects
|
||||
.sort_by(|a, b| a.start_time.total_cmp(&b.start_time));
|
||||
legacy_sort(&mut map.hit_objects);
|
||||
sort::osu_legacy(&mut map.hit_objects);
|
||||
|
||||
map.mode = GameMode::Mania;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::{
|
||||
},
|
||||
hit_object::{HitObject, HitObjectKind, HoldNote, Slider, Spinner},
|
||||
},
|
||||
util::{float_ext::FloatExt, legacy_sort::legacy_sort, tandem_sort::TandemSorter},
|
||||
util::{float_ext::FloatExt, sort},
|
||||
};
|
||||
|
||||
use super::{Beatmap, DEFAULT_SLIDER_LENIENCY};
|
||||
@@ -282,7 +282,7 @@ impl From<BeatmapState> for Beatmap {
|
||||
slider_tick_rate,
|
||||
} = state.difficulty.into();
|
||||
|
||||
let mut sorter = TandemSorter::new(
|
||||
let mut sorter = sort::TandemSorter::new(
|
||||
&state.hit_objects,
|
||||
|a, b| a.start_time.total_cmp(&b.start_time),
|
||||
true,
|
||||
@@ -292,7 +292,7 @@ impl From<BeatmapState> for Beatmap {
|
||||
sorter.sort(&mut state.hit_sounds);
|
||||
|
||||
if state.mode == GameMode::Mania {
|
||||
legacy_sort(&mut state.hit_objects);
|
||||
sort::osu_legacy(&mut state.hit_objects);
|
||||
}
|
||||
|
||||
Beatmap {
|
||||
|
||||
+9
-4
@@ -3,9 +3,12 @@ use rosu_map::{
|
||||
util::Pos,
|
||||
};
|
||||
|
||||
use crate::model::{
|
||||
control_point::{DifficultyPoint, TimingPoint},
|
||||
hit_object::{HitObject, HitObjectKind, HoldNote, Slider, Spinner},
|
||||
use crate::{
|
||||
model::{
|
||||
control_point::{DifficultyPoint, TimingPoint},
|
||||
hit_object::{HitObject, HitObjectKind, HoldNote, Slider, Spinner},
|
||||
},
|
||||
util::sort,
|
||||
};
|
||||
|
||||
use super::{convert::OsuBeatmap, PLAYFIELD_BASE_SIZE};
|
||||
@@ -255,7 +258,9 @@ impl OsuSlider {
|
||||
})
|
||||
.collect();
|
||||
|
||||
nested_objects.sort_unstable_by(|a, b| a.start_time.total_cmp(&b.start_time));
|
||||
sort::csharp(&mut nested_objects, |a, b| {
|
||||
a.start_time.total_cmp(&b.start_time)
|
||||
});
|
||||
|
||||
let lazy_travel_time = nested_objects
|
||||
.last()
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
hit_object::{HitObject, HitObjectKind, HoldNote, Slider, Spinner},
|
||||
mode::ConvertStatus,
|
||||
},
|
||||
util::{float_ext::FloatExt, tandem_sort::TandemSorter},
|
||||
util::{float_ext::FloatExt, sort::TandemSorter},
|
||||
};
|
||||
|
||||
use super::Taiko;
|
||||
|
||||
@@ -78,7 +78,7 @@ impl Peaks {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
peaks.sort_unstable_by(|a, b| b.total_cmp(a));
|
||||
peaks.sort_by(f64::total_cmp);
|
||||
|
||||
for strain in peaks {
|
||||
difficulty += strain * weight;
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
pub mod float_ext;
|
||||
pub mod legacy_sort;
|
||||
pub mod limited_queue;
|
||||
pub mod map_or_attrs;
|
||||
pub mod mods;
|
||||
pub mod random;
|
||||
pub mod tandem_sort;
|
||||
pub mod sort;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// C#'s unstable sorting algorithm.
|
||||
///
|
||||
/// <https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,fc1bc8c930f6c93c,references>
|
||||
pub fn sort<T, F>(keys: &mut [T], cmp: F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
introspective_sort(keys, 0, keys.len(), &cmp);
|
||||
}
|
||||
|
||||
fn introspective_sort<T, F>(keys: &mut [T], left: usize, len: usize, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
if len >= 2 {
|
||||
intro_sort(keys, left, len + left - 1, 2 * keys.len().ilog2(), cmp);
|
||||
}
|
||||
}
|
||||
|
||||
fn intro_sort<T, F>(keys: &mut [T], lo: usize, mut hi: usize, mut depth_limit: u32, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
const INTRO_SORT_SIZE_THRESHOLD: usize = 16;
|
||||
|
||||
while hi > lo {
|
||||
let partition_size = hi - lo + 1;
|
||||
|
||||
if partition_size <= INTRO_SORT_SIZE_THRESHOLD {
|
||||
match partition_size {
|
||||
1 => {}
|
||||
2 => super::swap_if_greater(keys, cmp, lo, hi),
|
||||
3 => {
|
||||
super::swap_if_greater(keys, cmp, lo, hi - 1);
|
||||
super::swap_if_greater(keys, cmp, lo, hi);
|
||||
super::swap_if_greater(keys, cmp, hi - 1, hi);
|
||||
}
|
||||
_ => insertion_sort(keys, lo, hi, cmp),
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if depth_limit == 0 {
|
||||
super::heap_sort(keys, lo, hi, cmp);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
depth_limit -= 1;
|
||||
let p = pick_pivot_and_partition(keys, lo, hi, cmp);
|
||||
intro_sort(keys, p + 1, hi, depth_limit, cmp);
|
||||
hi = p - 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_pivot_and_partition<T, F>(keys: &mut [T], lo: usize, hi: usize, cmp: &F) -> usize
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
super::swap_if_greater(keys, cmp, lo, mid);
|
||||
super::swap_if_greater(keys, cmp, lo, hi);
|
||||
super::swap_if_greater(keys, cmp, mid, hi);
|
||||
super::swap(keys, mid, hi - 1);
|
||||
let mut left = lo;
|
||||
let mut right = hi - 1;
|
||||
|
||||
let mut pivot_idx = right;
|
||||
let mut next_left = true;
|
||||
|
||||
while left < right {
|
||||
while {
|
||||
left += 1;
|
||||
|
||||
cmp(&keys[left], &keys[pivot_idx]).is_lt()
|
||||
} {}
|
||||
|
||||
while {
|
||||
right -= 1;
|
||||
|
||||
cmp(&keys[pivot_idx], &keys[right]).is_lt()
|
||||
} {}
|
||||
|
||||
if left >= right {
|
||||
break;
|
||||
}
|
||||
|
||||
super::swap(keys, left, right);
|
||||
|
||||
if next_left {
|
||||
pivot_idx = left;
|
||||
} else {
|
||||
pivot_idx = right;
|
||||
}
|
||||
|
||||
next_left = !next_left;
|
||||
}
|
||||
|
||||
super::swap(keys, left, hi - 1);
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
fn insertion_sort<T, F>(keys: &mut [T], lo: usize, hi: usize, cmp: F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
for i in lo..hi {
|
||||
let target = &keys[i + 1];
|
||||
|
||||
let smaller_idx = keys[lo..=i]
|
||||
.iter()
|
||||
.rposition(|curr| cmp(target, curr).is_ge());
|
||||
|
||||
if let Some(idx) = smaller_idx {
|
||||
keys[lo + idx + 1..=i + 1].rotate_right(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
pub use self::{csharp::sort as csharp, osu_legacy::sort as osu_legacy, tandem::TandemSorter};
|
||||
|
||||
mod csharp;
|
||||
mod osu_legacy;
|
||||
mod tandem;
|
||||
|
||||
fn heap_sort<T, F>(keys: &mut [T], lo: usize, hi: usize, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
let n = hi - lo + 1;
|
||||
|
||||
for i in (1..=n / 2).rev() {
|
||||
down_heap(keys, i, n, lo, cmp);
|
||||
}
|
||||
|
||||
for i in (2..=n).rev() {
|
||||
swap(keys, lo, lo + i - 1);
|
||||
down_heap(keys, 1, i - 1, lo, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
fn down_heap<T, F>(keys: &mut [T], mut i: usize, n: usize, lo: usize, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
while i <= n / 2 {
|
||||
let mut child = 2 * i;
|
||||
|
||||
if child < n && cmp(&keys[lo + child - 1], &keys[lo + child]).is_lt() {
|
||||
child += 1;
|
||||
}
|
||||
|
||||
if cmp(&keys[lo + i - 1], &keys[lo + child - 1]).is_ge() {
|
||||
break;
|
||||
}
|
||||
|
||||
keys.swap(lo + i - 1, lo + child - 1);
|
||||
i = child;
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_if_greater<T, F>(keys: &mut [T], cmp: &F, a: usize, b: usize)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
if a != b && cmp(&keys[a], &keys[b]).is_gt() {
|
||||
keys.swap(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
fn swap<T>(keys: &mut [T], i: usize, j: usize) {
|
||||
if i != j {
|
||||
keys.swap(i, j)
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@ use crate::model::hit_object::HitObject;
|
||||
|
||||
const QUICK_SORT_DEPTH_THRESHOLD: usize = 32;
|
||||
|
||||
/// [Legacy sorting algorithm](https://github.com/ppy/osu/blob/e669e28dc9b6d79d82a36053e6a279de8dafddd1/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs#L19)
|
||||
pub fn legacy_sort(keys: &mut [HitObject]) {
|
||||
/// osu!'s legacy sorting algorithm.
|
||||
///
|
||||
/// <https://github.com/ppy/osu/blob/e669e28dc9b6d79d82a36053e6a279de8dafddd1/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs#L19>
|
||||
pub fn sort(keys: &mut [HitObject]) {
|
||||
if keys.len() < 2 {
|
||||
return;
|
||||
}
|
||||
@@ -21,7 +23,7 @@ fn depth_limited_quick_sort(
|
||||
) {
|
||||
loop {
|
||||
if depth_limit == 0 {
|
||||
heap_sort(keys, left, right);
|
||||
super::heap_sort(keys, left, right, &cmp);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -31,17 +33,9 @@ fn depth_limited_quick_sort(
|
||||
|
||||
let mid = i + ((j - i) >> 1);
|
||||
|
||||
if keys[i] > keys[mid] {
|
||||
keys.swap(i, mid);
|
||||
}
|
||||
|
||||
if keys[i] > keys[j] {
|
||||
keys.swap(i, j);
|
||||
}
|
||||
|
||||
if keys[mid] > keys[j] {
|
||||
keys.swap(mid, j);
|
||||
}
|
||||
super::swap_if_greater(keys, &cmp, i, mid);
|
||||
super::swap_if_greater(keys, &cmp, i, j);
|
||||
super::swap_if_greater(keys, &cmp, mid, j);
|
||||
|
||||
loop {
|
||||
while keys[i] < keys[mid] {
|
||||
@@ -88,32 +82,6 @@ fn depth_limited_quick_sort(
|
||||
}
|
||||
}
|
||||
|
||||
fn heap_sort(keys: &mut [HitObject], lo: usize, hi: usize) {
|
||||
let n = hi - lo + 1;
|
||||
|
||||
for i in (1..=n / 2).rev() {
|
||||
down_heap(keys, i, n, lo);
|
||||
}
|
||||
|
||||
for i in (2..=n).rev() {
|
||||
keys.swap(lo, lo + i - 1);
|
||||
down_heap(keys, 1, i - 1, lo);
|
||||
}
|
||||
}
|
||||
|
||||
fn down_heap(keys: &mut [HitObject], mut i: usize, n: usize, lo: usize) {
|
||||
while i <= n / 2 {
|
||||
let mut child = 2 * i;
|
||||
|
||||
if child < n && keys[lo + child - 1] < keys[lo + child] {
|
||||
child += 1;
|
||||
}
|
||||
|
||||
if keys[lo + i - 1] >= keys[lo + child - 1] {
|
||||
break;
|
||||
}
|
||||
|
||||
keys.swap(lo + i - 1, lo + child - 1);
|
||||
i = child;
|
||||
}
|
||||
fn cmp(a: &HitObject, b: &HitObject) -> Ordering {
|
||||
a.start_time.total_cmp(&b.start_time)
|
||||
}
|
||||
@@ -18,6 +18,8 @@ impl TandemSorter {
|
||||
if stable {
|
||||
indices.sort_by(sort_by);
|
||||
} else {
|
||||
// When sorting integers, the order of elements with equal values
|
||||
// does not matter so we can use rust's sort instead of C#'s.
|
||||
indices.sort_unstable_by(sort_by);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user