1use super::{
2 types::{Facelet as F, FaceletState, DEFAULT_STATE},
3 FaceletTarget,
4};
5use crate::{
6 commutator::types::{Commutator, Cycle, ThreeCycle},
7 error::Error,
8 moves::{Alg, Move, MoveCount, MoveKind},
9};
10use constants::*;
11use std::{
12 collections::HashSet,
13 fmt,
14 ops::{Index, IndexMut, Mul},
15};
16
17#[derive(Debug, PartialEq, Eq, Clone, Hash)]
18pub struct FaceletCube(FaceletState);
19
20impl Default for FaceletCube {
21 fn default() -> Self {
22 Self(DEFAULT_STATE)
23 }
24}
25
26impl FaceletCube {
27 pub fn new(state: FaceletState) -> Self {
28 Self(state)
29 }
30
31 pub fn is_solved(&self) -> bool {
32 self.0
33 .iter()
34 .map(F::as_color)
35 .collect::<Vec<_>>()
36 .chunks(9)
37 .all(|side| side.iter().all(|c| Some(c) == side.first()))
38 }
39
40 pub fn apply_move(&self, m: Move) -> Self {
41 self * &FaceletCube::from(m)
42 }
43
44 pub fn apply_alg(&self, alg: &Alg) -> Self {
45 alg.iter().fold(self.clone(), |acc, m| acc.apply_move(*m))
46 }
47
48 pub fn apply_commutator(&self, commutator: &Commutator) -> Self {
49 self.apply_alg(&commutator.expand())
50 }
51}
52
53impl From<Move> for FaceletCube {
54 fn from(value: Move) -> Self {
55 let state = match value.kind {
56 MoveKind::U => U_CUBE,
57 MoveKind::F => F_CUBE,
58 MoveKind::R => R_CUBE,
59 MoveKind::B => B_CUBE,
60 MoveKind::L => L_CUBE,
61 MoveKind::D => D_CUBE,
62 MoveKind::M => M_CUBE,
63 MoveKind::S => S_CUBE,
64 MoveKind::E => E_CUBE,
65 MoveKind::X => X_CUBE,
66 MoveKind::Y => Y_CUBE,
67 MoveKind::Z => Z_CUBE,
68 MoveKind::Fw => FW_CUBE,
69 MoveKind::Lw => LW_CUBE,
70 MoveKind::Dw => DW_CUBE,
71 MoveKind::Uw => UW_CUBE,
72 MoveKind::Rw => RW_CUBE,
73 MoveKind::Bw => BW_CUBE,
74 };
75
76 match value.count {
77 MoveCount::Simple => state,
78 MoveCount::Double => state.mul(&state),
79 MoveCount::Prime => state.mul(&state).mul(&state),
80 }
81 }
82}
83
84impl<T> TryFrom<Cycle<T>> for FaceletCube
85where
86 T: Clone + Copy + FaceletTarget + fmt::Display,
87{
88 type Error = Error;
89
90 fn try_from(value: Cycle<T>) -> Result<Self, Self::Error> {
91 FaceletCube::default().cycle(value)
92 }
93}
94
95impl Index<usize> for FaceletCube {
96 type Output = F;
97
98 fn index(&self, index: usize) -> &Self::Output {
99 &self.0[index]
100 }
101}
102
103impl IndexMut<usize> for FaceletCube {
104 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
105 &mut self.0[index]
106 }
107}
108
109impl Index<F> for FaceletCube {
110 type Output = F;
111
112 fn index(&self, index: F) -> &Self::Output {
113 &self[index as usize]
114 }
115}
116
117impl IndexMut<F> for FaceletCube {
118 fn index_mut(&mut self, index: F) -> &mut Self::Output {
119 &mut self[index as usize]
120 }
121}
122
123impl Mul<Self> for &FaceletCube {
124 type Output = FaceletCube;
125
126 fn mul(self, rhs: Self) -> Self::Output {
127 let mut res = FaceletCube::default();
128
129 for (i, &f) in rhs.0.iter().enumerate() {
130 res[i] = self[f];
131 }
132
133 res
134 }
135}
136
137impl fmt::Display for FaceletCube {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 let s = self
140 .0
141 .iter()
142 .map(|f| f.as_color().to_string())
143 .collect::<String>();
144
145 write!(f, "{s}")
146 }
147}
148
149impl ThreeCycle for FaceletCube {
150 fn cycle<T>(self, cycle: Cycle<T>) -> Result<Self, Error>
151 where
152 T: fmt::Display + Clone + Copy + FaceletTarget,
153 {
154 let mut res = self.clone();
155 let first = cycle.first().to_facelets();
156 let second = cycle.second().to_facelets();
157 let third = cycle.third().to_facelets();
158 let expected_count = first.len() + second.len() + third.len();
159 let count = first
160 .iter()
161 .chain(second.iter())
162 .chain(third.iter())
163 .collect::<HashSet<_>>()
164 .len();
165
166 if count == expected_count {
167 for i in 0..count / 3 {
168 res.0[first[i] as usize] = self.0[third[i] as usize];
169 res.0[second[i] as usize] = self.0[first[i] as usize];
170 res.0[third[i] as usize] = self.0[second[i] as usize];
171 }
172
173 Ok(res)
174 } else {
175 Err(Error::InvalidThreeCycle(cycle.to_string()))
176 }
177 }
178}
179
180#[rustfmt::skip]
181mod constants {
182 use super::*;
183
184 pub const U_CUBE: FaceletCube = FaceletCube([
185 F::U6, F::U3, F::U0, F::U7, F::U4, F::U1, F::U8, F::U5, F::U2,
186 F::B0, F::B1, F::B2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
187 F::R0, F::R1, F::R2, F::F3, F::F4, F::F5, F::F6, F::F7, F::F8,
188 F::D0, F::D1, F::D2, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
189 F::F0, F::F1, F::F2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
190 F::L0, F::L1, F::L2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
191 ]);
192
193 pub const R_CUBE: FaceletCube = FaceletCube([
194 F::U0, F::U1, F::F2, F::U3, F::U4, F::F5, F::U6, F::U7, F::F8,
195 F::R6, F::R3, F::R0, F::R7, F::R4, F::R1, F::R8, F::R5, F::R2,
196 F::F0, F::F1, F::D2, F::F3, F::F4, F::D5, F::F6, F::F7, F::D8,
197 F::D0, F::D1, F::B6, F::D3, F::D4, F::B3, F::D6, F::D7, F::B0,
198 F::L0, F::L1, F::L2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
199 F::U8, F::B1, F::B2, F::U5, F::B4, F::B5, F::U2, F::B7, F::B8,
200 ]);
201
202 pub const F_CUBE: FaceletCube = FaceletCube([
203 F::U0, F::U1, F::U2, F::U3, F::U4, F::U5, F::L8, F::L5, F::L2,
204 F::U6, F::R1, F::R2, F::U7, F::R4, F::R5, F::U8, F::R7, F::R8,
205 F::F6, F::F3, F::F0, F::F7, F::F4, F::F1, F::F8, F::F5, F::F2,
206 F::R6, F::R3, F::R0, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
207 F::L0, F::L1, F::D0, F::L3, F::L4, F::D1, F::L6, F::L7, F::D2,
208 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
209 ]);
210
211 pub const D_CUBE: FaceletCube = FaceletCube([
212 F::U0, F::U1, F::U2, F::U3, F::U4, F::U5, F::U6, F::U7, F::U8,
213 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::F6, F::F7, F::F8,
214 F::F0, F::F1, F::F2, F::F3, F::F4, F::F5, F::L6, F::L7, F::L8,
215 F::D6, F::D3, F::D0, F::D7, F::D4, F::D1, F::D8, F::D5, F::D2,
216 F::L0, F::L1, F::L2, F::L3, F::L4, F::L5, F::B6, F::B7, F::B8,
217 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::R6, F::R7, F::R8,
218 ]);
219
220 pub const L_CUBE: FaceletCube = FaceletCube([
221 F::B8, F::U1, F::U2, F::B5, F::U4, F::U5, F::B2, F::U7, F::U8,
222 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
223 F::U0, F::F1, F::F2, F::U3, F::F4, F::F5, F::U6, F::F7, F::F8,
224 F::F0, F::D1, F::D2, F::F3, F::D4, F::D5, F::F6, F::D7, F::D8,
225 F::L6, F::L3, F::L0, F::L7, F::L4, F::L1, F::L8, F::L5, F::L2,
226 F::B0, F::B1, F::D6, F::B3, F::B4, F::D3, F::B6, F::B7, F::D0,
227 ]);
228
229 pub const B_CUBE: FaceletCube = FaceletCube([
230 F::R2, F::R5, F::R8, F::U3, F::U4, F::U5, F::U6, F::U7, F::U8,
231 F::R0, F::R1, F::D8, F::R3, F::R4, F::D7, F::R6, F::R7, F::D6,
232 F::F0, F::F1, F::F2, F::F3, F::F4, F::F5, F::F6, F::F7, F::F8,
233 F::D0, F::D1, F::D2, F::D3, F::D4, F::D5, F::L0, F::L3, F::L6,
234 F::U2, F::L1, F::L2, F::U1, F::L4, F::L5, F::U0, F::L7, F::L8,
235 F::B6, F::B3, F::B0, F::B7, F::B4, F::B1, F::B8, F::B5, F::B2,
236 ]);
237
238 pub const M_CUBE: FaceletCube = FaceletCube([
239 F::U0, F::B7, F::U2, F::U3, F::B4, F::U5, F::U6, F::B1, F::U8,
240 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
241 F::F0, F::U1, F::F2, F::F3, F::U4, F::F5, F::F6, F::U7, F::F8,
242 F::D0, F::F1, F::D2, F::D3, F::F4, F::D5, F::D6, F::F7, F::D8,
243 F::L0, F::L1, F::L2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
244 F::B0, F::D7, F::B2, F::B3, F::D4, F::B5, F::B6, F::D1, F::B8,
245 ]);
246
247 pub const E_CUBE: FaceletCube = FaceletCube([
248 F::U0, F::U1, F::U2, F::U3, F::U4, F::U5, F::U6, F::U7, F::U8,
249 F::R0, F::R1, F::R2, F::F3, F::F4, F::F5, F::R6, F::R7, F::R8,
250 F::F0, F::F1, F::F2, F::L3, F::L4, F::L5, F::F6, F::F7, F::F8,
251 F::D0, F::D1, F::D2, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
252 F::L0, F::L1, F::L2, F::B3, F::B4, F::B5, F::L6, F::L7, F::L8,
253 F::B0, F::B1, F::B2, F::R3, F::R4, F::R5, F::B6, F::B7, F::B8,
254 ]);
255
256 pub const S_CUBE: FaceletCube = FaceletCube([
257 F::U0, F::U1, F::U2, F::L7, F::L4, F::L1, F::U6, F::U7, F::U8,
258 F::R0, F::U3, F::R2, F::R3, F::U4, F::R5, F::R6, F::U5, F::R8,
259 F::F0, F::F1, F::F2, F::F3, F::F4, F::F5, F::F6, F::F7, F::F8,
260 F::D0, F::D1, F::D2, F::R7, F::R4, F::R1, F::D6, F::D7, F::D8,
261 F::L0, F::D3, F::L2, F::L3, F::D4, F::L5, F::L6, F::D5, F::L8,
262 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
263 ]);
264
265 pub const X_CUBE: FaceletCube = FaceletCube([
266 F::F0, F::F1, F::F2, F::F3, F::F4, F::F5, F::F6, F::F7, F::F8,
267 F::R6, F::R3, F::R0, F::R7, F::R4, F::R1, F::R8, F::R5, F::R2,
268 F::D0, F::D1, F::D2, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
269 F::B8, F::B7, F::B6, F::B5, F::B4, F::B3, F::B2, F::B1, F::B0,
270 F::L2, F::L5, F::L8, F::L1, F::L4, F::L7, F::L0, F::L3, F::L6,
271 F::U8, F::U7, F::U6, F::U5, F::U4, F::U3, F::U2, F::U1, F::U0,
272 ]);
273
274 pub const Y_CUBE: FaceletCube = FaceletCube([
275 F::U6, F::U3, F::U0, F::U7, F::U4, F::U1, F::U8, F::U5, F::U2,
276 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
277 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
278 F::D2, F::D5, F::D8, F::D1, F::D4, F::D7, F::D0, F::D3, F::D6,
279 F::F0, F::F1, F::F2, F::F3, F::F4, F::F5, F::F6, F::F7, F::F8,
280 F::L0, F::L1, F::L2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
281 ]);
282
283 pub const Z_CUBE: FaceletCube = FaceletCube([
284 F::L6, F::L3, F::L0, F::L7, F::L4, F::L1, F::L8, F::L5, F::L2,
285 F::U6, F::U3, F::U0, F::U7, F::U4, F::U1, F::U8, F::U5, F::U2,
286 F::F6, F::F3, F::F0, F::F7, F::F4, F::F1, F::F8, F::F5, F::F2,
287 F::R6, F::R3, F::R0, F::R7, F::R4, F::R1, F::R8, F::R5, F::R2,
288 F::D6, F::D3, F::D0, F::D7, F::D4, F::D1, F::D8, F::D5, F::D2,
289 F::B2, F::B5, F::B8, F::B1, F::B4, F::B7, F::B0, F::B3, F::B6,
290 ]);
291
292 pub const UW_CUBE: FaceletCube = FaceletCube([
293 F::U6, F::U3, F::U0, F::U7, F::U4, F::U1, F::U8, F::U5, F::U2,
294 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::R6, F::R7, F::R8,
295 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::F6, F::F7, F::F8,
296 F::D0, F::D1, F::D2, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
297 F::F0, F::F1, F::F2, F::F3, F::F4, F::F5, F::L6, F::L7, F::L8,
298 F::L0, F::L1, F::L2, F::L3, F::L4, F::L5, F::B6, F::B7, F::B8,
299 ]);
300
301 pub const RW_CUBE: FaceletCube = FaceletCube([
302 F::U0, F::F1, F::F2, F::U3, F::F4, F::F5, F::U6, F::F7, F::F8,
303 F::R6, F::R3, F::R0, F::R7, F::R4, F::R1, F::R8, F::R5, F::R2,
304 F::F0, F::D1, F::D2, F::F3, F::D4, F::D5, F::F6, F::D7, F::D8,
305 F::D0, F::B7, F::B6, F::D3, F::B4, F::B3, F::D6, F::B1, F::B0,
306 F::L0, F::L1, F::L2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
307 F::U8, F::U7, F::B2, F::U5, F::U4, F::B5, F::U2, F::U1, F::B8,
308 ]);
309
310 pub const FW_CUBE: FaceletCube = FaceletCube([
311 F::U0, F::U1, F::U2, F::L7, F::L4, F::L1, F::L8, F::L5, F::L2,
312 F::U6, F::U3, F::R2, F::U7, F::U4, F::R5, F::U8, F::U5, F::R8,
313 F::F6, F::F3, F::F0, F::F7, F::F4, F::F1, F::F8, F::F5, F::F2,
314 F::R6, F::R3, F::R0, F::R7, F::R4, F::R1, F::D6, F::D7, F::D8,
315 F::L0, F::D3, F::D0, F::L3, F::D4, F::D1, F::L6, F::D5, F::D2,
316 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
317 ]);
318
319 pub const DW_CUBE: FaceletCube = FaceletCube([
320 F::U0, F::U1, F::U2, F::U3, F::U4, F::U5, F::U6, F::U7, F::U8,
321 F::R0, F::R1, F::R2, F::F3, F::F4, F::F5, F::F6, F::F7, F::F8,
322 F::F0, F::F1, F::F2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
323 F::D6, F::D3, F::D0, F::D7, F::D4, F::D1, F::D8, F::D5, F::D2,
324 F::L0, F::L1, F::L2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
325 F::B0, F::B1, F::B2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
326 ]);
327
328 pub const LW_CUBE: FaceletCube = FaceletCube([
329 F::B8, F::B7, F::U2, F::B5, F::B4, F::U5, F::B2, F::B1, F::U8,
330 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
331 F::U0, F::U1, F::F2, F::U3, F::U4, F::F5, F::U6, F::U7, F::F8,
332 F::F0, F::F1, F::D2, F::F3, F::F4, F::D5, F::F6, F::F7, F::D8,
333 F::L6, F::L3, F::L0, F::L7, F::L4, F::L1, F::L8, F::L5, F::L2,
334 F::B0, F::D7, F::D6, F::B3, F::D4, F::D3, F::B6, F::D1, F::D0,
335 ]);
336
337 pub const BW_CUBE: FaceletCube = FaceletCube([
338 F::R2, F::R5, F::R8, F::R1, F::R4, F::R7, F::U6, F::U7, F::U8,
339 F::R0, F::D5, F::D8, F::R3, F::D4, F::D7, F::R6, F::D3, F::D6,
340 F::F0, F::F1, F::F2, F::F3 ,F::F4, F::F5, F::F6, F::F7, F::F8,
341 F::D0, F::D1, F::D2, F::L1, F::L4, F::L7, F::L0, F::L3, F::L6,
342 F::U2, F::U5, F::L2, F::U1, F::U4, F::L5, F::U0, F::U3, F::L8,
343 F::B6, F::B3, F::B0, F::B7, F::B4, F::B1, F::B8, F::B5, F::B2,
344 ]);
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350 use crate::{
351 alg,
352 moves::Inverse,
353 sticker::{Corner, Edge},
354 };
355
356 #[test]
357 fn test_primitive_moves() {
358 let scramble = alg!("U R F D L B");
359 let cube = FaceletCube::default().apply_alg(&scramble);
360 let expected = "BBDBUFLLFURRURBDDLUFRUFRLLBFDRFDRUBBRLFULFBDDFUURBLDDL";
361
362 assert_eq!(expected, cube.to_string());
363 }
364
365 #[test]
366 fn test_scramble() {
367 let scramble = alg!("D F2 U' B2 F2 U2 L2 D B2 D2 U' F2 U' F2 R' B R' D R2 D2 R' F' L R'");
368 let cube = FaceletCube::default().apply_alg(&scramble);
369 let expected = "FRDRULDFFRBLLRRFUURDDUFFLBLURDBDFLURUDBDLBUDFBURFBLBLB";
370
371 assert_eq!(expected, cube.to_string());
372 }
373
374 #[test]
375 fn test_slice_moves() {
376 let scramble = alg!("M E S E' S' M'");
377 let cube = FaceletCube::default().apply_alg(&scramble);
378 let expected = "UUUUBUUUURRRRURRRRFFFFLFFFFDDDDFDDDDLLLLDLLLLBBBBRBBBB";
379
380 assert_eq!(expected, cube.to_string());
381 }
382
383 #[test]
384 fn test_rotations() {
385 let scramble = alg!("x y z");
386 let cube = FaceletCube::default().apply_alg(&scramble);
387 let expected = "DDDDDDDDDFFFFFFFFFRRRRRRRRRUUUUUUUUUBBBBBBBBBLLLLLLLLL";
388
389 assert_eq!(expected, cube.to_string());
390 }
391
392 #[test]
393 fn test_wide_moves() {
394 let scramble = alg!("u r f d l b");
395 let cube = FaceletCube::default().apply_alg(&scramble);
396 let expected = "BDDUDDLUFURRDLLDFLURRLFBLFBFRRUULUUBRFFRRDBBDFFUBBBDLL";
397
398 assert_eq!(expected, cube.to_string());
399 }
400
401 #[test]
402 fn test_solved_state() {
403 let cube = FaceletCube::default();
404
405 assert!(cube.is_solved());
406
407 let scramble = alg!("x y2 z'");
408 let cube = FaceletCube::default().apply_alg(&scramble);
409
410 assert!(cube.is_solved());
411
412 let scramble = alg!("R U R' U'");
413 let cube = FaceletCube::default().apply_alg(&scramble);
414
415 assert!(!cube.is_solved());
416 }
417
418 #[test]
419 fn test_edge_cycle() {
420 let cycle = Cycle::new(Edge::UF, Edge::UB, Edge::FL);
421 let cube = FaceletCube::default().cycle(cycle).unwrap();
422
423 #[rustfmt::skip]
424 let expecte = FaceletCube([
425 F::U0, F::U7, F::U2, F::U3, F::U4, F::U5, F::U6, F::F3, F::U8,
426 F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8,
427 F::F0, F::L5, F::F2, F::U1, F::F4, F::F5, F::F6, F::F7, F::F8,
428 F::D0, F::D1, F::D2, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
429 F::L0, F::L1, F::L2, F::L3, F::L4, F::B1, F::L6, F::L7, F::L8,
430 F::B0, F::F1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
431 ]);
432
433 assert_eq!(expecte, cube);
434
435 let cube = cube.cycle(cycle.inverse()).unwrap();
436
437 assert_eq!(FaceletCube::default(), cube);
438 }
439
440 #[test]
441 fn test_corner_cycle() {
442 let cycle = Cycle::new(Corner::UFR, Corner::ULF, Corner::RFD);
443 let cube = FaceletCube::default().cycle(cycle).unwrap();
444
445 #[rustfmt::skip]
446 let expecte = FaceletCube([
447 F::U0, F::U1, F::U2, F::U3, F::U4, F::U5, F::U8, F::U7, F::R6,
448 F::D2, F::R1, F::R2, F::R3, F::R4, F::R5, F::U6, F::R7, F::R8,
449 F::R0, F::F1, F::F8, F::F3, F::F4, F::F5, F::F6, F::F7, F::L2,
450 F::D0, F::D1, F::F0, F::D3, F::D4, F::D5, F::D6, F::D7, F::D8,
451 F::L0, F::L1, F::F2, F::L3, F::L4, F::L5, F::L6, F::L7, F::L8,
452 F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8,
453 ]);
454
455 assert_eq!(expecte, cube);
456
457 let cube = cube.cycle(cycle.inverse()).unwrap();
458
459 assert_eq!(FaceletCube::default(), cube);
460 }
461}