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,
);
diff_range.floor() / clock_rate
diff_range / clock_rate
}
GameMode::Mania => {
let mut value = if !self.converted {
+5 -4
View File
@@ -1,8 +1,7 @@
use std::cmp::Ordering;
use crate::{
curve::{Curve, CurveBuffers},
parse::{HitObject, HitObjectKind},
util::TandemSorter,
Beatmap, GameMode,
};
@@ -87,8 +86,10 @@ impl Beatmap {
// We only convert STD to TKO so we don't need to remove objects
// with the same timestamp that would appear only in MNA
map.hit_objects
.sort_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal));
let mut sorter = TandemSorter::new(&map.hit_objects, true);
sorter.sort(&mut map.hit_objects);
sorter.toggle_marks();
sorter.sort(&mut map.sounds);
map.mode = GameMode::Taiko;
+1 -1
View File
@@ -46,7 +46,7 @@ pub enum GradualDifficultyAttributes<'map> {
/// Gradual osu!standard difficulty attributes.
Osu(OsuGradualDifficultyAttributes),
/// Gradual osu!taiko difficulty attributes.
Taiko(TaikoGradualDifficultyAttributes<'map>),
Taiko(TaikoGradualDifficultyAttributes),
}
impl<'map> GradualDifficultyAttributes<'map> {
+22 -5
View File
@@ -521,22 +521,39 @@ mod tests {
#[test]
fn custom() {
let path = "F:/osu!/beatmaps/34881.osu";
let path = "F:/osu!/beatmaps/1186086.osu";
let map = Beatmap::from_path(path).unwrap();
let attrs = match OsuPP::new(&map).mode(GameMode::Mania).mods(0).calculate() {
PerformanceAttributes::Mania(attrs) => attrs,
let attrs = match OsuPP::new(&map).mode(GameMode::Taiko).mods(0).calculate() {
PerformanceAttributes::Taiko(attrs) => attrs,
_ => unreachable!(),
};
println!(
"difficulty:\n\
stars={}\n\
stamina={}\n\
rhythm={}\n\
colour={}\n\
peak={}\n\
hit_window={}\n\
max_combo={}\n\
stars={}\n\
performance:\n\
difficulty={}\n\
acc={}\n\
effective={}\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,
};
// Note: Edge sets are currently not considered, seems to be fine though.
let edge_sounds_opt = split.next().map(|sounds| {
sounds
.split('|')
.take(repeats + 2)
.map(parse_custom_sound)
.collect()
});
let mut edge_sounds = vec![sound; repeats + 2];
let edge_sounds = match edge_sounds_opt {
None => Vec::new(),
Some(sounds) => sounds,
};
split
.next()
.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)) {
Status::Ok(false) => {}
@@ -686,7 +684,7 @@ macro_rules! parse_hitobjects_body {
GameMode::Osu | GameMode::Taiko | GameMode::Catch if !unsorted => {}
GameMode::Osu | GameMode::Taiko => {
// 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.toggle_marks();
sorter.sort(&mut $self.sounds);
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::TaikoDifficultyObject;
use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns};
pub(crate) struct AlternatingMonoPattern<'o> {
pub(crate) mono_streaks: Vec<Rc<RefCell<MonoStreak<'o>>>>,
pub(crate) parent: Option<Weak<RefCell<RepeatingHitPatterns<'o>>>>,
pub(crate) struct AlternatingMonoPattern {
pub(crate) mono_streaks: Vec<Rc<RefCell<MonoStreak>>>,
pub(crate) parent: Option<Weak<RefCell<RepeatingHitPatterns>>>,
pub(crate) idx: usize,
}
impl Debug for AlternatingMonoPattern<'_> {
impl Debug for AlternatingMonoPattern {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
@@ -26,7 +26,7 @@ impl Debug for AlternatingMonoPattern<'_> {
}
}
impl<'o> AlternatingMonoPattern<'o> {
impl AlternatingMonoPattern {
pub(crate) fn new() -> Rc<RefCell<Self>> {
let this = Self {
mono_streaks: Vec::new(),
@@ -37,7 +37,7 @@ impl<'o> AlternatingMonoPattern<'o> {
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
.first()
.and_then(|streak| streak.borrow().first_hit_object())
+4 -4
View File
@@ -14,10 +14,10 @@ mod preprocessor;
mod repeating_hit_patterns;
#[derive(Clone, Debug, Default)]
pub(crate) struct TaikoDifficultyColour<'o> {
pub(crate) mono_streak: Option<Weak<RefCell<MonoStreak<'o>>>>,
pub(crate) alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern<'o>>>>,
pub(crate) repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns<'o>>>>,
pub(crate) struct TaikoDifficultyColour {
pub(crate) mono_streak: Option<Weak<RefCell<MonoStreak>>>,
pub(crate) alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern>>>,
pub(crate) repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns>>>,
}
#[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};
pub(crate) struct MonoStreak<'o> {
pub(crate) hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject<'o>>>>,
pub(crate) parent: Option<Weak<RefCell<AlternatingMonoPattern<'o>>>>,
pub(crate) struct MonoStreak {
pub(crate) hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject>>>,
pub(crate) parent: Option<Weak<RefCell<AlternatingMonoPattern>>>,
pub(crate) idx: usize,
}
impl Debug for MonoStreak<'_> {
impl Debug for MonoStreak {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
@@ -26,7 +26,7 @@ impl Debug for MonoStreak<'_> {
}
}
impl<'o> MonoStreak<'o> {
impl MonoStreak {
pub(crate) fn new() -> Rc<RefCell<Self>> {
let this = Self {
hit_objects: Vec::new(),
@@ -37,7 +37,7 @@ impl<'o> MonoStreak<'o> {
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)
}
+12 -12
View File
@@ -11,10 +11,10 @@ use super::{
repeating_hit_patterns::RepeatingHitPatterns,
};
pub(crate) struct ColourDifficultyPreprocessor {}
pub(crate) struct ColourDifficultyPreprocessor;
impl<'o> ColourDifficultyPreprocessor {
pub(crate) fn process_and_assign(lists: &mut ObjectLists<'o>) {
impl ColourDifficultyPreprocessor {
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
// * assigned with the relevant encodings.
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 alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks);
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 curr_mono_streak = mono_streaks.last_mut();
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
let condition = prev.filter(|prev| {
!(taiko_obj.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_hit
&& prev.borrow().base.is_hit
&& (taiko_obj.borrow().base.is_rim != prev.borrow().base.is_rim))
});
if condition.is_none() {
@@ -120,8 +120,8 @@ impl<'o> ColourDifficultyPreprocessor {
}
fn encode_alternating_mono_pattern(
data: Vec<Rc<RefCell<MonoStreak<'o>>>>,
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern<'o>>>> {
data: Vec<Rc<RefCell<MonoStreak>>>,
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> {
let mut mono_patterns = VecDeque::new();
mono_patterns.push_back(AlternatingMonoPattern::new());
let mut curr_mono_pattern = mono_patterns.back_mut();
@@ -151,8 +151,8 @@ impl<'o> ColourDifficultyPreprocessor {
}
fn encode_repeating_hit_pattern(
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern<'o>>>>,
) -> Vec<Rc<RefCell<RepeatingHitPatterns<'o>>>> {
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>,
) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
let mut hit_patterns = Vec::new();
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;
pub(crate) struct RepeatingHitPatterns<'o> {
pub(crate) alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern<'o>>>>,
pub(crate) struct RepeatingHitPatterns {
pub(crate) alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern>>>,
pub(crate) prev: Option<Weak<RefCell<Self>>>,
pub(crate) repetition_interval: usize,
}
impl Debug for RepeatingHitPatterns<'_> {
impl Debug for RepeatingHitPatterns {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
@@ -26,7 +26,7 @@ impl Debug for RepeatingHitPatterns<'_> {
}
}
impl<'o> RepeatingHitPatterns<'o> {
impl RepeatingHitPatterns {
const MAX_REPETITION_INTERVAL: usize = 16;
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))
}
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
.first()
.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 crate::parse::HitObject;
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,
}
use super::{colours::TaikoDifficultyColour, taiko_object::TaikoObject};
#[derive(Clone, Debug)]
pub(crate) struct TaikoDifficultyObject<'o> {
pub(crate) base: TaikoObject<'o>,
pub(crate) struct TaikoDifficultyObject {
pub(crate) base: TaikoObject,
pub(crate) prev_time: f64,
pub(crate) colour: TaikoDifficultyColour<'o>,
pub(crate) colour: TaikoDifficultyColour,
pub(crate) rhythm: &'static HitObjectRhythm,
pub(crate) mono_idx: MonoIndex,
pub(crate) note_idx: Option<usize>,
pub(crate) idx: usize,
pub(crate) start_time: f64,
pub(crate) delta: f64,
}
impl<'o> TaikoDifficultyObject<'o> {
impl TaikoDifficultyObject {
pub(crate) fn new(
base: TaikoObject<'o>,
last: TaikoObject<'o>,
last_last: TaikoObject<'o>,
base: TaikoObject,
base_start_time: f64,
last_start_time: f64,
last_last_start_time: f64,
clock_rate: f64,
lists: &ObjectLists<'o>,
lists: &ObjectLists,
idx: usize,
) -> Self {
// * Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor
let colour = TaikoDifficultyColour::default();
let delta = (base.h.start_time - last.h.start_time) / clock_rate;
let rhythm = closest_rhythm(delta, last.h, last_last.h, clock_rate);
let delta = (base_start_time - last_start_time) / 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
} else if base.sound.is_rim() {
} else if base.is_rim {
MonoIndex::Rim(lists.rims.len())
} else {
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 {
base,
prev_time: last.h.start_time / clock_rate,
prev_time: last_start_time / clock_rate,
colour,
rhythm,
mono_idx,
note_idx,
idx,
start_time: base_start_time / clock_rate,
delta,
}
}
@@ -173,11 +93,11 @@ impl Eq for HitObjectRhythm {}
fn closest_rhythm(
delta_time: f64,
last: &HitObject,
last_last: &HitObject,
last_start_time: f64,
last_last_start_time: f64,
clock_rate: f64,
) -> &'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;
COMMON_RHYTHMS
@@ -190,3 +110,84 @@ fn closest_rhythm(
})
.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)]
pub struct TaikoGradualDifficultyAttributes<'map> {
pub struct TaikoGradualDifficultyAttributes {
pub(crate) idx: usize,
attrs: TaikoDifficultyAttributes,
hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject<'map>>>>,
lists: ObjectLists<'map>,
hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject>>>,
lists: ObjectLists,
peaks: Peaks,
}
impl<'map> TaikoGradualDifficultyAttributes<'map> {
impl TaikoGradualDifficultyAttributes {
/// 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 clock_rate = mods.clock_rate();
@@ -75,13 +75,20 @@ impl<'map> TaikoGradualDifficultyAttributes<'map> {
.taiko_objects()
.enumerate()
.skip(2)
.zip(map.taiko_objects().skip(1))
.zip(map.taiko_objects())
.zip(map.hit_objects.iter().skip(1))
.zip(map.hit_objects.iter())
.fold(
ObjectLists::default(),
|mut lists, (((idx, base), last), last_last)| {
let diff_obj =
TaikoDifficultyObject::new(base, last, last_last, clock_rate, &lists, idx);
|mut lists, (((idx, (base, base_start_time)), last), last_last)| {
let diff_obj = TaikoDifficultyObject::new(
base,
base_start_time,
last.start_time,
last_last.start_time,
clock_rate,
&lists,
idx,
);
match &diff_obj.mono_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;
fn next(&mut self) -> Option<Self::Item> {
@@ -120,7 +127,7 @@ impl Iterator for TaikoGradualDifficultyAttributes<'_> {
{
let curr = curr.borrow();
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 {
@@ -169,7 +176,7 @@ impl Iterator for TaikoGradualDifficultyAttributes<'_> {
}
}
impl ExactSizeIterator for TaikoGradualDifficultyAttributes<'_> {
impl ExactSizeIterator for TaikoGradualDifficultyAttributes {
#[inline]
fn len(&self) -> usize {
self.hit_objects.len()
+1 -1
View File
@@ -134,7 +134,7 @@ impl TaikoScoreState {
/// ```
#[derive(Clone, Debug)]
pub struct TaikoGradualPerformanceAttributes<'map> {
difficulty: TaikoGradualDifficultyAttributes<'map>,
difficulty: TaikoGradualDifficultyAttributes,
performance: TaikoPP<'map>,
}
+14 -7
View File
@@ -232,15 +232,22 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
.taiko_objects()
.take(take)
.skip(2)
.zip(map.taiko_objects().skip(1))
.zip(map.taiko_objects())
.zip(map.hit_objects.iter().skip(1))
.zip(map.hit_objects.iter())
.enumerate()
.inspect(|(_, ((base, _), _))| max_combo += base.h.is_circle() as usize)
.inspect(|(_, (((base, _), _), _))| max_combo += base.is_hit as usize)
.fold(
ObjectLists::default(),
|mut lists, (idx, ((base, last), last_last))| {
let diff_obj =
TaikoDifficultyObject::new(base, last, last_last, clock_rate, &lists, idx);
|mut lists, (idx, (((base, base_start_time), last), last_last))| {
let diff_obj = TaikoDifficultyObject::new(
base,
base_start_time,
last.start_time,
last_last.start_time,
clock_rate,
&lists,
idx,
);
match &diff_obj.mono_idx {
MonoIndex::Centre(_) => lists.centres.push(idx),
@@ -272,7 +279,7 @@ fn rescale(stars: f64) -> f64 {
if stars < 0.0 {
stars
} 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 {
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)
}
@@ -52,15 +52,11 @@ impl StrainSkill for Colour {
&mut self.curr_section_end
}
fn strain_value_at(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
<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)
}
}
@@ -77,7 +73,7 @@ impl StrainDecaySkill for Colour {
&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)
}
}
@@ -91,7 +87,7 @@ impl ColourEvaluator {
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 parent_eval = mono_streak
@@ -104,7 +100,7 @@ impl ColourEvaluator {
}
fn evaluate_diff_of_alternating_mono_pattern(
alternating_mono_pattern: Rc<RefCell<AlternatingMonoPattern<'_>>>,
alternating_mono_pattern: Rc<RefCell<AlternatingMonoPattern>>,
) -> f64 {
let alternating_mono_pattern = alternating_mono_pattern.borrow();
@@ -118,14 +114,14 @@ impl ColourEvaluator {
}
fn evaluate_diff_of_repeating_hit_patterns(
repeating_hit_patterns: Rc<RefCell<RepeatingHitPatterns<'_>>>,
repeating_hit_patterns: Rc<RefCell<RepeatingHitPatterns>>,
) -> 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))
}
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject<'_>) -> f64 {
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject) -> f64 {
let colour = &hit_object.colour;
let mut difficulty = 0.0;
+10 -22
View File
@@ -13,7 +13,7 @@ use super::{
};
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;
}
@@ -24,22 +24,18 @@ pub(crate) trait StrainSkill: Skill {
fn curr_section_peak(&mut self) -> &mut f64;
fn curr_section_end(&mut self) -> &mut f64;
fn strain_value_at(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64;
fn strain_value_at(&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
if curr.idx == 0 {
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();
{
@@ -60,7 +56,7 @@ pub(crate) trait StrainSkill: Skill {
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
// * This means we need to capture the strain level at the beginning of the new section,
// * 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_mut(&mut self) -> &mut f64;
fn strain_value_of(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64;
fn strain_value_of(&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)
}
fn strain_value_at(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
*self.curr_strain_mut() *= self.strain_decay(curr.delta);
*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 {
let colour_rating =
StrainSkill::difficulty_value(self.colour) * Self::COLOUR_SKILL_MULTIPLIER;
// let rhythm_rating =
// StrainSkill::difficulty_value(self.rhythm.clone()) * Self::RHYTHM_SKILL_MULTIPLIER;
// let stamina_rating =
// StrainSkill::difficulty_value(self.stamina.clone()) * Self::STAMINA_SKILL_MULTIPLIER;
StrainSkill::difficulty_value(self.colour.clone()) * Self::COLOUR_SKILL_MULTIPLIER;
let rhythm_rating =
StrainSkill::difficulty_value(self.rhythm.clone()) * Self::RHYTHM_SKILL_MULTIPLIER;
let stamina_rating =
StrainSkill::difficulty_value(self.stamina.clone()) * Self::STAMINA_SKILL_MULTIPLIER;
PeaksDifficultyValues {
colour_rating,
rhythm_rating: 0.0,
stamina_rating: 0.0,
// combined_rating: self.difficulty_value(),
combined_rating: 0.0,
rhythm_rating,
stamina_rating,
combined_rating: self.difficulty_value(),
}
}
@@ -60,10 +59,10 @@ impl Peaks {
}
impl Skill for Peaks {
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) {
StrainSkill::process(&mut self.colour, curr, hit_objects);
StrainSkill::process(&mut self.rhythm, curr, hit_objects);
StrainSkill::process(&mut self.stamina, curr, hit_objects);
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
<Colour as Skill>::process(&mut self.colour, curr, hit_objects);
<Rhythm as Skill>::process(&mut self.rhythm, curr, hit_objects);
<Stamina as Skill>::process(&mut self.stamina, curr, hit_objects);
}
fn difficulty_value(self) -> f64 {
+7 -11
View File
@@ -40,7 +40,7 @@ impl Rhythm {
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;
self.history.push(HistoryElement::new(hit_object));
@@ -100,7 +100,7 @@ impl 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)
}
@@ -122,15 +122,11 @@ impl StrainSkill for Rhythm {
&mut self.curr_section_end
}
fn strain_value_at(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
<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)
}
}
@@ -147,8 +143,8 @@ impl StrainDecaySkill for Rhythm {
&mut self.curr_decay_strain
}
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject<'_>, _: &ObjectLists<'_>) -> f64 {
let base_is_circle = curr.base.h.is_circle();
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, _: &ObjectLists) -> f64 {
let base_is_circle = curr.base.is_hit;
// * drum rolls and swells are exempt.
if !base_is_circle {
@@ -187,7 +183,7 @@ pub(crate) struct HistoryElement {
}
impl HistoryElement {
fn new(difficulty_object: &TaikoDifficultyObject<'_>) -> Self {
fn new(difficulty_object: &TaikoDifficultyObject) -> Self {
Self {
idx: difficulty_object.idx,
rhythm: difficulty_object.rhythm,
+7 -18
View File
@@ -22,7 +22,7 @@ impl 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)
}
@@ -44,15 +44,11 @@ impl StrainSkill for Stamina {
&mut self.curr_section_end
}
fn strain_value_at(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
<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)
}
}
@@ -69,11 +65,7 @@ impl StrainDecaySkill for Stamina {
&mut self.curr_strain
}
fn strain_value_of(
&mut self,
curr: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
StaminaEvaluator::evaluate_diff_of(curr, hit_objects)
}
}
@@ -90,11 +82,8 @@ impl StaminaEvaluator {
30.0 / interval
}
fn evaluate_diff_of(
hit_object: &TaikoDifficultyObject<'_>,
hit_objects: &ObjectLists<'_>,
) -> f64 {
if !hit_object.base.is_hit() {
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
if !hit_object.base.is_hit {
return 0.0;
}
@@ -104,7 +93,7 @@ impl StaminaEvaluator {
if let Some(key_prev) = key_prev {
// * 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 {
// * There is no previous hit object hit by the current key
0.0
+14 -15
View File
@@ -5,18 +5,17 @@ use crate::{parse::HitObject, Beatmap};
use super::rim::Rim;
#[derive(Copy, Clone, Debug)]
pub(crate) struct TaikoObject<'h> {
pub(crate) h: &'h HitObject,
pub(crate) sound: u8,
pub(crate) struct TaikoObject {
pub(crate) is_hit: bool,
pub(crate) is_rim: bool,
}
impl TaikoObject<'_> {
pub(crate) fn is_rim(&self) -> bool {
self.sound.is_rim()
}
pub(crate) fn is_hit(&self) -> bool {
self.h.is_circle()
impl TaikoObject {
pub(crate) fn new(h: &HitObject, sound: u8) -> Self {
Self {
is_hit: h.is_circle(),
is_rim: sound.is_rim(),
}
}
}
@@ -41,14 +40,14 @@ impl IntoTaikoObjectIter for Beatmap {
}
impl<'m> Iterator for TaikoObjectIter<'m> {
type Item = TaikoObject<'m>;
type Item = (TaikoObject, f64);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Some(TaikoObject {
h: self.hit_objects.next()?,
sound: *self.sounds.next()?,
})
let h = self.hit_objects.next()?;
let sound = self.sounds.next()?;
Some((TaikoObject::new(h, *sound), h.start_time))
}
#[inline]
+10 -4
View File
@@ -9,14 +9,20 @@ pub(crate) struct TandemSorter {
impl TandemSorter {
/// Sort indices based on 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
T: PartialOrd,
{
let mut indices: Vec<_> = (0..).take(slice.len()).collect();
indices
.sort_unstable_by(|&i, &j| slice[i].partial_cmp(&slice[j]).unwrap_or(Ordering::Equal));
let closure =
|&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 }
}
@@ -75,7 +81,7 @@ mod tests {
#[test]
fn sort() {
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);
assert_eq!(base, vec![1, 2, 3, 4, 5, 7, 8, 9]);