1use crate::extensions::HasPressRequirement;
2use crate::stage::DanceStage;
3use danceparser::Row;
4use std::fmt::{Debug, Display, Formatter};
5
6#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Hash)]
7pub enum Side {
8 Left,
9 Right,
10}
11
12#[derive(Clone, Copy, PartialOrd, PartialEq, Eq, Hash)]
14pub enum FootPart {
15 None,
16 LeftHeel,
17 LeftToe,
18 RightHeel,
19 RightToe,
20}
21
22impl FootPart {
23 pub(crate) fn all_except_none() -> [Self; 4] {
24 [
25 Self::LeftHeel,
26 Self::LeftToe,
27 Self::RightHeel,
28 Self::RightToe,
29 ]
30 }
31
32 pub const fn parse(c: char) -> Option<Self> {
33 match c {
34 'L' => Some(Self::LeftHeel),
35 'l' => Some(Self::LeftToe),
36 'R' => Some(Self::RightHeel),
37 'r' => Some(Self::RightToe),
38 '-' => Some(Self::None),
39 _ => None,
40 }
41 }
42
43 pub(crate) fn is_toe(&self) -> bool {
44 matches!(self, FootPart::LeftToe | FootPart::RightToe)
45 }
46
47 pub(crate) fn is_heel(&self) -> bool {
48 matches!(self, FootPart::LeftHeel | FootPart::RightHeel)
49 }
50
51 pub(crate) fn side(&self) -> Option<Side> {
52 match self {
53 FootPart::None => None,
54 FootPart::LeftHeel => Some(Side::Left),
55 FootPart::LeftToe => Some(Side::Left),
56 FootPart::RightHeel => Some(Side::Right),
57 FootPart::RightToe => Some(Side::Right),
58 }
59 }
60
61 pub(crate) fn heel(side: Side) -> FootPart {
62 match side {
63 Side::Left => FootPart::LeftHeel,
64 Side::Right => FootPart::RightHeel,
65 }
66 }
67
68 pub(crate) fn toe(side: Side) -> FootPart {
69 match side {
70 Side::Left => FootPart::LeftToe,
71 Side::Right => FootPart::RightToe,
72 }
73 }
74
75 pub(crate) fn other_part(&self) -> FootPart {
76 if self.is_toe() {
77 FootPart::heel(self.side().unwrap())
78 } else if self.is_heel() {
79 FootPart::toe(self.side().unwrap())
80 } else {
81 FootPart::None
82 }
83 }
84}
85
86impl Debug for FootPart {
87 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88 <Self as Display>::fmt(self, f)
89 }
90}
91
92impl Display for FootPart {
93 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
94 match self {
95 FootPart::None => write!(f, "-"),
96 FootPart::LeftHeel => write!(f, "L"),
97 FootPart::LeftToe => write!(f, "l"),
98 FootPart::RightHeel => write!(f, "R"),
99 FootPart::RightToe => write!(f, "r"),
100 }
101 }
102}
103
104#[derive(Clone, PartialEq, Eq, Hash)]
106pub struct FootPlacement(pub Vec<FootPart>);
107
108impl FootPlacement {
109 pub fn new(columns: usize) -> Self {
110 FootPlacement(vec![FootPart::None; columns])
111 }
112
113 pub fn from_ddr_solo(left: FootPart, down: FootPart, up: FootPart, right: FootPart) -> Self {
114 FootPlacement(vec![left, down, up, right])
115 }
116
117 pub fn parse(s: &str) -> Option<Self> {
118 s.chars()
119 .map(|c| FootPart::parse(c))
120 .collect::<Option<_>>()
121 .map(|v| FootPlacement(v))
122 }
123
124 pub(crate) fn get_foot_part_index(&self, part: FootPart) -> Option<usize> {
125 self.0.iter().position(|&x| x == part)
126 }
127
128 pub(crate) fn get_foot_part_indices(&self) -> FootPartIndices {
129 FootPartIndices {
130 left_heel: self.get_foot_part_index(FootPart::LeftHeel),
131 left_toe: self.get_foot_part_index(FootPart::LeftToe),
132 right_heel: self.get_foot_part_index(FootPart::RightHeel),
133 right_toe: self.get_foot_part_index(FootPart::RightToe),
134 }
135 }
136
137 pub(crate) fn contains(&self, part: FootPart) -> bool {
138 self.0.iter().any(|&x| x == part)
139 }
140
141 pub(crate) fn is_bracketing(&self, side: Side) -> bool {
142 match side {
143 Side::Left => self.contains(FootPart::LeftToe) && self.contains(FootPart::LeftHeel),
144 Side::Right => self.contains(FootPart::RightToe) && self.contains(FootPart::RightHeel),
145 }
146 }
147
148 pub(crate) fn at(&self, column_idx: usize) -> FootPart {
149 self.0[column_idx]
150 }
151
152 pub(crate) fn at_mut(&mut self, column_idx: usize) -> &mut FootPart {
153 &mut self.0[column_idx]
154 }
155}
156
157impl Debug for FootPlacement {
158 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
159 <Self as Display>::fmt(self, f)
160 }
161}
162
163impl Display for FootPlacement {
164 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
165 for part in &self.0 {
166 <FootPart as Display>::fmt(&part, f)?;
167 }
168 Ok(())
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub struct FootPartIndices {
174 pub left_heel: Option<usize>,
175 pub left_toe: Option<usize>,
176 pub right_heel: Option<usize>,
177 pub right_toe: Option<usize>,
178}
179
180pub(crate) fn foot_placement_permutations(stage: &DanceStage, row: &Row) -> Vec<FootPlacement> {
181 let mut permutations = Vec::new();
182
183 permute_foot_placement(
184 &mut permutations,
185 stage,
186 row,
187 &FootPlacement::new(row.columns.len()),
188 0,
189 );
190
191 permutations
192}
193
194fn permute_foot_placement(
195 permutations: &mut Vec<FootPlacement>,
196 stage: &DanceStage,
197 row: &Row,
198 current_placement: &FootPlacement,
199 column: usize,
200) {
201 if column >= row.columns.len() {
202 let indices = current_placement.get_foot_part_indices();
203
204 let invalid_left_toe = matches!((indices.left_heel, indices.left_toe), (None, Some(_)));
207 let invalid_right_toe = matches!((indices.right_heel, indices.right_toe), (None, Some(_)));
208 if invalid_left_toe || invalid_right_toe {
209 return;
210 }
211
212 let valid_left_bracket = !current_placement.is_bracketing(Side::Left)
214 || stage.is_valid_bracket(indices.left_heel.unwrap(), indices.left_toe.unwrap());
215 let valid_right_bracket = !current_placement.is_bracketing(Side::Right)
216 || stage.is_valid_bracket(indices.right_heel.unwrap(), indices.right_toe.unwrap());
217 if !valid_left_bracket || !valid_right_bracket {
218 return;
219 }
220
221 permutations.push(current_placement.clone());
222 return;
223 }
224
225 if let Some(note) = row.columns.get(column)
226 && note.require_press()
227 {
228 let mut new_placement = current_placement.clone();
229 for foot_part in FootPart::all_except_none() {
230 if current_placement.contains(foot_part) {
231 continue;
232 }
233
234 new_placement.0[column] = foot_part;
235 permute_foot_placement(permutations, stage, row, &new_placement, column + 1);
236 }
237
238 return;
239 }
240
241 permute_foot_placement(permutations, stage, row, ¤t_placement, column + 1);
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use danceparser::NoteKind;
248
249 #[test]
250 fn test_tap_permutations() {
251 let permutations = foot_placement_permutations(
252 &DanceStage::ddr_solo(),
253 &Row {
254 columns: vec![
255 NoteKind::Tap,
256 NoteKind::Empty,
257 NoteKind::Empty,
258 NoteKind::Empty,
259 ],
260 },
261 );
262
263 assert_eq!(
264 permutations,
265 vec![
266 FootPlacement::from_ddr_solo(
267 FootPart::LeftHeel,
268 FootPart::None,
269 FootPart::None,
270 FootPart::None,
271 ),
272 FootPlacement::from_ddr_solo(
273 FootPart::RightHeel,
274 FootPart::None,
275 FootPart::None,
276 FootPart::None,
277 )
278 ]
279 );
280 }
281
282 #[test]
283 fn test_jump_permutations() {
284 let permutations = foot_placement_permutations(
285 &DanceStage::ddr_solo(),
286 &Row {
287 columns: vec![
288 NoteKind::Tap,
289 NoteKind::Empty,
290 NoteKind::Empty,
291 NoteKind::Tap,
292 ],
293 },
294 );
295
296 assert_eq!(
297 permutations,
298 vec![
299 FootPlacement::from_ddr_solo(
300 FootPart::LeftHeel,
301 FootPart::None,
302 FootPart::None,
303 FootPart::RightHeel,
304 ),
305 FootPlacement::from_ddr_solo(
306 FootPart::RightHeel,
307 FootPart::None,
308 FootPart::None,
309 FootPart::LeftHeel,
310 )
311 ]
312 );
313 }
314
315 #[test]
316 fn test_bracket_permutations() {
317 let permutations = foot_placement_permutations(
318 &DanceStage::ddr_solo(),
319 &Row {
320 columns: vec![
321 NoteKind::Tap,
322 NoteKind::Tap,
323 NoteKind::Empty,
324 NoteKind::Empty,
325 ],
326 },
327 );
328
329 assert_eq!(
330 permutations,
331 vec![
332 FootPlacement::from_ddr_solo(
333 FootPart::LeftHeel,
334 FootPart::LeftToe,
335 FootPart::None,
336 FootPart::None
337 ),
338 FootPlacement::from_ddr_solo(
339 FootPart::LeftHeel,
340 FootPart::RightHeel,
341 FootPart::None,
342 FootPart::None
343 ),
344 FootPlacement::from_ddr_solo(
345 FootPart::LeftToe,
346 FootPart::LeftHeel,
347 FootPart::None,
348 FootPart::None
349 ),
350 FootPlacement::from_ddr_solo(
351 FootPart::RightHeel,
352 FootPart::LeftHeel,
353 FootPart::None,
354 FootPart::None
355 ),
356 FootPlacement::from_ddr_solo(
357 FootPart::RightHeel,
358 FootPart::RightToe,
359 FootPart::None,
360 FootPart::None
361 ),
362 FootPlacement::from_ddr_solo(
363 FootPart::RightToe,
364 FootPart::RightHeel,
365 FootPart::None,
366 FootPart::None
367 )
368 ]
369 );
370 }
371}