rosu_pattern_detector/mania/models/
base.rs

1use crate::structs::CommonMeasure;
2use crate::mania::models::pattern::{Pattern, get_pattern_weight};
3
4#[derive(Debug, PartialEq, Eq, Hash, Clone)]
5pub enum Notes {
6    Single,
7    Jump,
8    Hand,
9    Quad,
10    Chord,
11    None
12}
13
14#[derive(Debug, Clone)]
15pub struct NotesStruct {
16    pub(crate) timestamp: i32,
17    pub(crate) notes: Vec<bool>,
18}
19
20impl NotesStruct {
21    pub fn to_display_string(&self) -> String {
22        let notes_str: String = self.notes
23            .iter()
24            .map(|&active| if active { 'O' } else { 'X' })
25            .collect();
26        format!("{}: {}", self.timestamp, notes_str)
27    }
28    pub fn get_pattern(&self) -> Notes {
29        let count = self.notes.iter().filter(|&&n| n).count();
30        match count {
31            1 => Notes::Single,
32            2 => Notes::Jump,
33            3 => Notes::Hand,
34            4 => Notes::Quad,
35            _ => Notes::Chord,
36        }
37    }
38}
39
40#[derive(Debug)]
41pub struct ManiaMeasure {
42    pub(crate) measure: CommonMeasure,
43    pub(crate) notes: Vec<NotesStruct>,
44    pub(crate) pattern: Pattern,
45    pub(crate) value: f64,
46}
47
48impl ManiaMeasure {
49
50    pub fn get_weight(&self, average_npm: f64) -> f64 {
51        match (self.measure.npm, average_npm) {
52            (npm, avg) if avg <= 0.0 => if npm > 0 { 1.0 } else { 0.0 },
53            (npm, _) if npm <= 0 => 0.0,
54            (npm, avg) => (npm as f64 / avg).clamp(0.0, 5.0), // Vibro or Jumptrill (could cause severe inflation)
55        }
56    }
57
58    pub fn get_pattern_weight_modifier(&self, average_npm: f64) -> f64 {
59        self.get_weight(average_npm)*get_pattern_weight(&self.pattern)
60    }
61
62    pub fn detect_pattern(&mut self) -> Pattern {
63        if self.has_jack_pattern() {
64            Pattern::Jack(self.determine_jack_type())
65            
66        } else if self.has_hand_notes() {
67            Pattern::Handstream(self.determine_handstream_type())
68        } else if self.has_jump_notes() {
69            Pattern::Jumpstream(self.determine_jumpstream_type())
70        } else if self.has_single_notes() {
71            Pattern::Singlestream(crate::mania::models::pattern::SinglestreamPattern::Singlestream)
72        } else {
73            Pattern::None
74        }
75    }
76
77    fn has_jack_pattern(&self) -> bool {
78        self.notes.windows(2).any(|w| {
79            // Vérifie si une même colonne est activée dans deux notes consécutives
80            w[0].notes.iter()
81                .zip(w[1].notes.iter())
82                .any(|(&prev, &curr)| prev && curr)
83        })
84    }
85
86    fn has_hand_notes(&self) -> bool {
87        self.notes.iter().any(|n| n.get_pattern() == Notes::Hand)
88    }
89
90    fn has_jump_notes(&self) -> bool {
91        self.notes.iter().any(|n| n.get_pattern() == Notes::Jump)
92    }
93
94    fn has_single_notes(&self) -> bool {
95        self.notes.iter().any(|n| n.get_pattern() == Notes::Single)
96    }
97
98    fn determine_jack_type(&mut self) -> crate::mania::models::pattern::JackPattern {
99        crate::mania::models::pattern::JackPattern::determine_jack_type(self)
100    }
101
102    fn determine_handstream_type(&mut self) -> crate::mania::models::pattern::HandstreamPattern {
103        crate::mania::models::pattern::HandstreamPattern::determine_hs_type(self)
104    }
105
106    fn determine_jumpstream_type(&mut self) -> crate::mania::models::pattern::JumpstreamPattern {
107        crate::mania::models::pattern::JumpstreamPattern::determine_js_type(self)
108    }
109
110
111    pub fn print_notes(&self) {
112        for note in &self.notes {
113            let line = note.to_display_string();
114            println!("{}", line);
115        }
116    }
117
118    pub fn t_notes(&self) -> i32 {
119        self.notes
120            .iter()
121            .flat_map(|v| v.notes.iter())
122            .filter(|&&b| b)
123            .count() as i32
124    }
125}