adjust osu skill structure to taiko & mania
This commit is contained in:
+6
-114
@@ -1,120 +1,12 @@
|
||||
use std::{cmp::Ordering, mem};
|
||||
|
||||
use super::{difficulty_object::ManiaDifficultyObject, SECTION_LEN};
|
||||
|
||||
pub(crate) use self::strain::Strain;
|
||||
|
||||
mod strain;
|
||||
mod traits;
|
||||
|
||||
pub(crate) trait Skill {
|
||||
fn process(&mut self, curr: &ManiaDifficultyObject, diff_objects: &[ManiaDifficultyObject]);
|
||||
fn difficulty_value(self) -> f64;
|
||||
}
|
||||
pub(crate) use self::{
|
||||
strain::Strain,
|
||||
traits::{Skill, StrainDecaySkill, StrainSkill},
|
||||
};
|
||||
|
||||
pub(crate) trait StrainSkill: Sized + Skill {
|
||||
const DECAY_WEIGHT: f64 = 0.9;
|
||||
|
||||
fn curr_section_end(&self) -> f64;
|
||||
fn curr_section_end_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn curr_section_peak(&self) -> f64;
|
||||
fn curr_section_peak_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn strain_peaks_mut(&mut self) -> &mut Vec<f64>;
|
||||
|
||||
fn strain_value_at(&mut self, curr: &ManiaDifficultyObject) -> f64;
|
||||
|
||||
fn process(&mut self, curr: &ManiaDifficultyObject, diff_objects: &[ManiaDifficultyObject]) {
|
||||
// * The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
if curr.idx == 0 {
|
||||
*self.curr_section_end_mut() = (curr.start_time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
}
|
||||
|
||||
while curr.start_time > self.curr_section_end() {
|
||||
self.save_curr_peak();
|
||||
self.start_new_section_from(self.curr_section_end(), curr, diff_objects);
|
||||
*self.curr_section_end_mut() += SECTION_LEN;
|
||||
}
|
||||
|
||||
*self.curr_section_peak_mut() = self.strain_value_at(curr).max(self.curr_section_peak());
|
||||
}
|
||||
|
||||
fn save_curr_peak(&mut self) {
|
||||
let curr_section_peak = self.curr_section_peak();
|
||||
self.strain_peaks_mut().push(curr_section_peak);
|
||||
}
|
||||
|
||||
fn start_new_section_from(
|
||||
&mut self,
|
||||
time: f64,
|
||||
curr: &ManiaDifficultyObject,
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
) {
|
||||
*self.curr_section_peak_mut() = self.calculate_initial_strain(time, curr, diff_objects);
|
||||
}
|
||||
|
||||
fn calculate_initial_strain(
|
||||
&self,
|
||||
time: f64,
|
||||
curr: &ManiaDifficultyObject,
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
) -> f64;
|
||||
|
||||
fn get_curr_strain_peaks(mut self) -> Vec<f64> {
|
||||
let mut peaks = mem::take(self.strain_peaks_mut());
|
||||
peaks.push(self.curr_section_peak());
|
||||
|
||||
peaks
|
||||
}
|
||||
|
||||
fn difficulty_value(self) -> f64 {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
// * Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
||||
// * These sections will not contribute to the difficulty.
|
||||
let mut peaks = self.get_curr_strain_peaks();
|
||||
peaks.retain(|&peak| peak > 0.0);
|
||||
peaks.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
// * Difficulty is the weighted sum of the highest strains from every section.
|
||||
// * We're sorting from highest to lowest strain.
|
||||
for strain in peaks {
|
||||
difficulty += strain * weight;
|
||||
weight *= Self::DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait StrainDecaySkill: StrainSkill {
|
||||
const SKILL_MULTIPLIER: f64;
|
||||
const STRAIN_DECAY_BASE: f64;
|
||||
|
||||
fn curr_strain(&self) -> f64;
|
||||
fn curr_strain_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn strain_value_of(&mut self, curr: &ManiaDifficultyObject) -> f64;
|
||||
|
||||
fn calculate_initial_strain(
|
||||
&self,
|
||||
time: f64,
|
||||
curr: &ManiaDifficultyObject,
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
) -> f64;
|
||||
|
||||
fn strain_value_at(&mut self, curr: &ManiaDifficultyObject) -> f64 {
|
||||
*self.curr_strain_mut() *= self.strain_decay(curr.delta_time);
|
||||
*self.curr_strain_mut() += self.strain_value_of(curr) * Self::SKILL_MULTIPLIER;
|
||||
|
||||
self.curr_strain()
|
||||
}
|
||||
|
||||
fn strain_decay(&self, ms: f64) -> f64 {
|
||||
Self::STRAIN_DECAY_BASE.powf(ms / 1000.0)
|
||||
}
|
||||
}
|
||||
use super::difficulty_object::ManiaDifficultyObject;
|
||||
|
||||
fn previous(
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
use std::{cmp::Ordering, mem};
|
||||
|
||||
use crate::mania::{difficulty_object::ManiaDifficultyObject, SECTION_LEN};
|
||||
|
||||
pub(crate) trait Skill {
|
||||
fn process(&mut self, curr: &ManiaDifficultyObject, diff_objects: &[ManiaDifficultyObject]);
|
||||
fn difficulty_value(self) -> f64;
|
||||
}
|
||||
|
||||
pub(crate) trait StrainSkill: Sized + Skill {
|
||||
const DECAY_WEIGHT: f64 = 0.9;
|
||||
|
||||
fn curr_section_end(&self) -> f64;
|
||||
fn curr_section_end_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn curr_section_peak(&self) -> f64;
|
||||
fn curr_section_peak_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn strain_peaks_mut(&mut self) -> &mut Vec<f64>;
|
||||
|
||||
fn strain_value_at(&mut self, curr: &ManiaDifficultyObject) -> f64;
|
||||
|
||||
fn process(&mut self, curr: &ManiaDifficultyObject, diff_objects: &[ManiaDifficultyObject]) {
|
||||
// * The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
if curr.idx == 0 {
|
||||
*self.curr_section_end_mut() = (curr.start_time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
}
|
||||
|
||||
while curr.start_time > self.curr_section_end() {
|
||||
self.save_curr_peak();
|
||||
self.start_new_section_from(self.curr_section_end(), curr, diff_objects);
|
||||
*self.curr_section_end_mut() += SECTION_LEN;
|
||||
}
|
||||
|
||||
*self.curr_section_peak_mut() = self.strain_value_at(curr).max(self.curr_section_peak());
|
||||
}
|
||||
|
||||
fn save_curr_peak(&mut self) {
|
||||
let curr_section_peak = self.curr_section_peak();
|
||||
self.strain_peaks_mut().push(curr_section_peak);
|
||||
}
|
||||
|
||||
fn start_new_section_from(
|
||||
&mut self,
|
||||
time: f64,
|
||||
curr: &ManiaDifficultyObject,
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
) {
|
||||
*self.curr_section_peak_mut() = self.calculate_initial_strain(time, curr, diff_objects);
|
||||
}
|
||||
|
||||
fn calculate_initial_strain(
|
||||
&self,
|
||||
time: f64,
|
||||
curr: &ManiaDifficultyObject,
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
) -> f64;
|
||||
|
||||
fn get_curr_strain_peaks(mut self) -> Vec<f64> {
|
||||
let mut peaks = mem::take(self.strain_peaks_mut());
|
||||
peaks.push(self.curr_section_peak());
|
||||
|
||||
peaks
|
||||
}
|
||||
|
||||
fn difficulty_value(self) -> f64 {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
// * Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
||||
// * These sections will not contribute to the difficulty.
|
||||
let mut peaks = self.get_curr_strain_peaks();
|
||||
peaks.retain(|&peak| peak > 0.0);
|
||||
peaks.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
// * Difficulty is the weighted sum of the highest strains from every section.
|
||||
// * We're sorting from highest to lowest strain.
|
||||
for strain in peaks {
|
||||
difficulty += strain * weight;
|
||||
weight *= Self::DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait StrainDecaySkill: StrainSkill {
|
||||
const SKILL_MULTIPLIER: f64;
|
||||
const STRAIN_DECAY_BASE: f64;
|
||||
|
||||
fn curr_strain(&self) -> f64;
|
||||
fn curr_strain_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn strain_value_of(&mut self, curr: &ManiaDifficultyObject) -> f64;
|
||||
|
||||
fn calculate_initial_strain(
|
||||
&self,
|
||||
time: f64,
|
||||
curr: &ManiaDifficultyObject,
|
||||
diff_objects: &[ManiaDifficultyObject],
|
||||
) -> f64;
|
||||
|
||||
fn strain_value_at(&mut self, curr: &ManiaDifficultyObject) -> f64 {
|
||||
*self.curr_strain_mut() *= self.strain_decay(curr.delta_time);
|
||||
*self.curr_strain_mut() += self.strain_value_of(curr) * Self::SKILL_MULTIPLIER;
|
||||
|
||||
self.curr_strain()
|
||||
}
|
||||
|
||||
fn strain_decay(&self, ms: f64) -> f64 {
|
||||
Self::STRAIN_DECAY_BASE.powf(ms / 1000.0)
|
||||
}
|
||||
}
|
||||
+4
-119
@@ -2,124 +2,9 @@ mod colour;
|
||||
mod peaks;
|
||||
mod rhythm;
|
||||
mod stamina;
|
||||
mod traits;
|
||||
|
||||
use std::{cmp::Ordering, mem};
|
||||
|
||||
pub(crate) use self::peaks::{Peaks, PeaksDifficultyValues, PeaksRaw};
|
||||
|
||||
use super::{
|
||||
difficulty_object::{ObjectLists, TaikoDifficultyObject},
|
||||
SECTION_LEN,
|
||||
pub(crate) use self::{
|
||||
peaks::{Peaks, PeaksDifficultyValues, PeaksRaw},
|
||||
traits::{Skill, StrainDecaySkill, StrainSkill},
|
||||
};
|
||||
|
||||
pub(crate) trait Skill: Sized {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists);
|
||||
fn difficulty_value(self) -> f64;
|
||||
}
|
||||
|
||||
pub(crate) trait StrainSkill: Skill {
|
||||
const DECAY_WEIGHT: f64 = 0.9;
|
||||
|
||||
fn strain_peaks_mut(&mut self) -> &mut Vec<f64>;
|
||||
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 calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64;
|
||||
|
||||
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.start_time / section_len).ceil() * section_len;
|
||||
}
|
||||
|
||||
while curr.start_time > *self.curr_section_end() {
|
||||
self.save_curr_peak();
|
||||
|
||||
{
|
||||
let section_end = *self.curr_section_end();
|
||||
self.start_new_section_from(section_end, curr);
|
||||
}
|
||||
|
||||
*self.curr_section_end() += SECTION_LEN as f64;
|
||||
}
|
||||
|
||||
*self.curr_section_peak() = self
|
||||
.strain_value_at(curr, hit_objects)
|
||||
.max(*self.curr_section_peak());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn save_curr_peak(&mut self) {
|
||||
let peak = *self.curr_section_peak();
|
||||
self.strain_peaks_mut().push(peak);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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.
|
||||
*self.curr_section_peak() = self.calculate_initial_strain(time, curr);
|
||||
}
|
||||
|
||||
fn difficulty_value(self) -> f64 {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
// * Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
||||
// * These sections will not contribute to the difficulty.
|
||||
let mut peaks = self.get_curr_strain_peaks();
|
||||
|
||||
peaks.retain(|&peak| peak > 0.0);
|
||||
peaks.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
// * Difficulty is the weighted sum of the highest strains from every section.
|
||||
// * We're sorting from highest to lowest strain.
|
||||
for strain in peaks {
|
||||
difficulty += strain * weight;
|
||||
weight *= Self::DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_curr_strain_peaks(mut self) -> Vec<f64> {
|
||||
let curr_peak = *self.curr_section_peak();
|
||||
let mut strain_peaks = mem::take(self.strain_peaks_mut());
|
||||
strain_peaks.push(curr_peak);
|
||||
|
||||
strain_peaks
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait StrainDecaySkill: StrainSkill {
|
||||
const SKILL_MULTIPLIER: f64;
|
||||
const STRAIN_DECAY_BASE: f64;
|
||||
|
||||
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;
|
||||
|
||||
#[inline]
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
self.curr_strain() * self.strain_decay(time - curr.prev_time)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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;
|
||||
|
||||
self.curr_strain()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn strain_decay(&self, ms: f64) -> f64 {
|
||||
Self::STRAIN_DECAY_BASE.powf(ms / 1000.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
use std::{cmp::Ordering, mem};
|
||||
|
||||
use crate::taiko::{
|
||||
difficulty_object::{ObjectLists, TaikoDifficultyObject},
|
||||
SECTION_LEN,
|
||||
};
|
||||
|
||||
pub(crate) trait Skill: Sized {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists);
|
||||
fn difficulty_value(self) -> f64;
|
||||
}
|
||||
|
||||
pub(crate) trait StrainSkill: Skill {
|
||||
const DECAY_WEIGHT: f64 = 0.9;
|
||||
|
||||
fn strain_peaks_mut(&mut self) -> &mut Vec<f64>;
|
||||
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 calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64;
|
||||
|
||||
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.start_time / section_len).ceil() * section_len;
|
||||
}
|
||||
|
||||
while curr.start_time > *self.curr_section_end() {
|
||||
self.save_curr_peak();
|
||||
|
||||
{
|
||||
let section_end = *self.curr_section_end();
|
||||
self.start_new_section_from(section_end, curr);
|
||||
}
|
||||
|
||||
*self.curr_section_end() += SECTION_LEN as f64;
|
||||
}
|
||||
|
||||
*self.curr_section_peak() = self
|
||||
.strain_value_at(curr, hit_objects)
|
||||
.max(*self.curr_section_peak());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn save_curr_peak(&mut self) {
|
||||
let peak = *self.curr_section_peak();
|
||||
self.strain_peaks_mut().push(peak);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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.
|
||||
*self.curr_section_peak() = self.calculate_initial_strain(time, curr);
|
||||
}
|
||||
|
||||
fn difficulty_value(self) -> f64 {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
// * Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
||||
// * These sections will not contribute to the difficulty.
|
||||
let mut peaks = self.get_curr_strain_peaks();
|
||||
|
||||
peaks.retain(|&peak| peak > 0.0);
|
||||
peaks.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
// * Difficulty is the weighted sum of the highest strains from every section.
|
||||
// * We're sorting from highest to lowest strain.
|
||||
for strain in peaks {
|
||||
difficulty += strain * weight;
|
||||
weight *= Self::DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_curr_strain_peaks(mut self) -> Vec<f64> {
|
||||
let curr_peak = *self.curr_section_peak();
|
||||
let mut strain_peaks = mem::take(self.strain_peaks_mut());
|
||||
strain_peaks.push(curr_peak);
|
||||
|
||||
strain_peaks
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait StrainDecaySkill: StrainSkill {
|
||||
const SKILL_MULTIPLIER: f64;
|
||||
const STRAIN_DECAY_BASE: f64;
|
||||
|
||||
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;
|
||||
|
||||
#[inline]
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
self.curr_strain() * self.strain_decay(time - curr.prev_time)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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;
|
||||
|
||||
self.curr_strain()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn strain_decay(&self, ms: f64) -> f64 {
|
||||
Self::STRAIN_DECAY_BASE.powf(ms / 1000.0)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user