adjust osu skill structure to taiko & mania

This commit is contained in:
MaxOhn
2022-10-22 19:55:28 +02:00
parent 9acd6efe50
commit 14d3db7940
4 changed files with 241 additions and 233 deletions
+6 -114
View File
@@ -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],
+113
View File
@@ -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
View File
@@ -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)
}
}
+118
View File
@@ -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)
}
}