Skip to main content

rhythm_core/
rhythm.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::note::Note;
5
6#[derive(Debug, Clone, PartialEq)]
7#[cfg(feature = "serde")]
8#[derive(Serialize, Deserialize)]
9pub struct Rhythm<T: Note> {
10    pub notes: Vec<T>,
11    /// The current time.
12    time: f64,
13    /// The available notes. When single notes are hit or combo notes not in range, they will be removed from this list.
14    #[serde(skip_serializing, skip_deserializing)]
15    availables: Vec<T>,
16}
17
18impl<T: Note> Rhythm<T> {
19    pub fn new(notes: Vec<T>) -> Self {
20        let mut availables = notes.clone();
21        availables.sort_unstable();
22
23        Self {
24            notes,
25            time: 0.0,
26            availables,
27        }
28    }
29
30    pub fn current_time(&self) -> f64 {
31        self.time
32    }
33
34    pub fn forward(&mut self, time: impl Into<f64>) -> Vec<T> {
35        self.time += time.into();
36        self.update_availables()
37    }
38
39    pub fn set_time(&mut self, time: impl Into<f64>) {
40        self.time = time.into();
41        self.availables = self.notes.clone();
42        self.availables.sort_unstable();
43        self.update_availables();
44    }
45
46    pub fn finished(&self) -> bool {
47        self.availables.is_empty()
48    }
49
50    pub fn hit(&mut self, variant: impl Into<u16>) -> Option<(&T, f64)> {
51        let variant: u16 = variant.into();
52        let hitables = self.availables.iter_mut().filter(|note| {
53            note.start() <= self.time && note.start() + note.duration() >= self.time
54        });
55
56        for note in hitables {
57            if note.matches_variant(variant) && note.volume() > 0 {
58                note.set_volume(note.volume() - 1);
59                let time = self.time - note.start();
60                return Some((note, time));
61            }
62        }
63
64        None
65    }
66
67    fn update_availables(&mut self) -> Vec<T> {
68        let mut removed = vec![];
69        self.availables.retain(|note| {
70            let keep = note.start() + note.duration() >= self.time && note.volume() > 0;
71            if !keep && note.volume() > 0 {
72                removed.push(note.clone());
73            }
74            keep
75        });
76        removed
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::note::SimpleNote;
84
85    #[test]
86    fn test_single_note() {
87        let notes = vec![
88            SimpleNote::new(1000, 100, 1u16, 0u16),
89            SimpleNote::new(1100, 100, 1u16, 1u16),
90            SimpleNote::new(2000, 100, 1u16, 0u16),
91            SimpleNote::new(2100, 100, 1u16, 1u16),
92        ];
93
94        let mut rhythm = Rhythm::new(notes);
95
96        assert_eq!(rhythm.current_time(), 0.0);
97
98        assert_eq!(rhythm.forward(500), vec![]);
99        assert_eq!(rhythm.current_time(), 500.0);
100        assert_eq!(rhythm.hit(0u16), None);
101
102        assert_eq!(rhythm.forward(550), vec![]);
103        assert_eq!(rhythm.current_time(), 1050.0);
104        assert_eq!(rhythm.hit(1u16), None);
105        assert_eq!(
106            rhythm.hit(0u16),
107            Some((&SimpleNote::new(1000, 100, 0u16, 0u16), 50.0))
108        );
109        assert_eq!(rhythm.hit(0u16), None);
110
111        assert_eq!(
112            rhythm.forward(200),
113            vec![SimpleNote::new(1100, 100, 1u16, 1u16)]
114        );
115        assert_eq!(rhythm.current_time(), 1250.0);
116        assert_eq!(rhythm.hit(1u16), None);
117
118        assert_eq!(
119            rhythm.forward(2000),
120            vec![
121                SimpleNote::new(2000, 100, 1u16, 0u16),
122                SimpleNote::new(2100, 100, 1u16, 1u16)
123            ]
124        );
125    }
126
127    #[test]
128    fn test_combo_note() {
129        let notes = vec![
130            SimpleNote::new(1000, 1000, 10u16, 0u16),
131            SimpleNote::new(3000, 2000, u16::MAX, 1u16),
132        ];
133
134        let mut rhythm = Rhythm::new(notes);
135
136        assert_eq!(rhythm.current_time(), 0.0);
137
138        assert_eq!(rhythm.forward(1500), vec![]);
139        assert_eq!(rhythm.current_time(), 1500.0);
140        for i in 0..10 {
141            assert_eq!(
142                rhythm.hit(0u16),
143                Some((
144                    &SimpleNote::new(1000, 1000, 9 - i as u16, 0u16),
145                    500.0 + i as f64 * 10.0
146                ))
147            );
148            assert_eq!(rhythm.forward(10), vec![]);
149        }
150        assert_eq!(rhythm.current_time(), 1600.0);
151        assert_eq!(rhythm.hit(0u16), None);
152
153        assert_eq!(rhythm.forward(3000), vec![]);
154        assert_eq!(rhythm.current_time(), 4600.0);
155        for i in 0..1000 {
156            assert_eq!(
157                rhythm.hit(1u16),
158                Some((
159                    &SimpleNote::new(3000, 2000, u16::MAX - 1 - i as u16, 1u16),
160                    1600.0
161                ))
162            );
163        }
164    }
165}