fixed taiko

This commit is contained in:
MaxOhn
2022-10-20 00:10:38 +02:00
parent 6ff46367ca
commit 17ce0ef2c9
21 changed files with 278 additions and 274 deletions
+1 -1
View File
@@ -157,7 +157,7 @@ impl BeatmapAttributesBuilder {
Self::TAIKO_MAX, Self::TAIKO_MAX,
); );
diff_range.floor() / clock_rate diff_range / clock_rate
} }
GameMode::Mania => { GameMode::Mania => {
let mut value = if !self.converted { let mut value = if !self.converted {
+5 -4
View File
@@ -1,8 +1,7 @@
use std::cmp::Ordering;
use crate::{ use crate::{
curve::{Curve, CurveBuffers}, curve::{Curve, CurveBuffers},
parse::{HitObject, HitObjectKind}, parse::{HitObject, HitObjectKind},
util::TandemSorter,
Beatmap, GameMode, Beatmap, GameMode,
}; };
@@ -87,8 +86,10 @@ impl Beatmap {
// We only convert STD to TKO so we don't need to remove objects // We only convert STD to TKO so we don't need to remove objects
// with the same timestamp that would appear only in MNA // with the same timestamp that would appear only in MNA
map.hit_objects let mut sorter = TandemSorter::new(&map.hit_objects, true);
.sort_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal)); sorter.sort(&mut map.hit_objects);
sorter.toggle_marks();
sorter.sort(&mut map.sounds);
map.mode = GameMode::Taiko; map.mode = GameMode::Taiko;
+1 -1
View File
@@ -46,7 +46,7 @@ pub enum GradualDifficultyAttributes<'map> {
/// Gradual osu!standard difficulty attributes. /// Gradual osu!standard difficulty attributes.
Osu(OsuGradualDifficultyAttributes), Osu(OsuGradualDifficultyAttributes),
/// Gradual osu!taiko difficulty attributes. /// Gradual osu!taiko difficulty attributes.
Taiko(TaikoGradualDifficultyAttributes<'map>), Taiko(TaikoGradualDifficultyAttributes),
} }
impl<'map> GradualDifficultyAttributes<'map> { impl<'map> GradualDifficultyAttributes<'map> {
+22 -5
View File
@@ -521,22 +521,39 @@ mod tests {
#[test] #[test]
fn custom() { fn custom() {
let path = "F:/osu!/beatmaps/34881.osu"; let path = "F:/osu!/beatmaps/1186086.osu";
let map = Beatmap::from_path(path).unwrap(); let map = Beatmap::from_path(path).unwrap();
let attrs = match OsuPP::new(&map).mode(GameMode::Mania).mods(0).calculate() { let attrs = match OsuPP::new(&map).mode(GameMode::Taiko).mods(0).calculate() {
PerformanceAttributes::Mania(attrs) => attrs, PerformanceAttributes::Taiko(attrs) => attrs,
_ => unreachable!(), _ => unreachable!(),
}; };
println!( println!(
"difficulty:\n\ "difficulty:\n\
stars={}\n\ stamina={}\n\
rhythm={}\n\
colour={}\n\
peak={}\n\
hit_window={}\n\
max_combo={}\n\ max_combo={}\n\
stars={}\n\
performance:\n\ performance:\n\
difficulty={}\n\ difficulty={}\n\
acc={}\n\
effective={}\n\
pp={}\n", pp={}\n",
attrs.difficulty.stars, attrs.difficulty.max_combo, attrs.pp_difficulty, attrs.pp, attrs.difficulty.stamina,
attrs.difficulty.rhythm,
attrs.difficulty.colour,
attrs.difficulty.peak,
attrs.difficulty.hit_window,
attrs.difficulty.max_combo,
attrs.difficulty.stars,
attrs.pp_difficulty,
attrs.pp_acc,
attrs.effective_miss_count,
attrs.pp,
); );
} }
} }
+11 -13
View File
@@ -600,19 +600,17 @@ macro_rules! parse_hitobjects_body {
None => None, None => None,
}; };
// Note: Edge sets are currently not considered, seems to be fine though. let mut edge_sounds = vec![sound; repeats + 2];
let edge_sounds_opt = split.next().map(|sounds| {
sounds
.split('|')
.take(repeats + 2)
.map(parse_custom_sound)
.collect()
});
let edge_sounds = match edge_sounds_opt { split
None => Vec::new(), .next()
Some(sounds) => sounds, .map(|sounds| sounds.split('|').map(parse_custom_sound))
}; .into_iter()
.flatten()
.zip(edge_sounds.iter_mut())
.for_each(|(parsed, sound)| *sound = parsed);
// Note: Edge sets are currently not considered, seems to be fine though.
match has_custom_sound_file(split.nth(1)) { match has_custom_sound_file(split.nth(1)) {
Status::Ok(false) => {} Status::Ok(false) => {}
@@ -686,7 +684,7 @@ macro_rules! parse_hitobjects_body {
GameMode::Osu | GameMode::Taiko | GameMode::Catch if !unsorted => {} GameMode::Osu | GameMode::Taiko | GameMode::Catch if !unsorted => {}
GameMode::Osu | GameMode::Taiko => { GameMode::Osu | GameMode::Taiko => {
// Sort both hitobjects and hitsounds // Sort both hitobjects and hitsounds
let mut sorter = TandemSorter::new(&$self.hit_objects); let mut sorter = TandemSorter::new(&$self.hit_objects, false);
sorter.sort(&mut $self.hit_objects); sorter.sort(&mut $self.hit_objects);
sorter.toggle_marks(); sorter.toggle_marks();
sorter.sort(&mut $self.sounds); sorter.sort(&mut $self.sounds);
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::TaikoDifficultyObject;
use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns}; use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns};
pub(crate) struct AlternatingMonoPattern<'o> { pub(crate) struct AlternatingMonoPattern {
pub(crate) mono_streaks: Vec<Rc<RefCell<MonoStreak<'o>>>>, pub(crate) mono_streaks: Vec<Rc<RefCell<MonoStreak>>>,
pub(crate) parent: Option<Weak<RefCell<RepeatingHitPatterns<'o>>>>, pub(crate) parent: Option<Weak<RefCell<RepeatingHitPatterns>>>,
pub(crate) idx: usize, pub(crate) idx: usize,
} }
impl Debug for AlternatingMonoPattern<'_> { impl Debug for AlternatingMonoPattern {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!( write!(
f, f,
@@ -26,7 +26,7 @@ impl Debug for AlternatingMonoPattern<'_> {
} }
} }
impl<'o> AlternatingMonoPattern<'o> { impl AlternatingMonoPattern {
pub(crate) fn new() -> Rc<RefCell<Self>> { pub(crate) fn new() -> Rc<RefCell<Self>> {
let this = Self { let this = Self {
mono_streaks: Vec::new(), mono_streaks: Vec::new(),
@@ -37,7 +37,7 @@ impl<'o> AlternatingMonoPattern<'o> {
Rc::new(RefCell::new(this)) Rc::new(RefCell::new(this))
} }
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject<'o>>>> { pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject>>> {
self.mono_streaks self.mono_streaks
.first() .first()
.and_then(|streak| streak.borrow().first_hit_object()) .and_then(|streak| streak.borrow().first_hit_object())
+4 -4
View File
@@ -14,10 +14,10 @@ mod preprocessor;
mod repeating_hit_patterns; mod repeating_hit_patterns;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub(crate) struct TaikoDifficultyColour<'o> { pub(crate) struct TaikoDifficultyColour {
pub(crate) mono_streak: Option<Weak<RefCell<MonoStreak<'o>>>>, pub(crate) mono_streak: Option<Weak<RefCell<MonoStreak>>>,
pub(crate) alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern<'o>>>>, pub(crate) alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern>>>,
pub(crate) repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns<'o>>>>, pub(crate) repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns>>>,
} }
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq)]
+6 -6
View File
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::{MonoIndex, TaikoDifficultyObject};
use super::{alternating_mono_pattern::AlternatingMonoPattern, HitKind}; use super::{alternating_mono_pattern::AlternatingMonoPattern, HitKind};
pub(crate) struct MonoStreak<'o> { pub(crate) struct MonoStreak {
pub(crate) hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject<'o>>>>, pub(crate) hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject>>>,
pub(crate) parent: Option<Weak<RefCell<AlternatingMonoPattern<'o>>>>, pub(crate) parent: Option<Weak<RefCell<AlternatingMonoPattern>>>,
pub(crate) idx: usize, pub(crate) idx: usize,
} }
impl Debug for MonoStreak<'_> { impl Debug for MonoStreak {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!( write!(
f, f,
@@ -26,7 +26,7 @@ impl Debug for MonoStreak<'_> {
} }
} }
impl<'o> MonoStreak<'o> { impl MonoStreak {
pub(crate) fn new() -> Rc<RefCell<Self>> { pub(crate) fn new() -> Rc<RefCell<Self>> {
let this = Self { let this = Self {
hit_objects: Vec::new(), hit_objects: Vec::new(),
@@ -37,7 +37,7 @@ impl<'o> MonoStreak<'o> {
Rc::new(RefCell::new(this)) Rc::new(RefCell::new(this))
} }
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject<'o>>>> { pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject>>> {
self.hit_objects.first().map(Weak::clone) self.hit_objects.first().map(Weak::clone)
} }
+12 -12
View File
@@ -11,10 +11,10 @@ use super::{
repeating_hit_patterns::RepeatingHitPatterns, repeating_hit_patterns::RepeatingHitPatterns,
}; };
pub(crate) struct ColourDifficultyPreprocessor {} pub(crate) struct ColourDifficultyPreprocessor;
impl<'o> ColourDifficultyPreprocessor { impl ColourDifficultyPreprocessor {
pub(crate) fn process_and_assign(lists: &mut ObjectLists<'o>) { pub(crate) fn process_and_assign(lists: &mut ObjectLists) {
// * Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is // * Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is
// * assigned with the relevant encodings. // * assigned with the relevant encodings.
for repeating_hit_pattern in Self::encode(lists) { for repeating_hit_pattern in Self::encode(lists) {
@@ -78,14 +78,14 @@ impl<'o> ColourDifficultyPreprocessor {
} }
} }
fn encode(data: &mut ObjectLists<'o>) -> Vec<Rc<RefCell<RepeatingHitPatterns<'o>>>> { fn encode(data: &mut ObjectLists) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
let mono_streaks = Self::encode_mono_streak(data); let mono_streaks = Self::encode_mono_streak(data);
let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks); let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks);
Self::encode_repeating_hit_pattern(alternating_mono_patterns) Self::encode_repeating_hit_pattern(alternating_mono_patterns)
} }
fn encode_mono_streak(data: &mut ObjectLists<'o>) -> Vec<Rc<RefCell<MonoStreak<'o>>>> { fn encode_mono_streak(data: &mut ObjectLists) -> Vec<Rc<RefCell<MonoStreak>>> {
let mut mono_streaks = vec![MonoStreak::new()]; let mut mono_streaks = vec![MonoStreak::new()];
let mut curr_mono_streak = mono_streaks.last_mut(); let mut curr_mono_streak = mono_streaks.last_mut();
let mut data_iter = data.all.iter(); let mut data_iter = data.all.iter();
@@ -100,9 +100,9 @@ impl<'o> ColourDifficultyPreprocessor {
// * If this is the first object in the list or the colour changed, create a new mono streak // * If this is the first object in the list or the colour changed, create a new mono streak
let condition = prev.filter(|prev| { let condition = prev.filter(|prev| {
!(taiko_obj.borrow().base.is_hit() !(taiko_obj.borrow().base.is_hit
&& prev.borrow().base.is_hit() && prev.borrow().base.is_hit
&& (taiko_obj.borrow().base.is_rim() != prev.borrow().base.is_rim())) && (taiko_obj.borrow().base.is_rim != prev.borrow().base.is_rim))
}); });
if condition.is_none() { if condition.is_none() {
@@ -120,8 +120,8 @@ impl<'o> ColourDifficultyPreprocessor {
} }
fn encode_alternating_mono_pattern( fn encode_alternating_mono_pattern(
data: Vec<Rc<RefCell<MonoStreak<'o>>>>, data: Vec<Rc<RefCell<MonoStreak>>>,
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern<'o>>>> { ) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> {
let mut mono_patterns = VecDeque::new(); let mut mono_patterns = VecDeque::new();
mono_patterns.push_back(AlternatingMonoPattern::new()); mono_patterns.push_back(AlternatingMonoPattern::new());
let mut curr_mono_pattern = mono_patterns.back_mut(); let mut curr_mono_pattern = mono_patterns.back_mut();
@@ -151,8 +151,8 @@ impl<'o> ColourDifficultyPreprocessor {
} }
fn encode_repeating_hit_pattern( fn encode_repeating_hit_pattern(
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern<'o>>>>, mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>,
) -> Vec<Rc<RefCell<RepeatingHitPatterns<'o>>>> { ) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
let mut hit_patterns = Vec::new(); let mut hit_patterns = Vec::new();
let mut curr_hit_pattern: Option<Rc<std::cell::RefCell<_>>> = None; let mut curr_hit_pattern: Option<Rc<std::cell::RefCell<_>>> = None;
+5 -5
View File
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::TaikoDifficultyObject;
use super::alternating_mono_pattern::AlternatingMonoPattern; use super::alternating_mono_pattern::AlternatingMonoPattern;
pub(crate) struct RepeatingHitPatterns<'o> { pub(crate) struct RepeatingHitPatterns {
pub(crate) alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern<'o>>>>, pub(crate) alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern>>>,
pub(crate) prev: Option<Weak<RefCell<Self>>>, pub(crate) prev: Option<Weak<RefCell<Self>>>,
pub(crate) repetition_interval: usize, pub(crate) repetition_interval: usize,
} }
impl Debug for RepeatingHitPatterns<'_> { impl Debug for RepeatingHitPatterns {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!( write!(
f, f,
@@ -26,7 +26,7 @@ impl Debug for RepeatingHitPatterns<'_> {
} }
} }
impl<'o> RepeatingHitPatterns<'o> { impl RepeatingHitPatterns {
const MAX_REPETITION_INTERVAL: usize = 16; const MAX_REPETITION_INTERVAL: usize = 16;
pub(crate) fn new(prev: Option<Weak<RefCell<Self>>>) -> Rc<RefCell<Self>> { pub(crate) fn new(prev: Option<Weak<RefCell<Self>>>) -> Rc<RefCell<Self>> {
@@ -39,7 +39,7 @@ impl<'o> RepeatingHitPatterns<'o> {
Rc::new(RefCell::new(this)) Rc::new(RefCell::new(this))
} }
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject<'o>>>> { pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject>>> {
self.alternating_mono_patterns self.alternating_mono_patterns
.first() .first()
.and_then(|pattern| pattern.borrow().first_hit_object()) .and_then(|pattern| pattern.borrow().first_hit_object())
+102 -101
View File
@@ -1,135 +1,55 @@
use std::{cell::RefCell, cmp::Ordering, rc::Rc}; use std::{cell::RefCell, cmp::Ordering, rc::Rc};
use crate::parse::HitObject; use super::{colours::TaikoDifficultyColour, taiko_object::TaikoObject};
use super::{colours::TaikoDifficultyColour, rim::Rim, taiko_object::TaikoObject};
#[derive(Clone, Debug, Default)]
pub(crate) struct ObjectLists<'o> {
pub(crate) all: Vec<Rc<RefCell<TaikoDifficultyObject<'o>>>>,
pub(crate) centres: Vec<usize>,
pub(crate) rims: Vec<usize>,
pub(crate) notes: Vec<usize>,
}
impl<'o> ObjectLists<'o> {
pub(crate) fn prev_mono(
&self,
curr: usize,
backwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
let curr = &self.all[curr];
let prev = match curr.borrow().mono_idx {
MonoIndex::Centre(idx) => idx
.checked_sub(backwards_idx + 1)
.and_then(|idx| self.centres.get(idx))?,
MonoIndex::Rim(idx) => idx
.checked_sub(backwards_idx + 1)
.and_then(|idx| self.rims.get(idx))?,
MonoIndex::None => return None,
};
self.all.get(*prev)
}
#[allow(unused)]
pub(crate) fn next_mono(
&self,
curr: usize,
forwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
let curr = &self.all[curr];
let next = match curr.borrow().mono_idx {
MonoIndex::Centre(idx) => self.centres.get(idx + (forwards_idx + 1))?,
MonoIndex::Rim(idx) => self.rims.get(idx + (forwards_idx + 1))?,
MonoIndex::None => return None,
};
self.all.get(*next)
}
pub(crate) fn prev_note(
&self,
curr: usize,
backwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
let curr = &self.all[curr];
let note_idx = curr.borrow().note_idx?;
let idx = note_idx.checked_sub(backwards_idx + 1)?;
let prev = self.notes.get(idx)?;
self.all.get(*prev)
}
#[allow(unused)]
pub(crate) fn next_note(
&self,
curr: usize,
forwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
let curr = &self.all[curr];
let note_idx = curr.borrow().note_idx?;
let idx = note_idx + (forwards_idx + 1);
let prev = self.notes.get(idx)?;
self.all.get(*prev)
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum MonoIndex {
Centre(usize),
Rim(usize),
None,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct TaikoDifficultyObject<'o> { pub(crate) struct TaikoDifficultyObject {
pub(crate) base: TaikoObject<'o>, pub(crate) base: TaikoObject,
pub(crate) prev_time: f64, pub(crate) prev_time: f64,
pub(crate) colour: TaikoDifficultyColour<'o>, pub(crate) colour: TaikoDifficultyColour,
pub(crate) rhythm: &'static HitObjectRhythm, pub(crate) rhythm: &'static HitObjectRhythm,
pub(crate) mono_idx: MonoIndex, pub(crate) mono_idx: MonoIndex,
pub(crate) note_idx: Option<usize>, pub(crate) note_idx: Option<usize>,
pub(crate) idx: usize, pub(crate) idx: usize,
pub(crate) start_time: f64,
pub(crate) delta: f64, pub(crate) delta: f64,
} }
impl<'o> TaikoDifficultyObject<'o> { impl TaikoDifficultyObject {
pub(crate) fn new( pub(crate) fn new(
base: TaikoObject<'o>, base: TaikoObject,
last: TaikoObject<'o>, base_start_time: f64,
last_last: TaikoObject<'o>, last_start_time: f64,
last_last_start_time: f64,
clock_rate: f64, clock_rate: f64,
lists: &ObjectLists<'o>, lists: &ObjectLists,
idx: usize, idx: usize,
) -> Self { ) -> Self {
// * Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor // * Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor
let colour = TaikoDifficultyColour::default(); let colour = TaikoDifficultyColour::default();
let delta = (base.h.start_time - last.h.start_time) / clock_rate; let delta = (base_start_time - last_start_time) / clock_rate;
let rhythm = closest_rhythm(delta, last.h, last_last.h, clock_rate); let rhythm = closest_rhythm(delta, last_start_time, last_last_start_time, clock_rate);
let mono_idx = if !base.is_hit() { let mono_idx = if !base.is_hit {
MonoIndex::None MonoIndex::None
} else if base.sound.is_rim() { } else if base.is_rim {
MonoIndex::Rim(lists.rims.len()) MonoIndex::Rim(lists.rims.len())
} else { } else {
MonoIndex::Centre(lists.centres.len()) MonoIndex::Centre(lists.centres.len())
}; };
let note_idx = base.is_hit().then_some(lists.notes.len()); let note_idx = base.is_hit.then_some(lists.notes.len());
Self { Self {
base, base,
prev_time: last.h.start_time / clock_rate, prev_time: last_start_time / clock_rate,
colour, colour,
rhythm, rhythm,
mono_idx, mono_idx,
note_idx, note_idx,
idx, idx,
start_time: base_start_time / clock_rate,
delta, delta,
} }
} }
@@ -173,11 +93,11 @@ impl Eq for HitObjectRhythm {}
fn closest_rhythm( fn closest_rhythm(
delta_time: f64, delta_time: f64,
last: &HitObject, last_start_time: f64,
last_last: &HitObject, last_last_start_time: f64,
clock_rate: f64, clock_rate: f64,
) -> &'static HitObjectRhythm { ) -> &'static HitObjectRhythm {
let prev_len = (last.start_time - last_last.start_time) / clock_rate; let prev_len = (last_start_time - last_last_start_time) / clock_rate;
let ratio = delta_time / prev_len; let ratio = delta_time / prev_len;
COMMON_RHYTHMS COMMON_RHYTHMS
@@ -190,3 +110,84 @@ fn closest_rhythm(
}) })
.unwrap() .unwrap()
} }
#[derive(Clone, Debug, Default)]
pub(crate) struct ObjectLists {
pub(crate) all: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
pub(crate) centres: Vec<usize>,
pub(crate) rims: Vec<usize>,
pub(crate) notes: Vec<usize>,
}
impl ObjectLists {
pub(crate) fn prev_mono(
&self,
curr: usize,
backwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
let curr = &self.all[curr];
let prev = match curr.borrow().mono_idx {
MonoIndex::Centre(idx) => idx
.checked_sub(backwards_idx + 1)
.and_then(|idx| self.centres.get(idx))?,
MonoIndex::Rim(idx) => idx
.checked_sub(backwards_idx + 1)
.and_then(|idx| self.rims.get(idx))?,
MonoIndex::None => return None,
};
self.all.get(*prev)
}
#[allow(unused)]
pub(crate) fn next_mono(
&self,
curr: usize,
forwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
let curr = &self.all[curr];
let next = match curr.borrow().mono_idx {
MonoIndex::Centre(idx) => self.centres.get(idx + (forwards_idx + 1))?,
MonoIndex::Rim(idx) => self.rims.get(idx + (forwards_idx + 1))?,
MonoIndex::None => return None,
};
self.all.get(*next)
}
pub(crate) fn prev_note(
&self,
curr: usize,
backwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
let curr = &self.all[curr];
let note_idx = curr.borrow().note_idx?;
let idx = note_idx.checked_sub(backwards_idx + 1)?;
let prev = self.notes.get(idx)?;
self.all.get(*prev)
}
#[allow(unused)]
pub(crate) fn next_note(
&self,
curr: usize,
forwards_idx: usize,
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
let curr = &self.all[curr];
let note_idx = curr.borrow().note_idx?;
let idx = note_idx + (forwards_idx + 1);
let prev = self.notes.get(idx)?;
self.all.get(*prev)
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum MonoIndex {
Centre(usize),
Rim(usize),
None,
}
+20 -13
View File
@@ -41,17 +41,17 @@ use super::{
/// } /// }
/// ``` /// ```
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TaikoGradualDifficultyAttributes<'map> { pub struct TaikoGradualDifficultyAttributes {
pub(crate) idx: usize, pub(crate) idx: usize,
attrs: TaikoDifficultyAttributes, attrs: TaikoDifficultyAttributes,
hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject<'map>>>>, hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject>>>,
lists: ObjectLists<'map>, lists: ObjectLists,
peaks: Peaks, peaks: Peaks,
} }
impl<'map> TaikoGradualDifficultyAttributes<'map> { impl TaikoGradualDifficultyAttributes {
/// Create a new difficulty attributes iterator for osu!taiko maps. /// Create a new difficulty attributes iterator for osu!taiko maps.
pub fn new(map: &'map Beatmap, mods: u32) -> Self { pub fn new(map: &Beatmap, mods: u32) -> Self {
let peaks = Peaks::new(); let peaks = Peaks::new();
let clock_rate = mods.clock_rate(); let clock_rate = mods.clock_rate();
@@ -75,13 +75,20 @@ impl<'map> TaikoGradualDifficultyAttributes<'map> {
.taiko_objects() .taiko_objects()
.enumerate() .enumerate()
.skip(2) .skip(2)
.zip(map.taiko_objects().skip(1)) .zip(map.hit_objects.iter().skip(1))
.zip(map.taiko_objects()) .zip(map.hit_objects.iter())
.fold( .fold(
ObjectLists::default(), ObjectLists::default(),
|mut lists, (((idx, base), last), last_last)| { |mut lists, (((idx, (base, base_start_time)), last), last_last)| {
let diff_obj = let diff_obj = TaikoDifficultyObject::new(
TaikoDifficultyObject::new(base, last, last_last, clock_rate, &lists, idx); base,
base_start_time,
last.start_time,
last_last.start_time,
clock_rate,
&lists,
idx,
);
match &diff_obj.mono_idx { match &diff_obj.mono_idx {
MonoIndex::Centre(_) => lists.centres.push(idx), MonoIndex::Centre(_) => lists.centres.push(idx),
@@ -111,7 +118,7 @@ impl<'map> TaikoGradualDifficultyAttributes<'map> {
} }
} }
impl Iterator for TaikoGradualDifficultyAttributes<'_> { impl Iterator for TaikoGradualDifficultyAttributes {
type Item = TaikoDifficultyAttributes; type Item = TaikoDifficultyAttributes;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@@ -120,7 +127,7 @@ impl Iterator for TaikoGradualDifficultyAttributes<'_> {
{ {
let curr = curr.borrow(); let curr = curr.borrow();
self.peaks.process(&curr, &self.lists); self.peaks.process(&curr, &self.lists);
self.attrs.max_combo += curr.base.h.is_circle() as usize; self.attrs.max_combo += curr.base.is_hit as usize;
} }
let PeaksDifficultyValues { let PeaksDifficultyValues {
@@ -169,7 +176,7 @@ impl Iterator for TaikoGradualDifficultyAttributes<'_> {
} }
} }
impl ExactSizeIterator for TaikoGradualDifficultyAttributes<'_> { impl ExactSizeIterator for TaikoGradualDifficultyAttributes {
#[inline] #[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
self.hit_objects.len() self.hit_objects.len()
+1 -1
View File
@@ -134,7 +134,7 @@ impl TaikoScoreState {
/// ``` /// ```
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TaikoGradualPerformanceAttributes<'map> { pub struct TaikoGradualPerformanceAttributes<'map> {
difficulty: TaikoGradualDifficultyAttributes<'map>, difficulty: TaikoGradualDifficultyAttributes,
performance: TaikoPP<'map>, performance: TaikoPP<'map>,
} }
+14 -7
View File
@@ -232,15 +232,22 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
.taiko_objects() .taiko_objects()
.take(take) .take(take)
.skip(2) .skip(2)
.zip(map.taiko_objects().skip(1)) .zip(map.hit_objects.iter().skip(1))
.zip(map.taiko_objects()) .zip(map.hit_objects.iter())
.enumerate() .enumerate()
.inspect(|(_, ((base, _), _))| max_combo += base.h.is_circle() as usize) .inspect(|(_, (((base, _), _), _))| max_combo += base.is_hit as usize)
.fold( .fold(
ObjectLists::default(), ObjectLists::default(),
|mut lists, (idx, ((base, last), last_last))| { |mut lists, (idx, (((base, base_start_time), last), last_last))| {
let diff_obj = let diff_obj = TaikoDifficultyObject::new(
TaikoDifficultyObject::new(base, last, last_last, clock_rate, &lists, idx); base,
base_start_time,
last.start_time,
last_last.start_time,
clock_rate,
&lists,
idx,
);
match &diff_obj.mono_idx { match &diff_obj.mono_idx {
MonoIndex::Centre(_) => lists.centres.push(idx), MonoIndex::Centre(_) => lists.centres.push(idx),
@@ -272,7 +279,7 @@ fn rescale(stars: f64) -> f64 {
if stars < 0.0 { if stars < 0.0 {
stars stars
} else { } else {
10.43 * (stars / 8.0).ln_1p() 10.43 * (stars / 8.0 + 1.0).ln()
} }
} }
+8 -12
View File
@@ -30,7 +30,7 @@ impl Colour {
} }
impl Skill for Colour { impl Skill for Colour {
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) { fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
<Self as StrainSkill>::process(self, curr, hit_objects) <Self as StrainSkill>::process(self, curr, hit_objects)
} }
@@ -52,15 +52,11 @@ impl StrainSkill for Colour {
&mut self.curr_section_end &mut self.curr_section_end
} }
fn strain_value_at( fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
<Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects) <Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects)
} }
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 { fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
<Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr) <Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr)
} }
} }
@@ -77,7 +73,7 @@ impl StrainDecaySkill for Colour {
&mut self.curr_strain &mut self.curr_strain
} }
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject<'_>, _: &ObjectLists<'_>) -> f64 { fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, _: &ObjectLists) -> f64 {
ColourEvaluator::evaluate_diff_of(curr) ColourEvaluator::evaluate_diff_of(curr)
} }
} }
@@ -91,7 +87,7 @@ impl ColourEvaluator {
sigmoid * (height / 2.0) + middle sigmoid * (height / 2.0) + middle
} }
fn evaluate_diff_of_mono_streak(mono_streak: Rc<RefCell<MonoStreak<'_>>>) -> f64 { fn evaluate_diff_of_mono_streak(mono_streak: Rc<RefCell<MonoStreak>>) -> f64 {
let mono_streak = mono_streak.borrow(); let mono_streak = mono_streak.borrow();
let parent_eval = mono_streak let parent_eval = mono_streak
@@ -104,7 +100,7 @@ impl ColourEvaluator {
} }
fn evaluate_diff_of_alternating_mono_pattern( fn evaluate_diff_of_alternating_mono_pattern(
alternating_mono_pattern: Rc<RefCell<AlternatingMonoPattern<'_>>>, alternating_mono_pattern: Rc<RefCell<AlternatingMonoPattern>>,
) -> f64 { ) -> f64 {
let alternating_mono_pattern = alternating_mono_pattern.borrow(); let alternating_mono_pattern = alternating_mono_pattern.borrow();
@@ -118,14 +114,14 @@ impl ColourEvaluator {
} }
fn evaluate_diff_of_repeating_hit_patterns( fn evaluate_diff_of_repeating_hit_patterns(
repeating_hit_patterns: Rc<RefCell<RepeatingHitPatterns<'_>>>, repeating_hit_patterns: Rc<RefCell<RepeatingHitPatterns>>,
) -> f64 { ) -> f64 {
let repetition_interval = repeating_hit_patterns.borrow().repetition_interval as f64; let repetition_interval = repeating_hit_patterns.borrow().repetition_interval as f64;
2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0)) 2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0))
} }
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject<'_>) -> f64 { fn evaluate_diff_of(hit_object: &TaikoDifficultyObject) -> f64 {
let colour = &hit_object.colour; let colour = &hit_object.colour;
let mut difficulty = 0.0; let mut difficulty = 0.0;
+10 -22
View File
@@ -13,7 +13,7 @@ use super::{
}; };
pub(crate) trait Skill: Sized { pub(crate) trait Skill: Sized {
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>); fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists);
fn difficulty_value(self) -> f64; fn difficulty_value(self) -> f64;
} }
@@ -24,22 +24,18 @@ pub(crate) trait StrainSkill: Skill {
fn curr_section_peak(&mut self) -> &mut f64; fn curr_section_peak(&mut self) -> &mut f64;
fn curr_section_end(&mut self) -> &mut f64; fn curr_section_end(&mut self) -> &mut f64;
fn strain_value_at( fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64;
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64;
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64; fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64;
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) { fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
// * The first object doesn't generate a strain, so we begin with an incremented section end // * The first object doesn't generate a strain, so we begin with an incremented section end
if curr.idx == 0 { if curr.idx == 0 {
let section_len = SECTION_LEN as f64; let section_len = SECTION_LEN as f64;
*self.curr_section_end() = (curr.base.h.start_time / section_len).ceil() * section_len; *self.curr_section_end() = (curr.start_time / section_len).ceil() * section_len;
} }
while curr.base.h.start_time > *self.curr_section_end() { while curr.start_time > *self.curr_section_end() {
self.save_curr_peak(); self.save_curr_peak();
{ {
@@ -60,7 +56,7 @@ pub(crate) trait StrainSkill: Skill {
self.strain_peaks_mut().push(peak); self.strain_peaks_mut().push(peak);
} }
fn start_new_section_from(&mut self, time: f64, curr: &TaikoDifficultyObject<'_>) { fn start_new_section_from(&mut self, time: f64, curr: &TaikoDifficultyObject) {
// * The maximum strain of the new section is not zero by default // * The maximum strain of the new section is not zero by default
// * This means we need to capture the strain level at the beginning of the new section, // * This means we need to capture the strain level at the beginning of the new section,
// * and use that as the initial peak level. // * and use that as the initial peak level.
@@ -104,21 +100,13 @@ pub(crate) trait StrainDecaySkill: StrainSkill {
fn curr_strain(&self) -> f64; fn curr_strain(&self) -> f64;
fn curr_strain_mut(&mut self) -> &mut f64; fn curr_strain_mut(&mut self) -> &mut f64;
fn strain_value_of( fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64;
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64;
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 { fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
self.curr_strain() * self.strain_decay(time - curr.prev_time) self.curr_strain() * self.strain_decay(time - curr.prev_time)
} }
fn strain_value_at( fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
*self.curr_strain_mut() *= self.strain_decay(curr.delta); *self.curr_strain_mut() *= self.strain_decay(curr.delta);
*self.curr_strain_mut() += self.strain_value_of(curr, hit_objects) * Self::SKILL_MULTIPLIER; *self.curr_strain_mut() += self.strain_value_of(curr, hit_objects) * Self::SKILL_MULTIPLIER;
+12 -13
View File
@@ -28,18 +28,17 @@ impl Peaks {
pub(crate) fn difficulty_values(self) -> PeaksDifficultyValues { pub(crate) fn difficulty_values(self) -> PeaksDifficultyValues {
let colour_rating = let colour_rating =
StrainSkill::difficulty_value(self.colour) * Self::COLOUR_SKILL_MULTIPLIER; StrainSkill::difficulty_value(self.colour.clone()) * Self::COLOUR_SKILL_MULTIPLIER;
// let rhythm_rating = let rhythm_rating =
// StrainSkill::difficulty_value(self.rhythm.clone()) * Self::RHYTHM_SKILL_MULTIPLIER; StrainSkill::difficulty_value(self.rhythm.clone()) * Self::RHYTHM_SKILL_MULTIPLIER;
// let stamina_rating = let stamina_rating =
// StrainSkill::difficulty_value(self.stamina.clone()) * Self::STAMINA_SKILL_MULTIPLIER; StrainSkill::difficulty_value(self.stamina.clone()) * Self::STAMINA_SKILL_MULTIPLIER;
PeaksDifficultyValues { PeaksDifficultyValues {
colour_rating, colour_rating,
rhythm_rating: 0.0, rhythm_rating,
stamina_rating: 0.0, stamina_rating,
// combined_rating: self.difficulty_value(), combined_rating: self.difficulty_value(),
combined_rating: 0.0,
} }
} }
@@ -60,10 +59,10 @@ impl Peaks {
} }
impl Skill for Peaks { impl Skill for Peaks {
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) { fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
StrainSkill::process(&mut self.colour, curr, hit_objects); <Colour as Skill>::process(&mut self.colour, curr, hit_objects);
StrainSkill::process(&mut self.rhythm, curr, hit_objects); <Rhythm as Skill>::process(&mut self.rhythm, curr, hit_objects);
StrainSkill::process(&mut self.stamina, curr, hit_objects); <Stamina as Skill>::process(&mut self.stamina, curr, hit_objects);
} }
fn difficulty_value(self) -> f64 { fn difficulty_value(self) -> f64 {
+7 -11
View File
@@ -40,7 +40,7 @@ impl Rhythm {
self.notes_since_rhythm_change = 0; self.notes_since_rhythm_change = 0;
} }
fn repetition_penalties(&mut self, hit_object: &TaikoDifficultyObject<'_>) -> f64 { fn repetition_penalties(&mut self, hit_object: &TaikoDifficultyObject) -> f64 {
let mut penalty = 1.0; let mut penalty = 1.0;
self.history.push(HistoryElement::new(hit_object)); self.history.push(HistoryElement::new(hit_object));
@@ -100,7 +100,7 @@ impl Rhythm {
} }
impl Skill for Rhythm { impl Skill for Rhythm {
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) { fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
<Self as StrainSkill>::process(self, curr, hit_objects) <Self as StrainSkill>::process(self, curr, hit_objects)
} }
@@ -122,15 +122,11 @@ impl StrainSkill for Rhythm {
&mut self.curr_section_end &mut self.curr_section_end
} }
fn strain_value_at( fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
<Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects) <Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects)
} }
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 { fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
<Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr) <Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr)
} }
} }
@@ -147,8 +143,8 @@ impl StrainDecaySkill for Rhythm {
&mut self.curr_decay_strain &mut self.curr_decay_strain
} }
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject<'_>, _: &ObjectLists<'_>) -> f64 { fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, _: &ObjectLists) -> f64 {
let base_is_circle = curr.base.h.is_circle(); let base_is_circle = curr.base.is_hit;
// * drum rolls and swells are exempt. // * drum rolls and swells are exempt.
if !base_is_circle { if !base_is_circle {
@@ -187,7 +183,7 @@ pub(crate) struct HistoryElement {
} }
impl HistoryElement { impl HistoryElement {
fn new(difficulty_object: &TaikoDifficultyObject<'_>) -> Self { fn new(difficulty_object: &TaikoDifficultyObject) -> Self {
Self { Self {
idx: difficulty_object.idx, idx: difficulty_object.idx,
rhythm: difficulty_object.rhythm, rhythm: difficulty_object.rhythm,
+7 -18
View File
@@ -22,7 +22,7 @@ impl Stamina {
} }
impl Skill for Stamina { impl Skill for Stamina {
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) { fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
<Self as StrainSkill>::process(self, curr, hit_objects) <Self as StrainSkill>::process(self, curr, hit_objects)
} }
@@ -44,15 +44,11 @@ impl StrainSkill for Stamina {
&mut self.curr_section_end &mut self.curr_section_end
} }
fn strain_value_at( fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
<Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects) <Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects)
} }
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 { fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
<Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr) <Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr)
} }
} }
@@ -69,11 +65,7 @@ impl StrainDecaySkill for Stamina {
&mut self.curr_strain &mut self.curr_strain
} }
fn strain_value_of( fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
StaminaEvaluator::evaluate_diff_of(curr, hit_objects) StaminaEvaluator::evaluate_diff_of(curr, hit_objects)
} }
} }
@@ -90,11 +82,8 @@ impl StaminaEvaluator {
30.0 / interval 30.0 / interval
} }
fn evaluate_diff_of( fn evaluate_diff_of(hit_object: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
hit_object: &TaikoDifficultyObject<'_>, if !hit_object.base.is_hit {
hit_objects: &ObjectLists<'_>,
) -> f64 {
if !hit_object.base.is_hit() {
return 0.0; return 0.0;
} }
@@ -104,7 +93,7 @@ impl StaminaEvaluator {
if let Some(key_prev) = key_prev { if let Some(key_prev) = key_prev {
// * Add a base strain to all objects // * Add a base strain to all objects
0.5 + Self::speed_bonus(curr.base.h.start_time - key_prev.borrow().base.h.start_time) 0.5 + Self::speed_bonus(curr.start_time - key_prev.borrow().start_time)
} else { } else {
// * There is no previous hit object hit by the current key // * There is no previous hit object hit by the current key
0.0 0.0
+14 -15
View File
@@ -5,18 +5,17 @@ use crate::{parse::HitObject, Beatmap};
use super::rim::Rim; use super::rim::Rim;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub(crate) struct TaikoObject<'h> { pub(crate) struct TaikoObject {
pub(crate) h: &'h HitObject, pub(crate) is_hit: bool,
pub(crate) sound: u8, pub(crate) is_rim: bool,
} }
impl TaikoObject<'_> { impl TaikoObject {
pub(crate) fn is_rim(&self) -> bool { pub(crate) fn new(h: &HitObject, sound: u8) -> Self {
self.sound.is_rim() Self {
} is_hit: h.is_circle(),
is_rim: sound.is_rim(),
pub(crate) fn is_hit(&self) -> bool { }
self.h.is_circle()
} }
} }
@@ -41,14 +40,14 @@ impl IntoTaikoObjectIter for Beatmap {
} }
impl<'m> Iterator for TaikoObjectIter<'m> { impl<'m> Iterator for TaikoObjectIter<'m> {
type Item = TaikoObject<'m>; type Item = (TaikoObject, f64);
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
Some(TaikoObject { let h = self.hit_objects.next()?;
h: self.hit_objects.next()?, let sound = self.sounds.next()?;
sound: *self.sounds.next()?,
}) Some((TaikoObject::new(h, *sound), h.start_time))
} }
#[inline] #[inline]
+10 -4
View File
@@ -9,14 +9,20 @@ pub(crate) struct TandemSorter {
impl TandemSorter { impl TandemSorter {
/// Sort indices based on the given slice. /// Sort indices based on the given slice.
/// Note that this does **not** sort the given slice. /// Note that this does **not** sort the given slice.
pub(crate) fn new<T>(slice: &[T]) -> Self pub(crate) fn new<T>(slice: &[T], stable: bool) -> Self
where where
T: PartialOrd, T: PartialOrd,
{ {
let mut indices: Vec<_> = (0..).take(slice.len()).collect(); let mut indices: Vec<_> = (0..).take(slice.len()).collect();
indices let closure =
.sort_unstable_by(|&i, &j| slice[i].partial_cmp(&slice[j]).unwrap_or(Ordering::Equal)); |&i: &usize, &j: &usize| slice[i].partial_cmp(&slice[j]).unwrap_or(Ordering::Equal);
if stable {
indices.sort_by(closure);
} else {
indices.sort_unstable_by(closure);
}
Self { indices } Self { indices }
} }
@@ -75,7 +81,7 @@ mod tests {
#[test] #[test]
fn sort() { fn sort() {
let mut base = vec![9, 7, 8, 1, 4, 3, 5, 2]; let mut base = vec![9, 7, 8, 1, 4, 3, 5, 2];
let mut sorter = TandemSorter::new(&base); let mut sorter = TandemSorter::new(&base, false);
sorter.sort(&mut base); sorter.sort(&mut base);
assert_eq!(base, vec![1, 2, 3, 4, 5, 7, 8, 9]); assert_eq!(base, vec![1, 2, 3, 4, 5, 7, 8, 9]);