added convert support (#12)

* added taiko conversion

* finished converts

* made clippy happy

* optimized allocations for patterns

* taiko convert fixes

* added mode methods for osu calculators

* from CRLF back to LF

* updated changelog

* updated readme

* undo normal hitsound change

* added edge_sounds to changelog
This commit is contained in:
Max
2022-07-04 14:17:15 +02:00
committed by GitHub
parent c2e96ce95b
commit 7ed1ac5226
38 changed files with 2636 additions and 284 deletions
-111
View File
@@ -1,111 +0,0 @@
use std::cmp::Ordering;
use std::iter::{Cycle, Skip, Take};
use std::ops::Index;
use std::slice::Iter;
#[derive(Clone, Debug)]
pub(crate) struct LimitedQueue<T> {
queue: Vec<T>,
start: usize,
end: usize,
}
impl<T> LimitedQueue<T> {
#[inline]
pub(crate) fn new(capacity: usize) -> Self {
Self {
end: capacity - 1,
start: 0,
queue: Vec::with_capacity(capacity),
}
}
#[inline]
pub(crate) fn push(&mut self, elem: T) {
let capacity = self.queue.capacity();
self.end = (self.end + 1) % capacity;
if self.queue.len() == capacity {
self.start = (self.start + 1) % capacity;
self.queue[self.end] = elem;
} else {
self.queue.push(elem);
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.queue.len()
}
#[inline]
pub(crate) fn last(&self) -> Option<&T> {
self.queue.get(self.end)
}
#[inline]
pub(crate) fn clear(&mut self) {
self.start = 0;
self.end = self.queue.capacity() - 1;
self.queue.clear();
}
#[inline]
pub(crate) fn full(&self) -> bool {
self.queue.len() == self.queue.capacity()
}
#[inline]
pub(crate) fn iter(&self) -> LimitedQueueIter<'_, T> {
let iter = self
.queue
.iter()
.cycle()
.skip(self.start)
.take(self.queue.len());
LimitedQueueIter { iter }
}
}
impl<T: PartialOrd> LimitedQueue<T> {
pub(crate) fn min(&self) -> Option<&T> {
self.queue
.iter()
.reduce(|min, next| match min.partial_cmp(next) {
Some(Ordering::Less) => min,
Some(Ordering::Equal) => min,
Some(Ordering::Greater) => next,
None => min,
})
}
}
impl<T> Index<usize> for LimitedQueue<T> {
type Output = T;
#[inline]
fn index(&self, idx: usize) -> &Self::Output {
&self.queue[(self.start + idx) % self.queue.capacity()]
}
}
pub(crate) struct LimitedQueueIter<'a, T> {
iter: Take<Skip<Cycle<Iter<'a, T>>>>,
}
impl<'a, T> Iterator for LimitedQueueIter<'a, T> {
type Item = &'a T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a, T> ExactSizeIterator for LimitedQueueIter<'a, T> {}
+24 -5
View File
@@ -2,7 +2,6 @@ mod difficulty_object;
mod gradual_difficulty;
mod gradual_performance;
mod hitobject_rhythm;
mod limited_queue;
mod pp;
mod rim;
mod skill;
@@ -14,7 +13,6 @@ use difficulty_object::DifficultyObject;
pub use gradual_difficulty::*;
pub use gradual_performance::*;
use hitobject_rhythm::{closest_rhythm, HitObjectRhythm};
use limited_queue::LimitedQueue;
pub use pp::*;
use rim::Rim;
use skill_kind::SkillKind;
@@ -22,8 +20,9 @@ use stamina_cheese::StaminaCheeseDetector;
use taiko_object::IntoTaikoObjectIter;
use crate::taiko::skill::Skills;
use crate::{Beatmap, Mods, Strains};
use crate::{Beatmap, GameMode, Mods, OsuStars, Strains};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::f64::consts::PI;
@@ -53,7 +52,7 @@ const STAMINA_SKILL_MULTIPLIER: f64 = 0.02;
/// ```
#[derive(Clone, Debug)]
pub struct TaikoStars<'map> {
map: &'map Beatmap,
map: Cow<'map, Beatmap>,
mods: u32,
passed_objects: Option<usize>,
clock_rate: Option<f64>,
@@ -64,7 +63,7 @@ impl<'map> TaikoStars<'map> {
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
Self {
map,
map: Cow::Borrowed(map),
mods: 0,
passed_objects: None,
clock_rate: None,
@@ -331,7 +330,27 @@ impl TaikoPerformanceAttributes {
}
impl From<TaikoPerformanceAttributes> for TaikoDifficultyAttributes {
#[inline]
fn from(attributes: TaikoPerformanceAttributes) -> Self {
attributes.difficulty
}
}
impl<'map> From<OsuStars<'map>> for TaikoStars<'map> {
#[inline]
fn from(osu: OsuStars<'map>) -> Self {
let OsuStars {
map,
mods,
passed_objects,
clock_rate,
} = osu;
Self {
map: map.convert_mode(GameMode::TKO),
mods,
passed_objects,
clock_rate,
}
}
}
+38 -5
View File
@@ -1,5 +1,7 @@
use std::borrow::Cow;
use super::{TaikoDifficultyAttributes, TaikoPerformanceAttributes, TaikoScoreState, TaikoStars};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
use crate::{Beatmap, DifficultyAttributes, GameMode, Mods, OsuPP, PerformanceAttributes};
/// Performance calculator on osu!taiko maps.
///
@@ -33,7 +35,7 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct TaikoPP<'map> {
map: &'map Beatmap,
map: Cow<'map, Beatmap>,
attributes: Option<TaikoDifficultyAttributes>,
mods: u32,
combo: Option<usize>,
@@ -51,7 +53,7 @@ impl<'map> TaikoPP<'map> {
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
Self {
map,
map: Cow::Borrowed(map),
attributes: None,
mods: 0,
combo: None,
@@ -171,7 +173,7 @@ impl<'map> TaikoPP<'map> {
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> TaikoPerformanceAttributes {
let attributes = self.attributes.take().unwrap_or_else(|| {
let mut calculator = TaikoStars::new(self.map).mods(self.mods);
let mut calculator = TaikoStars::new(self.map.as_ref()).mods(self.mods);
if let Some(passed_objects) = self.passed_objects {
calculator = calculator.passed_objects(passed_objects);
@@ -205,7 +207,7 @@ impl<'map> TaikoPP<'map> {
}
let inner = TaikoPPInner {
map: self.map,
map: self.map.as_ref(),
attributes,
mods: self.mods,
acc: self.acc,
@@ -302,6 +304,37 @@ fn difficulty_range_od(od: f64) -> f64 {
crate::difficulty_range(od, 20.0, 35.0, 50.0)
}
impl<'map> From<OsuPP<'map>> for TaikoPP<'map> {
#[inline]
fn from(osu: OsuPP<'map>) -> Self {
let OsuPP {
map,
mods,
acc,
combo,
n300,
n100,
n_misses,
passed_objects,
clock_rate,
..
} = osu;
Self {
map: map.convert_mode(GameMode::TKO),
attributes: None,
mods,
combo,
acc: acc.unwrap_or(1.0),
passed_objects,
clock_rate,
n300,
n100,
n_misses,
}
}
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait TaikoAttributeProvider {
/// Provide the actual difficulty attributes.
+3 -1
View File
@@ -1,4 +1,6 @@
use super::{DifficultyObject, HitObjectRhythm, LimitedQueue, Rim};
use crate::limited_queue::LimitedQueue;
use super::{DifficultyObject, HitObjectRhythm, Rim};
use std::ops::Index;
+2 -2
View File
@@ -1,5 +1,5 @@
use super::{LimitedQueue, Rim};
use crate::Beatmap;
use super::Rim;
use crate::{limited_queue::LimitedQueue, Beatmap};
const ROLL_MIN_REPETITIONS: usize = 12;
const TL_MIN_REPETITIONS: isize = 16;