1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use crate::taiko::performance::TaikoPerformance;
/// The result of a difficulty calculation on an osu!taiko map.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TaikoDifficultyAttributes {
/// The difficulty of the stamina skill.
pub stamina: f64,
/// The difficulty of the rhythm skill.
pub rhythm: f64,
/// The difficulty of the color skill.
pub color: f64,
/// The difficulty of the reading skill.
pub reading: f64,
/// The perceived hit window for an n300 inclusive of rate-adjusting mods (DT/HT/etc)
pub great_hit_window: f64,
/// The perceived hit window for an n100 inclusive of rate-adjusting mods (DT/HT/etc)
pub ok_hit_window: f64,
/// The ratio of stamina difficulty from mono-color (single color) streams to total
/// stamina difficulty.
pub mono_stamina_factor: f64,
/// The difficulty corresponding to the mechanical skills.
///
/// This includes colour and stamina combined.
pub mechanical_difficulty: f64,
/// The factor corresponding to the consistency of a map.
pub consistency_factor: f64,
/// The final star rating.
pub stars: f64,
/// The maximum combo.
pub max_combo: u32,
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
///
/// [`Beatmap`]: crate::model::beatmap::Beatmap
pub is_convert: bool,
}
impl TaikoDifficultyAttributes {
/// Return the maximum combo.
pub const fn max_combo(&self) -> u32 {
self.max_combo
}
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
///
/// [`Beatmap`]: crate::model::beatmap::Beatmap
pub const fn is_convert(&self) -> bool {
self.is_convert
}
/// Returns a builder for performance calculation.
pub fn performance<'a>(self) -> TaikoPerformance<'a> {
self.into()
}
}
/// The result of a performance calculation on an osu!taiko map.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TaikoPerformanceAttributes {
/// The difficulty attributes that were used for the performance calculation
pub difficulty: TaikoDifficultyAttributes,
/// The final performance points.
pub pp: f64,
/// The accuracy portion of the final pp.
pub pp_acc: f64,
/// The strain portion of the final pp.
pub pp_difficulty: f64,
/// Upper bound on the player's tap deviation.
pub estimated_unstable_rate: Option<f64>,
}
impl TaikoPerformanceAttributes {
/// Return the star value.
pub const fn stars(&self) -> f64 {
self.difficulty.stars
}
/// Return the performance point value.
pub const fn pp(&self) -> f64 {
self.pp
}
/// Return the maximum combo of the map.
pub const fn max_combo(&self) -> u32 {
self.difficulty.max_combo
}
/// Whether the [`Beatmap`] was a convert i.e. an osu!standard map.
///
/// [`Beatmap`]: crate::model::beatmap::Beatmap
pub const fn is_convert(&self) -> bool {
self.difficulty.is_convert
}
/// Returns a builder for performance calculation.
pub fn performance<'a>(self) -> TaikoPerformance<'a> {
self.difficulty.into()
}
}
impl From<TaikoPerformanceAttributes> for TaikoDifficultyAttributes {
fn from(attributes: TaikoPerformanceAttributes) -> Self {
attributes.difficulty
}
}
#[cfg(test)]
pub(super) mod tests {
use super::TaikoDifficultyAttributes;
/// Asserts equality for two [`TaikoDifficultyAttributes`] instances.
///
/// `NaN` values are considered to be equal.
#[track_caller]
pub fn assert_eq_attrs(a: &TaikoDifficultyAttributes, b: &TaikoDifficultyAttributes) {
let TaikoDifficultyAttributes {
stamina,
rhythm,
color,
reading,
great_hit_window,
ok_hit_window,
mono_stamina_factor,
mechanical_difficulty,
consistency_factor,
stars,
max_combo,
is_convert,
} = a;
macro_rules! assert_eq_nan {
( $field:ident ) => {
if *$field != b.$field {
if $field.is_nan() != b.$field.is_nan() {
assert_eq!(*$field, b.$field);
}
}
};
}
assert_eq_nan!(stamina);
assert_eq_nan!(rhythm);
assert_eq_nan!(color);
assert_eq_nan!(reading);
assert_eq_nan!(great_hit_window);
assert_eq_nan!(ok_hit_window);
assert_eq_nan!(mono_stamina_factor);
assert_eq_nan!(mechanical_difficulty);
assert_eq_nan!(consistency_factor);
assert_eq_nan!(stars);
assert_eq!(*max_combo, b.max_combo);
assert_eq!(*is_convert, b.is_convert);
}
}