feat: adjusted lint restrictions (#31)

This commit is contained in:
Badewanne3
2023-12-31 09:19:41 +01:00
committed by GitHub
parent aeb65ab3a4
commit ecf1b2c7f4
59 changed files with 427 additions and 374 deletions
+13 -8
View File
@@ -137,21 +137,25 @@ impl BeatmapAttributesBuilder {
};
let raw_ar = mod_mult(self.ar);
let preempt = difficulty_range(raw_ar as f64, 1800.0, 1200.0, 450.0) / clock_rate;
let preempt = difficulty_range(f64::from(raw_ar), 1800.0, 1200.0, 450.0) / clock_rate;
// OD
let hit_window = match self.mode {
GameMode::Osu | GameMode::Catch => {
let raw_od = mod_mult(self.od);
difficulty_range(raw_od as f64, Self::OSU_MIN, Self::OSU_AVG, Self::OSU_MAX)
/ clock_rate
difficulty_range(
f64::from(raw_od),
Self::OSU_MIN,
Self::OSU_AVG,
Self::OSU_MAX,
) / clock_rate
}
GameMode::Taiko => {
let raw_od = mod_mult(self.od);
let diff_range = difficulty_range(
raw_od as f64,
f64::from(raw_od),
Self::TAIKO_MIN,
Self::TAIKO_AVG,
Self::TAIKO_MAX,
@@ -174,7 +178,7 @@ impl BeatmapAttributesBuilder {
value *= 1.4;
}
((value as f64 * clock_rate).floor() / clock_rate).ceil()
((f64::from(value) * clock_rate).floor() / clock_rate).ceil()
}
};
@@ -215,14 +219,14 @@ impl BeatmapAttributesBuilder {
let od = match self.mode {
GameMode::Osu => (Self::OSU_MIN - od) / 6.0,
GameMode::Taiko => (Self::TAIKO_MIN - od) / (Self::TAIKO_MIN - Self::TAIKO_AVG) * 5.0,
GameMode::Catch | GameMode::Mania => self.od as f64,
GameMode::Catch | GameMode::Mania => f64::from(self.od),
};
BeatmapAttributes {
ar,
od,
cs: cs as f64,
hp: hp as f64,
cs: f64::from(cs),
hp: f64::from(hp),
clock_rate,
hit_windows,
}
@@ -245,6 +249,7 @@ impl From<&Beatmap> for BeatmapAttributesBuilder {
}
}
#[allow(clippy::similar_names)]
fn difficulty_range(difficulty: f64, min: f64, mid: f64, max: f64) -> f64 {
if difficulty > 5.0 {
mid + (max - mid) * (difficulty - 5.0) / 5.0
+4 -4
View File
@@ -12,8 +12,8 @@ pub struct TimingPoint {
impl TimingPoint {
/// Create a new [`TimingPoint`].
#[inline]
pub fn new(time: f64, beat_len: f64) -> Self {
Self { time, beat_len }
pub const fn new(time: f64, beat_len: f64) -> Self {
Self { beat_len, time }
}
}
@@ -60,7 +60,7 @@ impl DifficultyPoint {
// * Note: In stable, the division occurs on floats, but with compiler optimisations
// * turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?).
let bpm_mult = if beat_len < 0.0 {
((-beat_len) as f32).clamp(10.0, 10_000.0) as f64 / 100.0
f64::from(((-beat_len) as f32).clamp(10.0, 10_000.0)) / 100.0
} else {
1.0
};
@@ -113,7 +113,7 @@ impl EffectPoint {
/// Create a new [`EffectPoint`].
#[inline]
pub fn new(time: f64, kiai: bool) -> Self {
pub const fn new(time: f64, kiai: bool) -> Self {
Self { time, kiai }
}
}
+3 -3
View File
@@ -9,7 +9,7 @@ pub(crate) struct Random {
}
impl Random {
pub(crate) fn new(seed: i32) -> Self {
pub(crate) const fn new(seed: i32) -> Self {
Self {
x: seed as u32,
y: 842_502_087,
@@ -33,10 +33,10 @@ impl Random {
}
pub(crate) fn gen_double(&mut self) -> f64 {
INT_TO_REAL * self.gen_signed() as f64
INT_TO_REAL * f64::from(self.gen_signed())
}
pub(crate) fn gen_int_range(&mut self, min: i32, max: i32) -> i32 {
(min as f64 + self.gen_double() * (max - min) as f64) as i32
(f64::from(min) + self.gen_double() * f64::from(max - min)) as i32
}
}
+7 -6
View File
@@ -25,6 +25,7 @@ mod pattern_type;
const MAX_NOTES_FOR_DENSITY: usize = 7;
impl Beatmap {
#[allow(clippy::too_many_lines)]
pub(in crate::beatmap) fn convert_to_mania(&self) -> Self {
let mut map = self.clone_without_hit_objects(false);
@@ -52,14 +53,14 @@ impl Beatmap {
.count();
let percent_slider_or_spinner =
(slider_or_spinner_count as f32 / self.hit_objects.len() as f32) as f64;
f64::from(slider_or_spinner_count as f32 / self.hit_objects.len() as f32);
let target_columns = if percent_slider_or_spinner < 0.2 {
7.0
} else if percent_slider_or_spinner < 0.3 || rounded_cs >= 5.0 {
(6 + (rounded_od > 5.0) as u8) as f32
f32::from(6 + u8::from(rounded_od > 5.0))
} else if percent_slider_or_spinner > 0.6 {
(4 + (rounded_od > 4.0) as u8) as f32
f32::from(4 + u8::from(rounded_od > 4.0))
} else {
(rounded_od + 1.0).clamp(4.0, 7.0)
};
@@ -67,7 +68,7 @@ impl Beatmap {
map.cs = target_columns;
let mut prev_note_times: LimitedQueue<f64, MAX_NOTES_FOR_DENSITY> = LimitedQueue::new();
let mut density = i32::MAX as f64;
let mut density = f64::from(i32::MAX);
let mut compute_density = |new_note_time: f64, d: &mut f64| {
prev_note_times.push(new_note_time);
@@ -129,10 +130,10 @@ impl Beatmap {
edge_sounds,
);
let segment_duration = gen.segment_duration as f64;
let segment_duration = f64::from(gen.segment_duration);
for i in 0..=repeats as i32 + 1 {
let time = obj.start_time + segment_duration * i as f64;
let time = obj.start_time + segment_duration * f64::from(i);
last_values.time = time;
last_values.pos = obj.pos;
+7 -7
View File
@@ -93,15 +93,15 @@ impl Pattern {
let hit_object = if start_time == end_time {
HitObject {
pos,
start_time: start_time as f64,
start_time: f64::from(start_time),
kind: HitObjectKind::Circle,
}
} else {
HitObject {
pos,
start_time: start_time as f64,
start_time: f64::from(start_time),
kind: HitObjectKind::Hold {
end_time: end_time as f64,
end_time: f64::from(end_time),
},
}
};
@@ -121,15 +121,15 @@ impl Pattern {
let hit_object = if start_time == end_time {
HitObject {
pos,
start_time: start_time as f64,
start_time: f64::from(start_time),
kind: HitObjectKind::Circle,
}
} else {
HitObject {
pos,
start_time: start_time as f64,
start_time: f64::from(start_time),
kind: HitObjectKind::Hold {
end_time: end_time as f64,
end_time: f64::from(end_time),
},
}
};
@@ -163,5 +163,5 @@ impl Pattern {
fn column_to_pos(column: u8, total_columns: i32) -> f32 {
let divisor = 512.0 / total_columns as f32;
(column as f32 * divisor).ceil()
(f32::from(column) * divisor).ceil()
}
@@ -62,8 +62,8 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
let start_time = hit_object.start_time.round_even() as i32;
// * This matches stable's calculation.
let end_time = (start_time as f64
+ curve.dist() * beat_len * span_count as f64 * 0.01 / orig.slider_mult)
let end_time = (f64::from(start_time)
+ curve.dist() * beat_len * f64::from(span_count) * 0.01 / orig.slider_mult)
.floor() as i32;
let segment_duration = (end_time - start_time) / span_count;
@@ -100,6 +100,8 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
for obj in orig_pattern.hit_objects {
let col = ManiaObject::column(obj.pos.x, self.total_columns as f32) as u8;
// Keeping it in-sync with lazer
#[allow(clippy::if_not_else)]
if self.end_time != obj.end_time().round_even() as i32 {
intermediate_pattern.add_object(obj, col);
} else {
@@ -142,7 +144,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
self.convert_type &= !PatternType::FORCE_NOT_STACK;
}
let note_count = 1 + (self.segment_duration >= 80) as i32;
let note_count = 1 + i32::from(self.segment_duration >= 80);
self.generate_random_notes(self.start_time, note_count)
} else if conversion_diff > 6.5 {
@@ -241,7 +243,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
None,
None,
None,
Some(&|c| c != last_column as i32),
Some(&|c| c != i32::from(last_column)),
&[],
);
@@ -262,7 +264,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
// * - x - -
// * x - - -
let mut column = self.get_column(Some(true)) as i32;
let mut column = i32::from(self.get_column(Some(true)));
let mut increasing = self.random.gen_double() > 0.5;
let mut pattern = Pattern::with_capacity(self.span_count as usize + 1);
@@ -299,13 +301,13 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
let legacy = (4..=8).contains(&self.total_columns);
let interval = self
.random
.gen_int_range(1, self.total_columns - (legacy as i32));
.gen_int_range(1, self.total_columns - i32::from(legacy));
let mut next_column = self.get_column(Some(true)) as i32;
let mut next_column = i32::from(self.get_column(Some(true)));
let random_start = self.random_start();
let not_2k = self.total_columns > 2;
let mut pattern =
Pattern::with_capacity((self.span_count as usize + 1) * (1 + not_2k as usize));
Pattern::with_capacity((self.span_count as usize + 1) * (1 + usize::from(not_2k)));
for _ in 0..=self.span_count as usize {
pattern.add_slider_note(self, next_column as u8, start_time, start_time);
@@ -313,7 +315,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
next_column += interval;
if next_column >= self.total_columns - random_start {
next_column = next_column - self.total_columns - random_start + (legacy as i32);
next_column = next_column - self.total_columns - random_start + i32::from(legacy);
}
next_column += random_start;
@@ -323,7 +325,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
pattern.add_slider_note(self, next_column as u8, start_time, start_time);
}
next_column = PatternGenerator::get_random_column(self, None, None) as i32;
next_column = i32::from(PatternGenerator::get_random_column(self, None, None));
start_time += self.segment_duration;
}
@@ -476,7 +478,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
let ignore_head = !(sample.whistle() || sample.finish() || sample.clap());
let mut row_pattern = Pattern::default();
let hold_column = hold_column as i32;
let hold_column = i32::from(hold_column);
for _ in 0..=self.span_count as usize {
if !(ignore_head && start_time == self.start_time) {
@@ -15,7 +15,7 @@ trait PatternGenerator {
// ----------------------------------
fn random_start(&self) -> i32 {
(self.total_columns() == 8) as i32
i32::from(self.total_columns() == 8)
}
fn get_column(&self, allow_special: Option<bool>) -> u8 {
@@ -53,7 +53,7 @@ trait PatternGenerator {
} else if val >= 1.0 - p3 {
3
} else {
1 + (val >= 1.0 - p2) as i32
1 + i32::from(val >= 1.0 - p2)
}
}
@@ -71,8 +71,8 @@ trait PatternGenerator {
}
let mut conversion_difficulty = 0.0;
conversion_difficulty += (orig.hp + orig.ar.clamp(4.0, 7.0)) as f64 / 1.5;
conversion_difficulty += orig.hit_objects.len() as f64 / drain_time as f64 * 9.0;
conversion_difficulty += f64::from(orig.hp + orig.ar.clamp(4.0, 7.0)) / 1.5;
conversion_difficulty += orig.hit_objects.len() as f64 / f64::from(drain_time) * 9.0;
conversion_difficulty /= 38.0;
conversion_difficulty *= 5.0;
conversion_difficulty /= 1.15;
@@ -115,7 +115,7 @@ trait PatternGenerator {
};
// * Check for the initial column
if is_valid(initial_column as i32) {
if is_valid(i32::from(initial_column)) {
return initial_column;
}
@@ -131,7 +131,7 @@ trait PatternGenerator {
PatternGenerator::get_random_column(self, Some(lower), Some(upper))
};
!is_valid(initial_column as i32)
!is_valid(i32::from(initial_column))
} {}
initial_column
+1 -1
View File
@@ -66,7 +66,7 @@ impl fmt::Display for PatternType {
}
impl PatternType {
pub(crate) fn contains(self, other: Self) -> bool {
pub(crate) const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
}
+11 -9
View File
@@ -1,6 +1,6 @@
use crate::{
curve::{Curve, CurveBuffers},
parse::{HitObject, HitObjectKind},
parse::{HitObject, HitObjectKind, Pos2},
util::TandemSorter,
Beatmap, GameMode,
};
@@ -13,7 +13,7 @@ impl Beatmap {
let mut map = self.clone_without_hit_objects(true);
let mut curve_bufs = CurveBuffers::default();
map.slider_mult *= LEGACY_TAIKO_VELOCITY_MULTIPLIER as f64;
map.slider_mult *= f64::from(LEGACY_TAIKO_VELOCITY_MULTIPLIER);
for (obj, sound) in self.hit_objects.iter().zip(self.sounds.iter()) {
match obj.kind {
@@ -38,10 +38,12 @@ impl Beatmap {
let edge_sound_count = edge_sounds.len().max(1);
while j
<= obj.start_time + params.duration as f64 + params.tick_spacing / 8.0
<= obj.start_time
+ f64::from(params.duration)
+ params.tick_spacing / 8.0
{
let h = HitObject {
pos: Default::default(),
pos: Pos2::default(),
start_time: j,
kind: HitObjectKind::Circle,
};
@@ -107,7 +109,7 @@ impl Beatmap {
// * The true distance, accounting for any repeats. This ends up being the drum roll distance later
let spans = (*repeats + 1) as f64;
let dist = curve.dist() * spans * LEGACY_TAIKO_VELOCITY_MULTIPLIER as f64;
let dist = curve.dist() * spans * f64::from(LEGACY_TAIKO_VELOCITY_MULTIPLIER);
let timing_point = self.timing_point_at(*start_time);
let difficulty_point = self.difficulty_point_at(*start_time).unwrap_or_default();
@@ -115,13 +117,13 @@ impl Beatmap {
let mut beat_len = timing_point.beat_len * difficulty_point.bpm_mult;
let slider_scoring_point_dist =
OSU_BASE_SCORING_DIST as f64 * self.slider_mult / self.tick_rate;
f64::from(OSU_BASE_SCORING_DIST) * self.slider_mult / self.tick_rate;
// * The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll.
let taiko_vel = slider_scoring_point_dist * self.tick_rate;
*duration = (dist / taiko_vel * beat_len) as u32;
let osu_vel = taiko_vel * (1000.0_f32 as f64 / beat_len);
let osu_vel = taiko_vel * (f64::from(1000.0_f32) / beat_len);
// * osu-stable always uses the speed-adjusted beatlength to determine the osu! velocity, but only uses it for conversion if beatmap version < 8
if self.version >= 8 {
@@ -129,7 +131,7 @@ impl Beatmap {
}
// * If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
*tick_spacing = (beat_len / self.tick_rate).min(*duration as f64 / spans);
*tick_spacing = (beat_len / self.tick_rate).min(f64::from(*duration) / spans);
*tick_spacing > 0.0 && dist / osu_vel * 1000.0 < 2.0 * beat_len
}
@@ -144,7 +146,7 @@ struct SliderParams<'c> {
}
impl<'c> SliderParams<'c> {
fn new(start_time: f64, repeats: usize, curve: &'c Curve<'c>) -> Self {
const fn new(start_time: f64, repeats: usize, curve: &'c Curve<'c>) -> Self {
Self {
curve,
repeats,
+2 -2
View File
@@ -104,7 +104,7 @@ impl BeatmapExt for Beatmap {
let attrs = self.attributes().mods(mods).build();
let scaling_factor = ScalingFactor::new(attrs.cs);
let hr = mods.hr();
let time_preempt = (attrs.hit_windows.ar * attrs.clock_rate) as f32 as f64;
let time_preempt = f64::from((attrs.hit_windows.ar * attrs.clock_rate) as f32);
let mut attrs = OsuDifficultyAttributes::default();
crate::osu::create_osu_objects(
@@ -149,7 +149,7 @@ impl BeatmapExt for Beatmap {
.collect();
let half_catcher_width =
(calculate_catch_width(attrs.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE) as f64;
f64::from(calculate_catch_width(attrs.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE);
let mut last_direction = 0;
let mut last_excess = half_catcher_width;
+2 -2
View File
@@ -89,7 +89,7 @@ impl Beatmap {
.or_else(|| self.timing_points.last().map(|t| t.time))
.unwrap_or(0.0);
/// Maps beat_len to a cumulative duration
/// Maps `beat_len` to a cumulative duration
#[derive(Debug)]
struct BeatLenDuration {
last_time: f64,
@@ -221,7 +221,7 @@ impl Beatmap {
slider_mult: self.slider_mult,
tick_rate: self.tick_rate,
hit_objects: Vec::with_capacity(self.hit_objects.len()),
sounds: Vec::with_capacity((with_sounds as usize) * self.sounds.len()),
sounds: Vec::with_capacity((usize::from(with_sounds)) * self.sounds.len()),
timing_points: self.timing_points.clone(),
difficulty_points: self.difficulty_points.clone(),
effect_points: self.effect_points.clone(),
+2
View File
@@ -15,6 +15,8 @@ pub enum GameMode {
impl From<u8> for GameMode {
#[inline]
fn from(mode: u8) -> Self {
// `0` will happen most commonly so it should be the first branch
#[allow(clippy::match_same_arms)]
match mode {
0 => Self::Osu,
1 => Self::Taiko,
+2 -2
View File
@@ -16,7 +16,7 @@ pub struct SortedVec<T> {
impl<T> SortedVec<T> {
/// Constructs a new, empty `SortedVec<T>`.
#[inline]
pub fn new() -> Self {
pub const fn new() -> Self {
Self { inner: Vec::new() }
}
@@ -75,7 +75,7 @@ impl<T: Sortable> SortedVec<T> {
/// Push a new value into the sorted list based on [`<T as Sortable>::push`](Sortable::push).
pub fn push(&mut self, value: T) {
<T as Sortable>::push(value, self)
<T as Sortable>::push(value, self);
}
}
+4 -4
View File
@@ -20,7 +20,7 @@ pub struct CatchObject {
impl CatchObject {
#[inline]
pub(crate) fn new((pos, time): (Pos2, f64)) -> Self {
pub(crate) const fn new((pos, time): (Pos2, f64)) -> Self {
Self {
pos: pos.x,
time,
@@ -70,7 +70,7 @@ impl CatchObject {
let next_x = next.pos;
let curr_x = self.pos;
let this_direction = (next_x > curr_x) as i8 * 2 - 1;
let this_direction = i8::from(next_x > curr_x) * 2 - 1;
let time_to_next = next.time - self.time - 1000.0 / 60.0 / 4.0;
let sub = if *last_direction == this_direction {
@@ -79,7 +79,7 @@ impl CatchObject {
half_catcher_width
};
let dist_to_next = (next_x - curr_x).abs() as f64 - sub;
let dist_to_next = f64::from((next_x - curr_x).abs()) - sub;
let hyper_dist = (time_to_next * BASE_SPEED - dist_to_next) as f32;
if hyper_dist < 0.0 {
@@ -87,7 +87,7 @@ impl CatchObject {
*last_excess = half_catcher_width;
} else {
self.hyper_dist = hyper_dist;
*last_excess = (hyper_dist as f64).clamp(0.0, half_catcher_width);
*last_excess = f64::from(hyper_dist).clamp(0.0, half_catcher_width);
}
*last_direction = this_direction;
+3 -3
View File
@@ -121,7 +121,7 @@ impl FruitOrJuice {
slider_objects.extend(&params.ticks);
for span_idx in 1..=*repeats {
let progress = (span_idx % 2 == 1) as u8 as f64;
let progress = f64::from(u8::from(span_idx % 2 == 1));
let pos = h.pos + curve.position_at(progress);
let time_offset = span_duration * span_idx as f64;
@@ -153,11 +153,11 @@ impl FruitOrJuice {
}
// Slider tail
let progress = (*repeats % 2 == 0) as u8 as f64;
let progress = f64::from(u8::from(*repeats % 2 == 0));
let pos = h.pos + curve.position_at(progress);
slider_objects.push((pos, h.start_time + total_duration));
let new_fruits = 2 + (tick_dist > 0.0) as usize * *repeats;
let new_fruits = 2 + usize::from(tick_dist > 0.0) * *repeats;
params.attributes.n_fruits += new_fruits;
params.attributes.n_droplets += slider_objects.len() - new_fruits;
+3 -3
View File
@@ -62,7 +62,7 @@ impl<'map> CatchGradualDifficulty<'map> {
Self { map, inner }
}
pub(crate) fn idx(&self) -> usize {
pub(crate) const fn idx(&self) -> usize {
self.inner.idx
}
}
@@ -96,7 +96,7 @@ impl CatchOwnedGradualDifficulty {
}
#[allow(unused)]
pub(crate) fn idx(&self) -> usize {
pub(crate) const fn idx(&self) -> usize {
self.inner.idx
}
}
@@ -181,7 +181,7 @@ impl CatchGradualDifficultyInner {
let hit_objects = CatchObjectIter::new(mods, attributes);
let half_catcher_width =
(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE) as f64;
f64::from(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE);
let last_direction = 0;
let last_excess = half_catcher_width;
+12 -10
View File
@@ -50,6 +50,7 @@ const CATCHER_SIZE: f32 = 106.75;
/// println!("Stars: {}", difficulty_attrs.stars);
/// ```
#[derive(Clone, Debug)]
#[must_use]
pub struct CatchStars<'map> {
map: &'map Beatmap,
mods: u32,
@@ -60,7 +61,7 @@ pub struct CatchStars<'map> {
impl<'map> CatchStars<'map> {
/// Create a new difficulty calculator for osu!catch maps.
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
pub const fn new(map: &'map Beatmap) -> Self {
Self {
map,
mods: 0,
@@ -73,7 +74,7 @@ impl<'map> CatchStars<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -88,7 +89,7 @@ impl<'map> CatchStars<'map> {
[`CatchGradualDifficultyAttributes`](crate::catch::CatchGradualDifficulty)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -98,7 +99,7 @@ impl<'map> CatchStars<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -147,6 +148,7 @@ impl CatchStrains {
}
}
#[allow(clippy::needless_pass_by_value)]
fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttributes) {
let CatchStars {
map,
@@ -183,7 +185,7 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
// Hyper dash business
let half_catcher_width =
(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE) as f64;
f64::from(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE);
let mut last_direction = 0;
let mut last_excess = half_catcher_width;
@@ -192,7 +194,7 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
let (mut prev, curr) = match (hit_objects.next(), hit_objects.next()) {
(Some(prev), Some(curr)) => (prev, curr),
(Some(_), None) | (None, None) => return (movement, params.attributes),
(_, None) => return (movement, params.attributes),
(None, Some(_)) => unreachable!(),
};
@@ -274,7 +276,7 @@ pub struct CatchDifficultyAttributes {
impl CatchDifficultyAttributes {
/// Return the maximum combo.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.n_fruits + self.n_droplets
}
@@ -297,19 +299,19 @@ pub struct CatchPerformanceAttributes {
impl CatchPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
pub const fn stars(&self) -> f64 {
self.difficulty.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f64 {
pub const fn pp(&self) -> f64 {
self.pp
}
/// Return the maximum combo of the map.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.difficulty.max_combo()
}
}
+9 -7
View File
@@ -90,15 +90,15 @@ impl Movement {
let dist_moved = pos - last_player_pos;
let weighted_strain_time = current.strain_time + 13.0 + (3.0 / current.clock_rate);
let mut dist_addition = (dist_moved.abs().powf(1.3) / 510.0) as f64;
let mut dist_addition = f64::from(dist_moved.abs().powf(1.3) / 510.0);
if dist_moved.abs() > 0.1 {
if self.last_distance_moved.abs() > 0.1
&& dist_moved.signum() != self.last_distance_moved.signum()
{
let bonus_factor = (dist_moved.abs().min(50.0) / 50.0) as f64;
let bonus_factor = f64::from(dist_moved.abs().min(50.0) / 50.0);
let anti_flow_factor =
(self.last_distance_moved.abs().min(70.0) / 70.0).max(0.38) as f64;
f64::from((self.last_distance_moved.abs().min(70.0) / 70.0).max(0.38));
dist_addition += DIRECTION_CHANGE_BONUS / (self.last_strain_time + 16.0).sqrt()
* bonus_factor
@@ -106,14 +106,16 @@ impl Movement {
* (1.0 - (weighted_strain_time / 1000.0).powi(3)).max(0.0);
}
dist_addition += (12.5 * dist_moved.abs().min(NORMALIZED_HITOBJECT_RADIUS * 2.0)
/ (NORMALIZED_HITOBJECT_RADIUS * 6.0)) as f64
/ weighted_strain_time.sqrt();
dist_addition += f64::from(
12.5 * dist_moved.abs().min(NORMALIZED_HITOBJECT_RADIUS * 2.0)
/ (NORMALIZED_HITOBJECT_RADIUS * 6.0),
) / weighted_strain_time.sqrt();
}
let mut edge_dash_bonus = 0.0;
if current.last.hyper_dist <= 20.0 {
#[allow(clippy::if_not_else)]
if !current.last.hyper_dash {
edge_dash_bonus += 5.7;
} else {
@@ -122,7 +124,7 @@ impl Movement {
dist_addition *= 1.0
+ edge_dash_bonus
* ((20.0 - current.last.hyper_dist) / 20.0) as f64
* f64::from((20.0 - current.last.hyper_dist) / 20.0)
* ((current.strain_time * current.clock_rate).min(265.0) / 265.0).powf(1.5);
}
+19 -15
View File
@@ -36,6 +36,7 @@ use std::cmp::Ordering;
/// ```
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
#[must_use]
pub struct CatchPP<'map> {
pub(crate) map_or_attrs: MapOrElse<MapRef<'map>, CatchDifficultyAttributes>,
pub(crate) mods: u32,
@@ -87,7 +88,7 @@ impl<'map> CatchPP<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -95,7 +96,7 @@ impl<'map> CatchPP<'map> {
/// Specify the max combo of the play.
#[inline]
pub fn combo(mut self, combo: usize) -> Self {
pub const fn combo(mut self, combo: usize) -> Self {
self.combo = Some(combo);
self
@@ -103,7 +104,7 @@ impl<'map> CatchPP<'map> {
/// Specify the amount of fruits of a play i.e. n300.
#[inline]
pub fn fruits(mut self, n_fruits: usize) -> Self {
pub const fn fruits(mut self, n_fruits: usize) -> Self {
self.n_fruits = Some(n_fruits);
self
@@ -111,7 +112,7 @@ impl<'map> CatchPP<'map> {
/// Specify the amount of droplets of a play i.e. n100.
#[inline]
pub fn droplets(mut self, n_droplets: usize) -> Self {
pub const fn droplets(mut self, n_droplets: usize) -> Self {
self.n_droplets = Some(n_droplets);
self
@@ -119,15 +120,15 @@ impl<'map> CatchPP<'map> {
/// Specify the amount of tiny droplets of a play i.e. n50.
#[inline]
pub fn tiny_droplets(mut self, n_tiny_droplets: usize) -> Self {
pub const fn tiny_droplets(mut self, n_tiny_droplets: usize) -> Self {
self.n_tiny_droplets = Some(n_tiny_droplets);
self
}
/// Specify the amount of tiny droplet misses of a play i.e. n_katu.
/// Specify the amount of tiny droplet misses of a play i.e. `n_katu`.
#[inline]
pub fn tiny_droplet_misses(mut self, n_tiny_droplet_misses: usize) -> Self {
pub const fn tiny_droplet_misses(mut self, n_tiny_droplet_misses: usize) -> Self {
self.n_tiny_droplet_misses = Some(n_tiny_droplet_misses);
self
@@ -135,7 +136,7 @@ impl<'map> CatchPP<'map> {
/// Specify the amount of fruit / droplet misses of the play.
#[inline]
pub fn misses(mut self, n_misses: usize) -> Self {
pub const fn misses(mut self, n_misses: usize) -> Self {
self.n_misses = Some(n_misses);
self
@@ -150,7 +151,7 @@ impl<'map> CatchPP<'map> {
[`CatchGradualPerformanceAttributes`](crate::catch::CatchGradualPerformance)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -160,7 +161,7 @@ impl<'map> CatchPP<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -168,7 +169,8 @@ impl<'map> CatchPP<'map> {
/// Provide parameters through an [`CatchScoreState`].
#[inline]
pub fn state(mut self, state: CatchScoreState) -> Self {
#[allow(clippy::needless_pass_by_value)]
pub const fn state(mut self, state: CatchScoreState) -> Self {
let CatchScoreState {
max_combo,
n_fruits,
@@ -198,6 +200,7 @@ impl<'map> CatchPP<'map> {
}
/// Create the [`CatchScoreState`] that will be used for performance calculation.
#[allow(clippy::too_many_lines)]
pub fn generate_state(&mut self) -> CatchScoreState {
let attrs = match self.map_or_attrs {
MapOrElse::Map(ref map) => {
@@ -297,6 +300,7 @@ impl<'map> CatchPP<'map> {
}
};
#[allow(clippy::single_match_else)]
match (self.n_tiny_droplets, self.n_tiny_droplet_misses) {
(Some(n_tiny_droplets), Some(n_tiny_droplet_misses)) => match self.acc {
Some(acc) => {
@@ -376,7 +380,7 @@ impl<'map> CatchPP<'map> {
///
/// [`OsuDifficultyAttributes`]: crate::osu::OsuDifficultyAttributes
#[inline]
pub fn try_from_osu(osu: OsuPP<'map>) -> Option<Self> {
pub const fn try_from_osu(osu: OsuPP<'map>) -> Option<Self> {
let OsuPP {
map_or_attrs,
mods,
@@ -449,7 +453,7 @@ impl CatchPPInner {
// Longer maps are worth more
let len_bonus = 0.95
+ 0.3 * (combo_hits as f64 / 2500.0).min(1.0)
+ (combo_hits > 2500) as u8 as f64 * (combo_hits as f64 / 2500.0).log10() * 0.475;
+ f64::from(u8::from(combo_hits > 2500)) * (combo_hits as f64 / 2500.0).log10() * 0.475;
pp *= len_bonus;
@@ -467,7 +471,7 @@ impl CatchPPInner {
let ar = attributes.ar;
let mut ar_factor = 1.0;
if ar > 9.0 {
ar_factor += 0.1 * (ar - 9.0) + (ar > 10.0) as u8 as f64 * 0.1 * (ar - 10.0);
ar_factor += 0.1 * (ar - 9.0) + f64::from(u8::from(ar > 10.0)) * 0.1 * (ar - 10.0);
} else if ar < 8.0 {
ar_factor += 0.025 * (8.0 - ar);
}
@@ -501,7 +505,7 @@ impl CatchPPInner {
}
}
fn combo_hits(&self) -> usize {
const fn combo_hits(&self) -> usize {
self.state.n_fruits + self.state.n_droplets + self.state.n_misses
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ impl CatchScoreState {
/// Return the total amount of hits by adding everything up.
#[inline]
pub fn total_hits(&self) -> usize {
pub const fn total_hits(&self) -> usize {
self.n_fruits
+ self.n_droplets
+ self.n_tiny_droplets
+10 -8
View File
@@ -147,7 +147,7 @@ impl<'bufs> Curve<'bufs> {
}
// * The current vertex ends the segment
let segment_vertices = &vertices[start..i + 1];
let segment_vertices = &vertices[start..=i];
let segment_kind = points[start].kind.unwrap_or(PathType::Linear);
Self::calculate_subpath(path, segment_vertices, segment_kind, bezier);
@@ -176,14 +176,16 @@ impl<'bufs> Curve<'bufs> {
cumulative_len.push(0.0);
let length_iter = path.iter().zip(path.iter().skip(1)).map(|(&curr, &next)| {
calculated_len += (next - curr).length() as f64;
calculated_len += f64::from((next - curr).length());
calculated_len
});
cumulative_len.extend(length_iter);
if let Some(expected_len) = expected_len.filter(|&len| calculated_len != len) {
if let Some(expected_len) =
expected_len.filter(|&len| (calculated_len - len).abs() >= f64::EPSILON)
{
// * In osu-stable, if the last two control points of a slider are equal, extension is not performed
let condition_opt = points
.len()
@@ -254,7 +256,7 @@ impl<'bufs> Curve<'bufs> {
}
}
Self::approximate_bezier(path, sub_points, bufs)
Self::approximate_bezier(path, sub_points, bufs);
}
}
}
@@ -290,7 +292,7 @@ impl<'bufs> Curve<'bufs> {
}
fn approximate_linear(path: &mut Vec<Pos2>, points: &[Pos2]) {
path.extend(points)
path.extend(points);
}
fn approximate_circular_arc(path: &mut Vec<Pos2>, a: Pos2, b: Pos2, c: Pos2) -> bool {
@@ -313,7 +315,7 @@ impl<'bufs> Curve<'bufs> {
if divisor.abs() <= f32::EPSILON {
2
} else {
((pr.theta_range / divisor as f64).ceil() as usize).max(2)
((pr.theta_range / f64::from(divisor)).ceil() as usize).max(2)
}
};
@@ -509,8 +511,8 @@ impl<'bufs> Curve<'bufs> {
let radius = d_a.length();
let theta_start = (d_a.y as f64).atan2(d_a.x as f64);
let mut theta_end = (d_c.y as f64).atan2(d_c.x as f64);
let theta_start = f64::from(d_a.y).atan2(f64::from(d_a.x));
let mut theta_end = f64::from(d_c.y).atan2(f64::from(d_c.x));
while theta_end < theta_start {
theta_end += 2.0 * PI;
+21 -7
View File
@@ -1,6 +1,8 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(
#![warn(
clippy::all,
clippy::pedantic,
clippy::missing_const_for_fn,
nonstandard_style,
rust_2018_idioms,
unused,
@@ -9,6 +11,18 @@
missing_docs,
rustdoc::broken_intra_doc_links
)]
#![allow(
clippy::must_use_candidate,
clippy::inline_always,
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::explicit_iter_loop,
clippy::items_after_statements,
clippy::missing_errors_doc,
clippy::module_name_repetitions
)]
//! A standalone crate to calculate star ratings and performance points for all [osu!](https://osu.ppy.sh/home) gamemodes.
//!
@@ -249,7 +263,7 @@ pub enum Strains {
impl Strains {
/// Time in ms inbetween two strains.
#[inline]
pub fn section_len(&self) -> f64 {
pub const fn section_len(&self) -> f64 {
match self {
Strains::Osu(strains) => strains.section_len,
Strains::Taiko(strains) => strains.section_len,
@@ -287,7 +301,7 @@ pub enum DifficultyAttributes {
impl DifficultyAttributes {
/// The star value.
#[inline]
pub fn stars(&self) -> f64 {
pub const fn stars(&self) -> f64 {
match self {
Self::Osu(attrs) => attrs.stars,
Self::Taiko(attrs) => attrs.stars,
@@ -298,7 +312,7 @@ impl DifficultyAttributes {
/// The maximum combo of the map.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
match self {
Self::Osu(attrs) => attrs.max_combo,
Self::Taiko(attrs) => attrs.max_combo,
@@ -358,7 +372,7 @@ pub enum PerformanceAttributes {
impl PerformanceAttributes {
/// The pp value.
#[inline]
pub fn pp(&self) -> f64 {
pub const fn pp(&self) -> f64 {
match self {
Self::Osu(attrs) => attrs.pp,
Self::Taiko(attrs) => attrs.pp,
@@ -369,7 +383,7 @@ impl PerformanceAttributes {
/// The star value.
#[inline]
pub fn stars(&self) -> f64 {
pub const fn stars(&self) -> f64 {
match self {
Self::Osu(attrs) => attrs.stars(),
Self::Taiko(attrs) => attrs.stars(),
@@ -391,7 +405,7 @@ impl PerformanceAttributes {
#[inline]
/// The maximum combo of the map.
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
match self {
Self::Osu(attrs) => attrs.difficulty.max_combo,
Self::Taiko(attrs) => attrs.difficulty.max_combo,
+4 -4
View File
@@ -63,7 +63,7 @@ impl<'map> ManiaGradualDifficulty<'map> {
Self { map, inner }
}
pub(crate) fn idx(&self) -> usize {
pub(crate) const fn idx(&self) -> usize {
self.inner.idx
}
}
@@ -123,7 +123,7 @@ impl ManiaOwnedGradualDifficulty {
}
#[allow(unused)]
pub(crate) fn idx(&self) -> usize {
pub(crate) const fn idx(&self) -> usize {
self.inner.idx
}
}
@@ -257,7 +257,7 @@ impl ManiaGradualDifficultyInner {
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
const fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
@@ -287,7 +287,7 @@ impl ManiaGradualDifficultyInner {
self.next(hit_objects)
}
fn len(&self) -> usize {
const fn len(&self) -> usize {
self.diff_objects.len() + 1 - self.idx
}
}
+13 -12
View File
@@ -50,6 +50,7 @@ const STAR_SCALING_FACTOR: f64 = 0.018;
/// println!("Stars: {}", difficulty_attrs.stars);
/// ```
#[derive(Clone, Debug)]
#[must_use]
pub struct ManiaStars<'map> {
map: Cow<'map, Beatmap>,
mods: u32,
@@ -78,7 +79,7 @@ impl<'map> ManiaStars<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -93,7 +94,7 @@ impl<'map> ManiaStars<'map> {
[`ManiaGradualDifficultyAttributes`](crate::mania::ManiaGradualDifficulty)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -103,7 +104,7 @@ impl<'map> ManiaStars<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -113,7 +114,7 @@ impl<'map> ManiaStars<'map> {
///
/// This only needs to be specified if the map was converted manually beforehand.
#[inline]
pub fn is_convert(mut self, is_convert: bool) -> Self {
pub const fn is_convert(mut self, is_convert: bool) -> Self {
self.is_convert = is_convert;
self
@@ -250,19 +251,19 @@ pub struct ManiaDifficultyAttributes {
impl ManiaDifficultyAttributes {
/// Return the maximum combo.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.max_combo
}
/// Return the amount of hitobjects.
#[inline]
pub fn n_objects(&self) -> usize {
pub const fn n_objects(&self) -> usize {
self.n_objects
}
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
#[inline]
pub fn is_convert(&self) -> bool {
pub const fn is_convert(&self) -> bool {
self.is_convert
}
@@ -287,31 +288,31 @@ pub struct ManiaPerformanceAttributes {
impl ManiaPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
pub const fn stars(&self) -> f64 {
self.difficulty.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f64 {
pub const fn pp(&self) -> f64 {
self.pp
}
/// Return the maximum combo of the map.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.difficulty.max_combo
}
/// Return the amount of hitobjects.
#[inline]
pub fn n_objects(&self) -> usize {
pub const fn n_objects(&self) -> usize {
self.difficulty.n_objects
}
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
#[inline]
pub fn is_convert(&self) -> bool {
pub const fn is_convert(&self) -> bool {
self.difficulty.is_convert
}
}
+41 -35
View File
@@ -41,6 +41,7 @@ use crate::{
/// ```
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
#[must_use]
pub struct ManiaPP<'map> {
map_or_attrs: MapOrElse<Cow<'map, Beatmap>, ManiaDifficultyAttributes>,
is_convert_overwrite: Option<bool>,
@@ -98,7 +99,7 @@ impl<'map> ManiaPP<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -113,7 +114,7 @@ impl<'map> ManiaPP<'map> {
[`ManiaGradualPerformanceAttributes`](crate::mania::ManiaGradualPerformance)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -123,7 +124,7 @@ impl<'map> ManiaPP<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -142,7 +143,7 @@ impl<'map> ManiaPP<'map> {
///
/// Defauls to [`HitResultPriority::BestCase`].
#[inline]
pub fn hitresult_priority(mut self, priority: HitResultPriority) -> Self {
pub const fn hitresult_priority(mut self, priority: HitResultPriority) -> Self {
self.hitresult_priority = Some(priority);
self
@@ -150,7 +151,7 @@ impl<'map> ManiaPP<'map> {
/// Specify the amount of 320s of a play.
#[inline]
pub fn n320(mut self, n320: usize) -> Self {
pub const fn n320(mut self, n320: usize) -> Self {
self.n320 = Some(n320);
self
@@ -158,7 +159,7 @@ impl<'map> ManiaPP<'map> {
/// Specify the amount of 300s of a play.
#[inline]
pub fn n300(mut self, n300: usize) -> Self {
pub const fn n300(mut self, n300: usize) -> Self {
self.n300 = Some(n300);
self
@@ -166,7 +167,7 @@ impl<'map> ManiaPP<'map> {
/// Specify the amount of 200s of a play.
#[inline]
pub fn n200(mut self, n200: usize) -> Self {
pub const fn n200(mut self, n200: usize) -> Self {
self.n200 = Some(n200);
self
@@ -174,7 +175,7 @@ impl<'map> ManiaPP<'map> {
/// Specify the amount of 100s of a play.
#[inline]
pub fn n100(mut self, n100: usize) -> Self {
pub const fn n100(mut self, n100: usize) -> Self {
self.n100 = Some(n100);
self
@@ -182,7 +183,7 @@ impl<'map> ManiaPP<'map> {
/// Specify the amount of 50s of a play.
#[inline]
pub fn n50(mut self, n50: usize) -> Self {
pub const fn n50(mut self, n50: usize) -> Self {
self.n50 = Some(n50);
self
@@ -190,7 +191,7 @@ impl<'map> ManiaPP<'map> {
/// Specify the amount of misses of a play.
#[inline]
pub fn n_misses(mut self, n_misses: usize) -> Self {
pub const fn n_misses(mut self, n_misses: usize) -> Self {
self.n_misses = Some(n_misses);
self
@@ -200,7 +201,7 @@ impl<'map> ManiaPP<'map> {
///
/// This only needs to be specified if the map was converted manually beforehand.
#[inline]
pub fn is_convert(mut self, is_convert: bool) -> Self {
pub const fn is_convert(mut self, is_convert: bool) -> Self {
self.is_convert_overwrite = Some(is_convert);
self
@@ -208,7 +209,8 @@ impl<'map> ManiaPP<'map> {
/// Provide parameters through an [`ManiaScoreState`].
#[inline]
pub fn state(mut self, state: ManiaScoreState) -> Self {
#[allow(clippy::needless_pass_by_value)]
pub const fn state(mut self, state: ManiaScoreState) -> Self {
let ManiaScoreState {
n320,
n300,
@@ -229,21 +231,21 @@ impl<'map> ManiaPP<'map> {
}
/// Create the [`ManiaScoreState`] that will be used for performance calculation.
#[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn generate_state(&mut self) -> ManiaScoreState {
let n_objects = match self.passed_objects {
Some(passed) => passed,
None => {
let attrs = match self.map_or_attrs {
MapOrElse::Map(ref map) => {
let attrs = self.generate_attributes(map);
let n_objects = if let Some(passed) = self.passed_objects {
passed
} else {
let attrs = match self.map_or_attrs {
MapOrElse::Map(ref map) => {
let attrs = self.generate_attributes(map);
self.map_or_attrs.else_or_insert(attrs)
}
MapOrElse::Else(ref attrs) => attrs,
};
self.map_or_attrs.else_or_insert(attrs)
}
MapOrElse::Else(ref attrs) => attrs,
};
attrs.n_objects
}
attrs.n_objects
};
let priority = self.hitresult_priority.unwrap_or_default();
@@ -274,16 +276,16 @@ impl<'map> ManiaPP<'map> {
// All but one hitresults given
(None, Some(_), Some(_), Some(_), Some(_)) => {
n320 = n_objects.saturating_sub(n300 + n200 + n100 + n50 + n_misses)
n320 = n_objects.saturating_sub(n300 + n200 + n100 + n50 + n_misses);
}
(Some(_), None, Some(_), Some(_), Some(_)) => {
n300 = n_objects.saturating_sub(n320 + n200 + n100 + n50 + n_misses)
n300 = n_objects.saturating_sub(n320 + n200 + n100 + n50 + n_misses);
}
(Some(_), Some(_), None, Some(_), Some(_)) => {
n200 = n_objects.saturating_sub(n320 + n300 + n100 + n50 + n_misses)
n200 = n_objects.saturating_sub(n320 + n300 + n100 + n50 + n_misses);
}
(Some(_), Some(_), Some(_), None, Some(_)) => {
n100 = n_objects.saturating_sub(n320 + n300 + n200 + n50 + n_misses)
n100 = n_objects.saturating_sub(n320 + n300 + n200 + n50 + n_misses);
}
(Some(_), Some(_), Some(_), Some(_), None) => {
n50 = n_objects.saturating_sub(n320 + n300 + n200 + n100 + n_misses);
@@ -855,7 +857,7 @@ impl ManiaPpInner {
* (1.0 + 0.1 * (self.total_hits() / 1500.0).min(1.0))
}
fn total_hits(&self) -> f64 {
const fn total_hits(&self) -> f64 {
self.state.total_hits() as f64
}
@@ -1010,7 +1012,11 @@ mod tests {
///
/// Very slow but accurate.
/// Only slight optimizations have been applied so that it doesn't run unreasonably long.
#[allow(clippy::too_many_arguments)]
#[allow(
clippy::too_many_arguments,
clippy::too_many_lines,
clippy::similar_names
)]
fn brute_force_best(
acc: f64,
n320: Option<usize>,
@@ -1033,11 +1039,11 @@ mod tests {
let n_remaining = N_OBJECTS - n_misses;
let multiple_given = (n320.is_some() as usize
+ n300.is_some() as usize
+ n200.is_some() as usize
+ n100.is_some() as usize
+ n50.is_some() as usize)
let multiple_given = (usize::from(n320.is_some())
+ usize::from(n300.is_some())
+ usize::from(n200.is_some())
+ usize::from(n100.is_some())
+ usize::from(n50.is_some()))
> 1;
let max_left = N_OBJECTS
+1 -1
View File
@@ -24,7 +24,7 @@ impl ManiaScoreState {
/// Return the total amount of hits by adding everything up.
#[inline]
pub fn total_hits(&self) -> usize {
pub const fn total_hits(&self) -> usize {
self.n320 + self.n300 + self.n200 + self.n100 + self.n50 + self.n_misses
}
+2 -1
View File
@@ -3,6 +3,7 @@ use crate::{mania::difficulty_object::ManiaDifficultyObject, util::CompactVec};
use super::{previous, Skill, StrainDecaySkill, StrainSkill};
#[derive(Clone, Debug)]
#[allow(clippy::struct_field_names)]
pub(crate) struct Strain {
start_times: Vec<f64>,
end_times: Vec<f64>,
@@ -45,7 +46,7 @@ impl Strain {
impl Skill for Strain {
#[inline]
fn process(&mut self, curr: &ManiaDifficultyObject, diff_objects: &[ManiaDifficultyObject]) {
<Self as StrainSkill>::process(self, curr, diff_objects)
<Self as StrainSkill>::process(self, curr, diff_objects);
}
#[inline]
+21 -20
View File
@@ -30,7 +30,7 @@ impl<'h> OsuDifficultyObject<'h> {
let delta_time = (base.start_time - last.start_time) / clock_rate;
// * Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects.
let strain_time = delta_time.max(Self::MIN_DELTA_TIME as f64);
let strain_time = delta_time.max(f64::from(Self::MIN_DELTA_TIME));
Self {
start_time,
@@ -94,6 +94,7 @@ impl Distances {
///
/// By taking in [`Pin<&mut OsuObject>`](Pin), we imply that the argument will be
/// modified but it won't be moved.
#[allow(clippy::similar_names)]
pub(crate) fn new(
base: &mut Pin<&mut OsuObject>,
last: &OsuObject,
@@ -113,11 +114,11 @@ impl Distances {
Self {
// * Bonus for repeat sliders until a better per nested object strain system can be achieved.
travel_dist: (lazy_travel_dist
* (1.0 + repeat_count as f64 / 2.5).powf(1.0 / 2.5) as f32)
as f64,
travel_dist: f64::from(
lazy_travel_dist * (1.0 + repeat_count as f64 / 2.5).powf(1.0 / 2.5) as f32,
),
travel_time: (base.lazy_travel_time() / clock_rate)
.max(OsuDifficultyObject::MIN_DELTA_TIME as f64),
.max(f64::from(OsuDifficultyObject::MIN_DELTA_TIME)),
lazy_travel_dist,
..Default::default()
}
@@ -136,17 +137,17 @@ impl Distances {
let last_cursor_pos = Self::get_end_cursor_pos(last);
this.lazy_jump_dist = (base.stacked_pos() * scaling_factor
- last_cursor_pos * scaling_factor)
.length() as f64;
this.lazy_jump_dist = f64::from(
(base.stacked_pos() * scaling_factor - last_cursor_pos * scaling_factor).length(),
);
this.min_jump_time = strain_time;
this.min_jump_dist = this.lazy_jump_dist;
if let OsuObjectKind::Slider(slider) = &last.kind {
let last_travel_time = (last.lazy_travel_time() / clock_rate)
.max(OsuDifficultyObject::MIN_DELTA_TIME as f64);
this.min_jump_time =
(strain_time - last_travel_time).max(OsuDifficultyObject::MIN_DELTA_TIME as f64);
.max(f64::from(OsuDifficultyObject::MIN_DELTA_TIME));
this.min_jump_time = (strain_time - last_travel_time)
.max(f64::from(OsuDifficultyObject::MIN_DELTA_TIME));
// * There are two types of slider-to-object patterns to consider in order
// * to better approximate the real movement a player will take to jump between the hitobjects.
@@ -176,8 +177,8 @@ impl Distances {
let tail_jump_dist = (stacked_tail_pos - base.stacked_pos()).length() * scaling_factor;
let diff = (Self::MAXIMUM_SLIDER_RADIUS - Self::ASSUMED_SLIDER_RADIUS) as f64;
let min = (tail_jump_dist - Self::MAXIMUM_SLIDER_RADIUS) as f64;
let diff = f64::from(Self::MAXIMUM_SLIDER_RADIUS - Self::ASSUMED_SLIDER_RADIUS);
let min = f64::from(tail_jump_dist - Self::MAXIMUM_SLIDER_RADIUS);
// "attributes on expressions are experimental see issue #15701 https://github.com/rust-lang/rust/issues/15701"
// rust pls...
@@ -192,8 +193,8 @@ impl Distances {
let v1 = last_last_cursor_pos - last.stacked_pos();
let v2 = base.stacked_pos() - last_cursor_pos;
let dot = v1.dot(v2) as f64;
let det = (v1.x * v2.y - v1.y * v2.x) as f64;
let dot = f64::from(v1.dot(v2));
let det = f64::from(v1.x * v2.y - v1.y * v2.x);
this.angle = Some(det.atan2(dot).abs());
}
@@ -208,16 +209,16 @@ impl Distances {
scaling_factor_: &ScalingFactor,
) -> f32 {
let mut curr_cursor_pos = pos + stack_offset;
let scaling_factor = Self::NORMALISED_RADIUS as f64 / scaling_factor_.radius as f64;
let scaling_factor = f64::from(Self::NORMALISED_RADIUS) / f64::from(scaling_factor_.radius);
let mut lazy_travel_dist: f32 = 0.0;
for (curr_movement_obj, i) in slider.nested_objects.iter().zip(1..) {
let mut curr_movement = (curr_movement_obj.pos + stack_offset) - curr_cursor_pos;
let mut curr_movement_len = scaling_factor * curr_movement.length() as f64;
let mut curr_movement_len = scaling_factor * f64::from(curr_movement.length());
// * Amount of movement required so that the cursor position needs to be updated.
let mut required_movement = Self::ASSUMED_SLIDER_RADIUS as f64;
let mut required_movement = f64::from(Self::ASSUMED_SLIDER_RADIUS);
if i == slider.nested_objects.len() {
// * The end of a slider has special aim rules due
@@ -234,10 +235,10 @@ impl Distances {
curr_movement = lazy_movement;
}
curr_movement_len = scaling_factor * curr_movement.length() as f64;
curr_movement_len = scaling_factor * f64::from(curr_movement.length());
} else if let NestedObjectKind::Repeat = curr_movement_obj.kind {
// * For a slider repeat, assume a tighter movement threshold to better assess repeat sliders.
required_movement = Self::NORMALISED_RADIUS as f64;
required_movement = f64::from(Self::NORMALISED_RADIUS);
}
if curr_movement_len > required_movement {
+6 -3
View File
@@ -70,10 +70,13 @@ struct NotClonable;
impl Debug for OsuGradualDifficulty {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("OsuGradualDifficulty")
.field("mods", &self.mods)
.field("idx", &self.idx)
.field("attrs", &self.attrs)
.field("diff_objects", &self.diff_objects)
.field("osu_objects", &"...")
.field("skills", &self.skills)
.field("_not_clonable", &"...")
.finish()
}
}
@@ -86,7 +89,7 @@ impl OsuGradualDifficulty {
let scaling_factor = ScalingFactor::new(map_attrs.cs);
let hr = mods.hr();
let hit_window = 2.0 * map_attrs.hit_windows.od;
let time_preempt = (map_attrs.hit_windows.ar * clock_rate) as f32 as f64;
let time_preempt = f64::from((map_attrs.hit_windows.ar * clock_rate) as f32);
// * Preempt time can go below 450ms. Normally, this is achieved via the DT mod
// * which uniformly speeds up all animations game wide regardless of AR.
@@ -171,7 +174,7 @@ impl OsuGradualDifficulty {
let delta_time = (curr.start_time - last.start_time) / clock_rate;
// * Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects.
let strain_time = delta_time.max(OsuDifficultyObject::MIN_DELTA_TIME as f64);
let strain_time = delta_time.max(f64::from(OsuDifficultyObject::MIN_DELTA_TIME));
let dists = Distances::new(
&mut curr,
@@ -361,7 +364,7 @@ mod osu_objects {
}
}
pub(super) fn is_empty(&self) -> bool {
pub(super) const fn is_empty(&self) -> bool {
self.objects.is_empty()
}
+18 -16
View File
@@ -58,6 +58,7 @@ const PLAYFIELD_BASE_SIZE: Pos2 = Pos2 { x: 512.0, y: 384.0 };
/// println!("Stars: {}", difficulty_attrs.stars);
/// ```
#[derive(Clone, Debug)]
#[must_use]
pub struct OsuStars<'map> {
pub(crate) map: &'map Beatmap,
pub(crate) mods: u32,
@@ -68,7 +69,7 @@ pub struct OsuStars<'map> {
impl<'map> OsuStars<'map> {
/// Create a new difficulty calculator for osu!standard maps.
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
pub const fn new(map: &'map Beatmap) -> Self {
Self {
map,
mods: 0,
@@ -92,7 +93,7 @@ impl<'map> OsuStars<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -107,7 +108,7 @@ impl<'map> OsuStars<'map> {
[`OsuGradualDifficulty`]."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -117,7 +118,7 @@ impl<'map> OsuStars<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -245,6 +246,7 @@ impl OsuStrains {
}
}
#[allow(clippy::needless_pass_by_value)]
fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
let OsuStars {
map,
@@ -260,7 +262,7 @@ fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
let scaling_factor = ScalingFactor::new(map_attrs.cs);
let hr = mods.hr();
let hit_window = 2.0 * map_attrs.hit_windows.od;
let time_preempt = (map_attrs.hit_windows.ar * clock_rate) as f32 as f64;
let time_preempt = f64::from((map_attrs.hit_windows.ar * clock_rate) as f32);
// * Preempt time can go below 450ms. Normally, this is achieved via the DT mod
// * which uniformly speeds up all animations game wide regardless of AR.
@@ -316,7 +318,7 @@ fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
let delta_time = (curr.start_time - last.start_time) / clock_rate;
// * Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects.
let strain_time = delta_time.max(OsuDifficultyObject::MIN_DELTA_TIME as f64);
let strain_time = delta_time.max(f64::from(OsuDifficultyObject::MIN_DELTA_TIME));
let dists = Distances::new(
&mut curr,
@@ -389,7 +391,7 @@ pub(crate) fn create_osu_objects(
hit_objects.extend(iter.map(|h| OsuObject::new(h, &mut params)));
}
let stack_threshold = time_preempt * map.stack_leniency as f64;
let stack_threshold = time_preempt * f64::from(map.stack_leniency);
if map.version >= 6 {
stacking(&mut hit_objects, stack_threshold);
@@ -443,9 +445,9 @@ fn stacking(hit_objects: &mut [OsuObject], stack_threshold: f64) {
if hit_objects[n].is_spinner() {
continue;
} else if hit_objects[obj_i_idx].start_time - hit_objects[n].end_time()
> stack_threshold
{
}
if hit_objects[obj_i_idx].start_time - hit_objects[n].end_time() > stack_threshold {
break; // * We are no longer within stacking range of the previous object.
}
@@ -589,13 +591,13 @@ pub struct OsuDifficultyAttributes {
impl OsuDifficultyAttributes {
/// Return the maximum combo.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.max_combo
}
/// Return the amount of hitobjects.
#[inline]
pub fn n_objects(&self) -> usize {
pub const fn n_objects(&self) -> usize {
self.n_circles + self.n_sliders + self.n_spinners
}
@@ -628,24 +630,24 @@ pub struct OsuPerformanceAttributes {
impl OsuPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
pub const fn stars(&self) -> f64 {
self.difficulty.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f64 {
pub const fn pp(&self) -> f64 {
self.pp
}
/// Return the maximum combo of the map.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.difficulty.max_combo
}
/// Return the amount of hitobjects.
#[inline]
pub fn n_objects(&self) -> usize {
pub const fn n_objects(&self) -> usize {
self.difficulty.n_objects()
}
}
+8 -6
View File
@@ -55,7 +55,7 @@ impl OsuSlider {
/// The amount of repeat points.
pub fn repeat_count(&self) -> usize {
self.nested_objects.iter().fold(0, |count, nested| {
count + matches!(nested.kind, NestedObjectKind::Repeat) as usize
count + usize::from(matches!(nested.kind, NestedObjectKind::Repeat))
})
}
@@ -120,6 +120,7 @@ pub(crate) struct ObjectParameters<'a> {
}
impl OsuObject {
#[allow(clippy::too_many_lines)]
pub(crate) fn new(h: &HitObject, params: &mut ObjectParameters<'_>) -> Self {
let ObjectParameters {
map,
@@ -195,6 +196,7 @@ impl OsuObject {
ticks.clear();
#[allow(clippy::if_not_else)]
let mut nested_objects = if tick_dist != 0.0 {
ticks.reserve((len / tick_dist) as usize);
let mut nested_objects =
@@ -221,7 +223,7 @@ impl OsuObject {
// Other spans
for span_idx in 1..=*repeats {
let progress = (span_idx % 2 == 1) as u8 as f64;
let progress = f64::from(u8::from(span_idx % 2 == 1));
let span_idx_f64 = span_idx as f64;
// Repeat point
@@ -269,7 +271,7 @@ impl OsuObject {
let final_span_end_time = (h.start_time + total_duration / 2.0)
.max(final_span_start_time + span_duration - LEGACY_LAST_TICK_OFFSET);
let progress = (*repeats % 2 == 0) as u8 as f64;
let progress = f64::from(u8::from(*repeats % 2 == 0));
let end_pos = curve.position_at(progress);
// * we need to use the LegacyLastTick here for compatibility reasons (difficulty).
@@ -347,7 +349,7 @@ impl OsuObject {
}
/// Endtime of the object.
pub fn end_time(&self) -> f64 {
pub const fn end_time(&self) -> f64 {
match &self.kind {
OsuObjectKind::Circle => self.start_time,
OsuObjectKind::Slider(slider) => slider.end_time,
@@ -445,7 +447,7 @@ impl OsuObject {
}
/// Applies stack offset, flips playfield for HR,
/// and adjusts slider tails and lazy_end_positions.
/// and adjusts slider tails and lazy end positions.
pub(crate) fn post_process(&mut self, hr: bool, scaling_factor: &ScalingFactor) {
self.stack_offset = scaling_factor.stack_offset(self.stack_height);
let pos = self.pos();
@@ -501,7 +503,7 @@ impl OsuObject {
}
if hr {
self.pos.y = PLAYFIELD_BASE_SIZE.y - pos.y
self.pos.y = PLAYFIELD_BASE_SIZE.y - pos.y;
}
}
}
+20 -15
View File
@@ -38,6 +38,7 @@ use crate::{
/// ```
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
#[must_use]
pub struct OsuPP<'map> {
pub(crate) map_or_attrs: MapOrElse<MapRef<'map>, OsuDifficultyAttributes>,
pub(crate) mods: u32,
@@ -104,7 +105,7 @@ impl<'map> OsuPP<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -112,7 +113,7 @@ impl<'map> OsuPP<'map> {
/// Specify the max combo of the play.
#[inline]
pub fn combo(mut self, combo: usize) -> Self {
pub const fn combo(mut self, combo: usize) -> Self {
self.combo = Some(combo);
self
@@ -122,7 +123,7 @@ impl<'map> OsuPP<'map> {
///
/// Defauls to [`HitResultPriority::BestCase`].
#[inline]
pub fn hitresult_priority(mut self, priority: HitResultPriority) -> Self {
pub const fn hitresult_priority(mut self, priority: HitResultPriority) -> Self {
self.hitresult_priority = Some(priority);
self
@@ -130,7 +131,7 @@ impl<'map> OsuPP<'map> {
/// Specify the amount of 300s of a play.
#[inline]
pub fn n300(mut self, n300: usize) -> Self {
pub const fn n300(mut self, n300: usize) -> Self {
self.n300 = Some(n300);
self
@@ -138,7 +139,7 @@ impl<'map> OsuPP<'map> {
/// Specify the amount of 100s of a play.
#[inline]
pub fn n100(mut self, n100: usize) -> Self {
pub const fn n100(mut self, n100: usize) -> Self {
self.n100 = Some(n100);
self
@@ -146,7 +147,7 @@ impl<'map> OsuPP<'map> {
/// Specify the amount of 50s of a play.
#[inline]
pub fn n50(mut self, n50: usize) -> Self {
pub const fn n50(mut self, n50: usize) -> Self {
self.n50 = Some(n50);
self
@@ -154,7 +155,7 @@ impl<'map> OsuPP<'map> {
/// Specify the amount of misses of a play.
#[inline]
pub fn n_misses(mut self, n_misses: usize) -> Self {
pub const fn n_misses(mut self, n_misses: usize) -> Self {
self.n_misses = Some(n_misses);
self
@@ -169,7 +170,7 @@ impl<'map> OsuPP<'map> {
[`OsuGradualPerformanceAttributes`](crate::osu::OsuGradualPerformance)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -179,7 +180,7 @@ impl<'map> OsuPP<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -187,7 +188,8 @@ impl<'map> OsuPP<'map> {
/// Provide parameters through an [`OsuScoreState`].
#[inline]
pub fn state(mut self, state: OsuScoreState) -> Self {
#[allow(clippy::needless_pass_by_value)]
pub const fn state(mut self, state: OsuScoreState) -> Self {
let OsuScoreState {
max_combo,
n300,
@@ -215,6 +217,7 @@ impl<'map> OsuPP<'map> {
}
/// Create the [`OsuScoreState`] that will be used for performance calculation.
#[allow(clippy::too_many_lines)]
pub fn generate_state(&mut self) -> OsuScoreState {
let attrs = match self.map_or_attrs {
MapOrElse::Map(ref map) => {
@@ -527,7 +530,7 @@ impl OsuPpInner {
let len_bonus = 0.95
+ 0.4 * (total_hits / 2000.0).min(1.0)
+ (total_hits > 2000.0) as u8 as f64 * (total_hits / 2000.0).log10() * 0.5;
+ f64::from(u8::from(total_hits > 2000.0)) * (total_hits / 2000.0).log10() * 0.5;
aim_value *= len_bonus;
@@ -594,7 +597,7 @@ impl OsuPpInner {
let len_bonus = 0.95
+ 0.4 * (total_hits / 2000.0).min(1.0)
+ (total_hits > 2000.0) as u8 as f64 * (total_hits / 2000.0).log10() * 0.5;
+ f64::from(u8::from(total_hits > 2000.0)) * (total_hits / 2000.0).log10() * 0.5;
speed_value *= len_bonus;
@@ -646,7 +649,7 @@ impl OsuPpInner {
// * Scale the speed value with # of 50s to punish doubletapping.
speed_value *= 0.99_f64.powf(
(self.state.n50 as f64 >= total_hits / 500.0) as u8 as f64
f64::from(u8::from(self.state.n50 as f64 >= total_hits / 500.0))
* (self.state.n50 as f64 - total_hits / 500.0),
);
@@ -718,7 +721,9 @@ impl OsuPpInner {
// * Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius.
flashlight_value *= 0.7
+ 0.1 * (total_hits / 200.0).min(1.0)
+ (total_hits > 200.0) as u8 as f64 * 0.2 * ((total_hits - 200.0) / 200.0).min(1.0);
+ f64::from(u8::from(total_hits > 200.0))
* 0.2
* ((total_hits - 200.0) / 200.0).min(1.0);
// * Scale the flashlight value with accuracy _slightly_.
flashlight_value *= 0.5 + self.acc / 2.0;
@@ -737,7 +742,7 @@ impl OsuPpInner {
}
}
fn total_hits(&self) -> f64 {
const fn total_hits(&self) -> f64 {
self.state.total_hits() as f64
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ impl OsuScoreState {
/// Return the total amount of hits by adding everything up.
#[inline]
pub fn total_hits(&self) -> usize {
pub const fn total_hits(&self) -> usize {
self.n300 + self.n100 + self.n50 + self.n_misses
}
+6 -9
View File
@@ -39,7 +39,7 @@ impl Skill for Aim {
curr: &OsuDifficultyObject<'_>,
diff_objects: &[OsuDifficultyObject<'_>],
) {
<Self as StrainSkill>::process(self, curr, diff_objects)
<Self as StrainSkill>::process(self, curr, diff_objects);
}
#[inline]
@@ -110,13 +110,10 @@ impl AimEvaluator {
) -> f64 {
let osu_curr_obj = curr;
let (osu_last_last_obj, osu_last_obj) = if let Some(tuple) =
previous(diff_objects, curr.idx, 1)
.zip(previous(diff_objects, curr.idx, 0))
.filter(|(_, last)| !(curr.base.is_spinner() || last.base.is_spinner()))
{
tuple
} else {
let Some((osu_last_last_obj, osu_last_obj)) = previous(diff_objects, curr.idx, 1)
.zip(previous(diff_objects, curr.idx, 0))
.filter(|(_, last)| !(curr.base.is_spinner() || last.base.is_spinner()))
else {
return 0.0;
};
@@ -234,7 +231,7 @@ impl AimEvaluator {
if osu_last_obj.base.is_slider() {
// * Reward sliders based on velocity.
slider_bonus = osu_last_obj.dists.travel_dist / osu_last_obj.dists.travel_time
slider_bonus = osu_last_obj.dists.travel_dist / osu_last_obj.dists.travel_time;
}
// * Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger.
+6 -5
View File
@@ -29,7 +29,7 @@ impl Flashlight {
curr_section_end: 0.0,
strain_peaks: CompactVec::new(),
has_hidden_mod: mods.hd(),
scaling_factor: 52.0 / radius as f64,
scaling_factor: 52.0 / f64::from(radius),
time_preempt,
time_fade_in,
}
@@ -47,7 +47,7 @@ impl Skill for Flashlight {
curr: &OsuDifficultyObject<'_>,
diff_objects: &[OsuDifficultyObject<'_>],
) {
<Self as StrainSkill>::process(self, curr, diff_objects)
<Self as StrainSkill>::process(self, curr, diff_objects);
}
#[inline]
@@ -155,8 +155,9 @@ impl FlashlightEvaluator {
let curr_hit_obj = curr_obj.base;
if !curr_obj.base.is_spinner() {
let jump_dist =
(osu_hit_obj.stacked_pos() - curr_hit_obj.stacked_end_pos()).length() as f64;
let jump_dist = f64::from(
(osu_hit_obj.stacked_pos() - curr_hit_obj.stacked_end_pos()).length(),
);
cumulative_strain_time += last_obj.strain_time;
// * We want to nerf objects that can be easily seen within the Flashlight circle radius.
@@ -210,7 +211,7 @@ impl FlashlightEvaluator {
if let OsuObjectKind::Slider(slider) = &osu_curr.base.kind {
// * Invert the scaling factor to determine the true travel distance independent of circle size.
let pixel_travel_dist = osu_curr.dists.lazy_travel_dist as f64 / scaling_factor;
let pixel_travel_dist = f64::from(osu_curr.dists.lazy_travel_dist) / scaling_factor;
// * Reward sliders based on velocity.
slider_bonus = ((pixel_travel_dist / osu_curr.dists.travel_time - Self::MIN_VELOCITY)
+13 -14
View File
@@ -56,7 +56,7 @@ impl Skill for Speed {
curr: &OsuDifficultyObject<'_>,
diff_objects: &[OsuDifficultyObject<'_>],
) {
<Self as StrainSkill>::process(self, curr, diff_objects)
<Self as StrainSkill>::process(self, curr, diff_objects);
}
#[inline]
@@ -210,7 +210,7 @@ impl RhythmEvaluator {
while previous(diff_objects, curr.idx, rhythm_start)
.filter(|prev| {
rhythm_start + 2 < historical_note_count
&& curr.start_time - prev.start_time < Self::HISTORY_TIME_MAX as f64
&& curr.start_time - prev.start_time < f64::from(Self::HISTORY_TIME_MAX)
})
.is_some()
{
@@ -218,20 +218,17 @@ impl RhythmEvaluator {
}
for i in (1..=rhythm_start).rev() {
let (curr_obj, prev_obj, last_obj) = if let Some(((curr, prev), last)) =
previous(diff_objects, curr.idx, i - 1)
.zip(previous(diff_objects, curr.idx, i))
.zip(previous(diff_objects, curr.idx, i + 1))
{
(curr, prev, last)
} else {
let Some(((curr_obj, prev_obj), last_obj)) = previous(diff_objects, curr.idx, i - 1)
.zip(previous(diff_objects, curr.idx, i))
.zip(previous(diff_objects, curr.idx, i + 1))
else {
break;
};
// * scales note 0 to 1 from history to now
let mut curr_historical_decay = (Self::HISTORY_TIME_MAX as f64
let mut curr_historical_decay = (f64::from(Self::HISTORY_TIME_MAX)
- (curr.start_time - curr_obj.start_time))
/ Self::HISTORY_TIME_MAX as f64;
/ f64::from(Self::HISTORY_TIME_MAX);
// * either we're limited by time or limited by object count.
curr_historical_decay = curr_historical_decay
@@ -245,7 +242,7 @@ impl RhythmEvaluator {
let base = (PI / (prev_delta.min(curr_delta) / prev_delta.max(curr_delta))).sin();
let curr_ratio = 1.0 + 6.0 * (base * base).min(0.5);
let hit_window = !curr_obj.base.is_spinner() as u64 as f64 * hit_window;
let hit_window = u64::from(!curr_obj.base.is_spinner()) as f64 * hit_window;
let mut window_penalty = ((((prev_delta - curr_delta).abs() - hit_window * 0.3)
.max(0.0))
@@ -257,6 +254,8 @@ impl RhythmEvaluator {
let mut effective_ratio = window_penalty * curr_ratio;
if first_delta_switch {
// Keep in-sync with lazer
#[allow(clippy::if_not_else)]
if !(prev_delta > 1.25 * curr_delta || prev_delta * 1.25 < curr_delta) {
if island_size < 7 {
// * island is still progressing, count size.
@@ -290,9 +289,9 @@ impl RhythmEvaluator {
rhythm_complexity_sum += (effective_ratio * start_ratio).sqrt()
* curr_historical_decay
* ((4 + island_size) as f64).sqrt()
* f64::from(4 + island_size).sqrt()
/ 2.0
* ((4 + prev_island_size) as f64).sqrt()
* f64::from(4 + prev_island_size).sqrt()
/ 2.0;
start_ratio = effective_ratio;
+2 -1
View File
@@ -125,7 +125,8 @@ pub(crate) trait OsuStrainSkill: StrainSkill + Sized {
// * We are reducing the highest strains first to account for extreme difficulty spikes
for (i, strain) in peak_iter.enumerate() {
let clamped = (i as f32 / Self::REDUCED_SECTION_COUNT as f32).clamp(0.0, 1.0) as f64;
let clamped =
f64::from((i as f32 / Self::REDUCED_SECTION_COUNT as f32).clamp(0.0, 1.0));
let scale = (lerp(1.0, 10.0, clamped)).log10();
*strain *= lerp(Self::REDUCED_STRAIN_BASELINE, 1.0, scale);
}
+7 -9
View File
@@ -16,31 +16,29 @@ pub struct HitObject {
impl HitObject {
/// The end time of the object.
pub(crate) fn end_time(&self) -> f64 {
pub(crate) const fn end_time(&self) -> f64 {
match &self.kind {
HitObjectKind::Circle => self.start_time,
// incorrect, only called in mania which has no sliders though
HitObjectKind::Slider { .. } => self.start_time,
HitObjectKind::Spinner { end_time } => *end_time,
HitObjectKind::Hold { end_time, .. } => *end_time,
// incorrect for sliders, only called in mania which has nono though
HitObjectKind::Circle | HitObjectKind::Slider { .. } => self.start_time,
HitObjectKind::Spinner { end_time } | HitObjectKind::Hold { end_time, .. } => *end_time,
}
}
/// If the object is a circle.
#[inline]
pub fn is_circle(&self) -> bool {
pub const fn is_circle(&self) -> bool {
matches!(self.kind, HitObjectKind::Circle)
}
/// If the object is a slider.
#[inline]
pub fn is_slider(&self) -> bool {
pub const fn is_slider(&self) -> bool {
matches!(self.kind, HitObjectKind::Slider { .. })
}
/// If the object is a spinner.
#[inline]
pub fn is_spinner(&self) -> bool {
pub const fn is_spinner(&self) -> bool {
matches!(self.kind, HitObjectKind::Spinner { .. })
}
}
+20 -25
View File
@@ -115,9 +115,8 @@ macro_rules! parse_general_body {
break;
}
let (key, value) = match $reader.split_colon() {
Some(tuple) => tuple,
None => continue,
let Some((key, value)) = $reader.split_colon() else {
continue;
};
if key == b"Mode" {
@@ -162,9 +161,8 @@ macro_rules! parse_difficulty_body {
break;
}
let (key, value) = match $reader.split_colon() {
Some(tuple) => tuple,
None => continue,
let Some((key, value)) = $reader.split_colon() else {
continue;
};
match key {
@@ -268,9 +266,8 @@ macro_rules! parse_timingpoints_body {
let line = $reader.get_line()?;
let mut split = line.split(',');
let time = match split.next().map(str::trim).and_then(f64::parse_in_range) {
Some(time) => time,
None => continue,
let Some(time) = split.next().map(str::trim).and_then(f64::parse_in_range) else {
continue;
};
// * beatLength is allowed to be NaN to handle an edge case in which
@@ -353,7 +350,7 @@ macro_rules! parse_timingpoints_body {
1.0
};
if time != pending_diff_points_time {
if (time - pending_diff_points_time).abs() >= f64::EPSILON {
if let Some(point) = pending_diff_point.take() {
$self.difficulty_points.push(point);
}
@@ -425,23 +422,20 @@ macro_rules! parse_hitobjects_body {
continue 'next_line;
};
let time = match split.next().map(str::trim).and_then(f64::parse_in_range) {
Some(time) => time,
None => continue 'next_line,
let Some(time) = split.next().map(str::trim).and_then(f64::parse_in_range) else {
continue 'next_line;
};
if !$self.hit_objects.is_empty() && time < prev_time {
unsorted = true;
}
let kind = match split.next().map(str::parse::<u8>) {
Some(Ok(kind)) => kind,
_ => continue 'next_line,
let Some(Ok(kind)) = split.next().map(str::parse::<u8>) else {
continue 'next_line;
};
let mut sound = match split.next().map(str::parse::<u8>) {
Some(Ok(sound)) => sound,
_ => continue 'next_line,
let Some(Ok(mut sound)) = split.next().map(str::parse::<u8>) else {
continue 'next_line;
};
fn has_custom_sound_file(bank_info: Option<&str>) -> Option<bool> {
@@ -502,8 +496,9 @@ macro_rules! parse_hitobjects_body {
let mut first = true;
// SAFETY: `Vec<(usize, usize)>` and `Vec<&str>` have the same size and layout.
let point_split: &mut Vec<&str> =
unsafe { std::mem::transmute(&mut point_split_raw) };
let point_split = unsafe {
&mut *((&mut point_split_raw as *mut Vec<(usize, usize)>).cast::<Vec<&str>>())
};
point_split.clear();
point_split.extend(control_point_iter);
@@ -765,9 +760,9 @@ mod slider_parsing {
) -> ConvertStatus {
let mut path_kind = PathType::from_str(points[0]);
let read_offset = first as usize;
let read_offset = usize::from(first);
let readable_points = points.len() - 1;
let end_point_len = end_point.is_some() as usize;
let end_point_len = usize::from(end_point.is_some());
vertices.clear();
vertices.reserve(read_offset + readable_points + end_point_len);
@@ -862,7 +857,7 @@ mod slider_parsing {
pub(super) fn read_point(value: &str, start_pos: Pos2) -> Option<PathControlPoint> {
let mut v = value
.split(':')
.flat_map(|s| f64::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f64))
.filter_map(|s| f64::parse_in_custom_range(s, f64::from(MAX_COORDINATE_VALUE)))
.map(|n| n as i32 as f32);
v.next()
@@ -1056,7 +1051,7 @@ enum Section {
}
impl Section {
fn from_bytes(bytes: &[u8]) -> Self {
const fn from_bytes(bytes: &[u8]) -> Self {
match bytes {
b"General" => Self::General,
b"Difficulty" => Self::Difficulty,
+3 -2
View File
@@ -3,6 +3,7 @@ use std::ops;
/// Simple (x, y) coordinate / vector
#[derive(Clone, Copy, Default, PartialEq)]
#[must_use]
pub struct Pos2 {
/// Position on the x-axis.
pub x: f32,
@@ -19,7 +20,7 @@ impl Pos2 {
/// Return a position with both coordinates on the given value.
#[inline]
pub fn new(value: f32) -> Self {
pub const fn new(value: f32) -> Self {
Self { x: value, y: value }
}
@@ -32,7 +33,7 @@ impl Pos2 {
/// Return the position's length.
#[inline]
pub fn length(&self) -> f32 {
((self.x * self.x + self.y * self.y) as f64).sqrt() as f32
(f64::from(self.x * self.x + self.y * self.y)).sqrt() as f32
}
/// Return the dot product.
+6 -6
View File
@@ -118,8 +118,9 @@ impl<R> FileReader<R> {
if self.buf.len() >= 3 {
// truncate one `0` that was left after truncating `\n` when reading the line.
// additionally truncate `0\r` if possible.
sub += 1 + 2
* (self.buf.len() >= 4 && self.buf[self.buf.len() - 2] == b'\r') as usize;
sub += 1 + 2 * usize::from(
self.buf.len() >= 4 && self.buf[self.buf.len() - 2] == b'\r',
);
}
self.buf.rotate_left(1);
@@ -159,7 +160,7 @@ impl<R> FileReader<R> {
.ok_or(ParseError::IncorrectFileHeader)
}
/// Returns the bytes inbetween '[' and ']'.
/// Returns the bytes inbetween `[` and `]`.
pub(crate) fn get_section(&self) -> Option<&[u8]> {
if self.buf[0] == b'[' {
if let Some(end) = self.buf[1..].iter().position(|&byte| byte == b']') {
@@ -191,9 +192,8 @@ impl<R> FileReader<R> {
///
/// Returns `None` if the second half is invalid UTF-8.
pub(crate) fn split_colon(&self) -> Option<(&[u8], &str)> {
let idx = match self.buf.iter().position(|&byte| byte == b':') {
Some(idx) => idx,
None => return Some((&self.buf, "")),
let Some(idx) = self.buf.iter().position(|&byte| byte == b':') else {
return Some((&self.buf, ""));
};
let back = std::str::from_utf8(&self.buf[idx + 1..]).ok()?;
+1 -1
View File
@@ -4,7 +4,7 @@ use std::cmp::Ordering;
const QUICK_SORT_DEPTH_THRESHOLD: usize = 32;
/// Algorithm from https://github.com/ppy/osu/blob/master/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs#L21
/// Algorithm from <https://github.com/ppy/osu/blob/master/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs#L21>
pub(crate) fn legacy_sort(keys: &mut [HitObject]) {
if keys.is_empty() {
return;
+3 -5
View File
@@ -37,6 +37,7 @@ use crate::{
/// ```
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug)]
#[must_use]
pub enum AnyPP<'map> {
/// osu!standard performance calculator
Osu(OsuPP<'map>),
@@ -266,8 +267,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn n_katu(self, n_katu: usize) -> Self {
match self {
Self::Osu(_) => self,
Self::Taiko(_) => self,
Self::Osu(_) | Self::Taiko(_) => self,
Self::Catch(f) => Self::Catch(f.tiny_droplet_misses(n_katu)),
Self::Mania(m) => Self::Mania(m.n200(n_katu)),
}
@@ -280,9 +280,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn n_geki(self, n_geki: usize) -> Self {
match self {
Self::Osu(_) => self,
Self::Taiko(_) => self,
Self::Catch(_) => self,
Self::Osu(_) | Self::Taiko(_) | Self::Catch(_) => self,
Self::Mania(m) => Self::Mania(m.n320(n_geki)),
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ impl ScoreState {
if mode != GameMode::Osu {
amount += self.n_katu;
amount += (mode != GameMode::Catch) as usize * self.n_geki;
amount += usize::from(mode != GameMode::Catch) * self.n_geki;
}
}
+1
View File
@@ -21,6 +21,7 @@ use crate::{
/// println!("Stars: {}", difficulty_attrs.stars());
/// ```
#[derive(Clone, Debug)]
#[must_use]
pub enum AnyStars<'map> {
/// osu!standard difficulty calculator
Osu(OsuStars<'map>),
+2 -2
View File
@@ -80,7 +80,7 @@ impl ColourDifficultyPreprocessor {
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);
let alternating_mono_patterns = Self::encode_alternating_mono_pattern(&mono_streaks);
Self::encode_repeating_hit_pattern(alternating_mono_patterns)
}
@@ -120,7 +120,7 @@ impl ColourDifficultyPreprocessor {
}
fn encode_alternating_mono_pattern(
data: Vec<Rc<RefCell<MonoStreak>>>,
data: &[Rc<RefCell<MonoStreak>>],
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> {
let mut mono_patterns = VecDeque::new();
mono_patterns.push_back(AlternatingMonoPattern::new());
+9 -9
View File
@@ -109,7 +109,7 @@ impl TaikoGradualDifficulty {
let mut diff_objects = ObjectLists::with_capacity(map.hit_objects.len().saturating_sub(2));
map.taiko_objects()
.inspect(|(h, _)| total_hits += h.is_hit as usize)
.inspect(|(h, _)| total_hits += usize::from(h.is_hit))
.skip(2)
.zip(map.hit_objects.iter().skip(1))
.zip(map.hit_objects.iter())
@@ -240,10 +240,8 @@ impl Iterator for TaikoGradualDifficulty {
self.idx += 1;
match self.first_combos {
FirstTwoCombos::None => {}
FirstTwoCombos::OnlyFirst => self.attrs.max_combo = 1,
FirstTwoCombos::OnlySecond => {}
FirstTwoCombos::Both => self.attrs.max_combo = 1,
FirstTwoCombos::None | FirstTwoCombos::OnlySecond => {}
FirstTwoCombos::OnlyFirst | FirstTwoCombos::Both => self.attrs.max_combo = 1,
}
}
(_, 0) => {
@@ -252,8 +250,9 @@ impl Iterator for TaikoGradualDifficulty {
match self.first_combos {
FirstTwoCombos::None => {}
FirstTwoCombos::OnlyFirst => self.attrs.max_combo = 1,
FirstTwoCombos::OnlySecond => self.attrs.max_combo = 1,
FirstTwoCombos::OnlyFirst | FirstTwoCombos::OnlySecond => {
self.attrs.max_combo = 1;
}
FirstTwoCombos::Both => self.attrs.max_combo = 2,
}
}
@@ -263,8 +262,9 @@ impl Iterator for TaikoGradualDifficulty {
match self.first_combos {
FirstTwoCombos::None => {}
FirstTwoCombos::OnlyFirst => self.attrs.max_combo = 1,
FirstTwoCombos::OnlySecond => self.attrs.max_combo = 1,
FirstTwoCombos::OnlyFirst | FirstTwoCombos::OnlySecond => {
self.attrs.max_combo = 1;
}
FirstTwoCombos::Both => self.attrs.max_combo = 2,
}
}
+14 -13
View File
@@ -54,6 +54,7 @@ const DIFFICULTY_MULTIPLIER: f64 = 1.35;
/// println!("Stars: {}", difficulty_attrs.stars);
/// ```
#[derive(Clone, Debug)]
#[must_use]
pub struct TaikoStars<'map> {
map: Cow<'map, Beatmap>,
mods: u32,
@@ -82,7 +83,7 @@ impl<'map> TaikoStars<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -97,7 +98,7 @@ impl<'map> TaikoStars<'map> {
[`TaikoGradualDifficultyAttributes`](crate::taiko::TaikoGradualDifficulty)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -107,7 +108,7 @@ impl<'map> TaikoStars<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -117,7 +118,7 @@ impl<'map> TaikoStars<'map> {
///
/// This only needs to be specified if the map was converted manually beforehand.
#[inline]
pub fn is_convert(mut self, is_convert: bool) -> Self {
pub const fn is_convert(mut self, is_convert: bool) -> Self {
self.is_convert = is_convert;
self
@@ -242,8 +243,8 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
map.taiko_objects()
.inspect(|(h, _)| {
n_diff_objects += (max_combo < take) as usize;
max_combo += (max_combo < take && h.is_hit) as usize;
n_diff_objects += usize::from(max_combo < take);
max_combo += usize::from(max_combo < take && h.is_hit);
})
.skip(2)
.zip(map.hit_objects.iter().skip(1))
@@ -276,7 +277,7 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
map.hit_objects
.iter()
.take(2)
.for_each(|h| n_diff_objects = n_diff_objects.saturating_sub(h.is_circle() as usize));
.for_each(|h| n_diff_objects = n_diff_objects.saturating_sub(usize::from(h.is_circle())));
ColourDifficultyPreprocessor::process_and_assign(&mut diff_objects);
@@ -320,13 +321,13 @@ pub struct TaikoDifficultyAttributes {
impl TaikoDifficultyAttributes {
/// Return the maximum combo.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.max_combo
}
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
#[inline]
pub fn is_convert(&self) -> bool {
pub const fn is_convert(&self) -> bool {
self.is_convert
}
@@ -355,25 +356,25 @@ pub struct TaikoPerformanceAttributes {
impl TaikoPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
pub const fn stars(&self) -> f64 {
self.difficulty.stars
}
/// Return the performance point value.
#[inline]
pub fn pp(&self) -> f64 {
pub const fn pp(&self) -> f64 {
self.pp
}
/// Return the maximum combo of the map.
#[inline]
pub fn max_combo(&self) -> usize {
pub const fn max_combo(&self) -> usize {
self.difficulty.max_combo
}
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
#[inline]
pub fn is_convert(&self) -> bool {
pub const fn is_convert(&self) -> bool {
self.difficulty.is_convert
}
}
+14 -12
View File
@@ -37,6 +37,7 @@ use crate::{
/// ```
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
#[must_use]
pub struct TaikoPP<'map> {
pub(crate) map_or_attrs: MapOrElse<Cow<'map, Beatmap>, TaikoDifficultyAttributes>,
is_convert_overwrite: Option<bool>,
@@ -89,7 +90,7 @@ impl<'map> TaikoPP<'map> {
///
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
#[inline]
pub fn mods(mut self, mods: u32) -> Self {
pub const fn mods(mut self, mods: u32) -> Self {
self.mods = mods;
self
@@ -97,7 +98,7 @@ impl<'map> TaikoPP<'map> {
/// Specify the max combo of the play.
#[inline]
pub fn combo(mut self, combo: usize) -> Self {
pub const fn combo(mut self, combo: usize) -> Self {
self.combo = Some(combo);
self
@@ -107,7 +108,7 @@ impl<'map> TaikoPP<'map> {
///
/// Defauls to [`HitResultPriority::BestCase`].
#[inline]
pub fn hitresult_priority(mut self, priority: HitResultPriority) -> Self {
pub const fn hitresult_priority(mut self, priority: HitResultPriority) -> Self {
self.hitresult_priority = Some(priority);
self
@@ -115,7 +116,7 @@ impl<'map> TaikoPP<'map> {
/// Specify the amount of 300s of a play.
#[inline]
pub fn n300(mut self, n300: usize) -> Self {
pub const fn n300(mut self, n300: usize) -> Self {
self.n300 = Some(n300);
self
@@ -123,7 +124,7 @@ impl<'map> TaikoPP<'map> {
/// Specify the amount of 100s of a play.
#[inline]
pub fn n100(mut self, n100: usize) -> Self {
pub const fn n100(mut self, n100: usize) -> Self {
self.n100 = Some(n100);
self
@@ -131,7 +132,7 @@ impl<'map> TaikoPP<'map> {
/// Specify the amount of misses of the play.
#[inline]
pub fn n_misses(mut self, n_misses: usize) -> Self {
pub const fn n_misses(mut self, n_misses: usize) -> Self {
self.n_misses = Some(n_misses);
self
@@ -155,7 +156,7 @@ impl<'map> TaikoPP<'map> {
[`TaikoGradualPerformanceAttributes`](crate::taiko::TaikoGradualPerformance)."
)]
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
pub const fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
self
@@ -165,7 +166,7 @@ impl<'map> TaikoPP<'map> {
/// If none is specified, it will take the clock rate based on the mods
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
pub const fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
@@ -175,7 +176,7 @@ impl<'map> TaikoPP<'map> {
///
/// This only needs to be specified if the map was converted manually beforehand.
#[inline]
pub fn is_convert(mut self, is_convert: bool) -> Self {
pub const fn is_convert(mut self, is_convert: bool) -> Self {
self.is_convert_overwrite = Some(is_convert);
self
@@ -183,7 +184,8 @@ impl<'map> TaikoPP<'map> {
/// Provide parameters through a [`TaikoScoreState`].
#[inline]
pub fn state(mut self, state: TaikoScoreState) -> Self {
#[allow(clippy::needless_pass_by_value)]
pub const fn state(mut self, state: TaikoScoreState) -> Self {
let TaikoScoreState {
max_combo,
n300,
@@ -483,11 +485,11 @@ impl TaikoPpInner {
acc_value
}
fn total_hits(&self) -> f64 {
const fn total_hits(&self) -> f64 {
self.state.total_hits() as f64
}
fn total_successful_hits(&self) -> usize {
const fn total_successful_hits(&self) -> usize {
self.state.n300 + self.state.n100
}
+1 -1
View File
@@ -22,7 +22,7 @@ impl TaikoScoreState {
/// Return the total amount of hits by adding everything up.
#[inline]
pub fn total_hits(&self) -> usize {
pub const fn total_hits(&self) -> usize {
self.n300 + self.n100 + self.n_misses
}
+9 -8
View File
@@ -35,7 +35,7 @@ impl Colour {
impl Skill for Colour {
#[inline]
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);
}
#[inline]
@@ -100,20 +100,21 @@ 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
.parent
.as_ref()
.and_then(Weak::upgrade)
.as_ref()
.map_or(1.0, Self::evaluate_diff_of_alternating_mono_pattern);
Self::sigmoid(mono_streak.idx as f64, 2.0, 2.0, 0.5, 1.0) * parent_eval * 0.5
}
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();
@@ -121,13 +122,14 @@ impl ColourEvaluator {
.parent
.as_ref()
.and_then(Weak::upgrade)
.as_ref()
.map_or(1.0, Self::evaluate_diff_of_repeating_hit_patterns);
Self::sigmoid(alternating_mono_pattern.idx as f64, 2.0, 2.0, 0.5, 1.0) * parent_eval
}
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;
@@ -140,7 +142,7 @@ impl ColourEvaluator {
// * Difficulty for MonoStreak
if let Some(mono_streak) = colour.mono_streak.as_ref().and_then(Weak::upgrade) {
difficulty += Self::evaluate_diff_of_mono_streak(mono_streak);
difficulty += Self::evaluate_diff_of_mono_streak(&mono_streak);
}
// * Difficulty for AlternatingMonoPattern
@@ -149,12 +151,11 @@ impl ColourEvaluator {
.as_ref()
.and_then(Weak::upgrade)
{
difficulty += Self::evaluate_diff_of_alternating_mono_pattern(alternating_mono_pattern);
difficulty += Self::evaluate_diff_of_alternating_mono_pattern(&alternating_mono_pattern);
}
// * Difficulty for RepeatingHitPattern
if let Some(repeating_hit_patterns) = colour.repeating_hit_patterns.as_ref().map(Rc::clone)
{
if let Some(repeating_hit_patterns) = colour.repeating_hit_patterns.as_ref() {
difficulty += Self::evaluate_diff_of_repeating_hit_patterns(repeating_hit_patterns);
}
+1
View File
@@ -111,6 +111,7 @@ impl Skill for Peaks {
}
}
#[allow(clippy::struct_field_names)]
pub(crate) struct PeaksDifficultyValues {
pub(crate) colour_rating: f64,
pub(crate) rhythm_rating: f64,
+2 -2
View File
@@ -102,7 +102,7 @@ impl Rhythm {
impl Skill for Rhythm {
#[inline]
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);
}
#[inline]
@@ -194,7 +194,7 @@ pub(crate) struct HistoryElement {
}
impl HistoryElement {
fn new(difficulty_object: &TaikoDifficultyObject) -> Self {
const fn new(difficulty_object: &TaikoDifficultyObject) -> Self {
Self {
idx: difficulty_object.idx,
rhythm: difficulty_object.rhythm,
+1 -1
View File
@@ -27,7 +27,7 @@ impl Stamina {
impl Skill for Stamina {
#[inline]
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);
}
#[inline]
+2 -2
View File
@@ -19,12 +19,12 @@ pub(crate) struct ByteHash {
impl Hasher for ByteHash {
#[inline]
fn finish(&self) -> u64 {
self.byte as u64
u64::from(self.byte)
}
#[inline]
fn write(&mut self, _: &[u8]) {
unreachable!()
unimplemented!()
}
#[inline]
+4 -4
View File
@@ -17,7 +17,7 @@ impl CompactVec {
}
pub(crate) fn push(&mut self, num: f64) {
self.push_n(num, 1)
self.push_n(num, 1);
}
pub(crate) fn push_n(&mut self, num: f64, n: usize) {
@@ -28,7 +28,7 @@ impl CompactVec {
{
last.count += n;
} else if n > 0 {
self.inner.push(Entry::new(num, n))
self.inner.push(Entry::new(num, n));
}
self.len += n;
@@ -38,7 +38,7 @@ impl CompactVec {
where
F: FnMut(f64) -> bool,
{
self.inner.retain(|entry| f(entry.value))
self.inner.retain(|entry| f(entry.value));
}
pub(crate) fn iter(&self) -> Iter<'_> {
@@ -69,7 +69,7 @@ struct Entry {
}
impl Entry {
fn new(value: f64, count: usize) -> Self {
const fn new(value: f64, count: usize) -> Self {
Self { value, count }
}
}
+6 -6
View File
@@ -51,18 +51,18 @@ impl<T, const N: usize> LimitedQueue<T, N> {
pub(crate) fn push(&mut self, elem: T) {
self.end = (self.end + 1) % N;
self.queue[self.end] = elem;
self.len += (self.len < N) as usize;
self.len += usize::from(self.len < N);
}
pub(crate) fn is_empty(&self) -> bool {
pub(crate) const fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) fn len(&self) -> usize {
pub(crate) const fn len(&self) -> usize {
self.len
}
pub(crate) fn last(&self) -> Option<&T> {
pub(crate) const fn last(&self) -> Option<&T> {
if self.is_empty() {
None
} else {
@@ -74,7 +74,7 @@ impl<T, const N: usize> LimitedQueue<T, N> {
self.queue
.iter()
.cycle()
.skip((self.len == N) as usize * (self.end + 1))
.skip(usize::from(self.len == N) * (self.end + 1))
.take(self.len)
}
}
@@ -92,7 +92,7 @@ impl<T, const N: usize> Index<usize> for LimitedQueue<T, N> {
self.len
);
let idx = (idx + (self.len == N) as usize * (self.end + 1)) % N;
let idx = (idx + usize::from(self.len == N) * (self.end + 1)) % N;
&self.queue[idx]
}
+1 -1
View File
@@ -38,7 +38,7 @@ impl<'map, E> From<&'map Beatmap> for MapOrElse<MapRef<'map>, E> {
pub(crate) struct MapRef<'map>(&'map Beatmap);
impl<'map> MapRef<'map> {
pub(crate) fn into_inner(self) -> &'map Beatmap {
pub(crate) const fn into_inner(self) -> &'map Beatmap {
self.0
}
}
+2 -2
View File
@@ -62,13 +62,13 @@ impl TandemSorter {
}
#[inline(always)]
fn idx_is_marked(idx: usize) -> bool {
const fn idx_is_marked(idx: usize) -> bool {
// Check if first bit is set
idx.leading_zeros() == 0
}
#[inline(always)]
fn toggle_mark_idx(idx: usize) -> usize {
const fn toggle_mark_idx(idx: usize) -> usize {
// Flip the first bit
idx ^ !(usize::MAX >> 1)
}