fixed taiko
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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())
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+13
-14
@@ -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
@@ -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]);
|
||||||
|
|||||||
Reference in New Issue
Block a user