three_style_lib/facelet/
state.rs

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}