1use strum_macros::EnumString;
11
12#[derive(PartialEq, Eq, EnumString, Debug, Clone, Copy)]
21pub enum BaseMoveToken {
22 U,
23 D,
24 L,
25 R,
26 F,
27 B,
28}
29
30impl std::fmt::Display for BaseMoveToken {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{:?}", self)
33 }
34}
35
36#[derive(PartialEq, Eq, Debug, Clone, Copy)]
40pub enum Direction {
41 Normal,
42 Prime,
43 Double,
44}
45
46impl std::fmt::Display for Direction {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Direction::Normal => write!(f, ""),
50 Direction::Prime => write!(f, "'"),
51 Direction::Double => write!(f, "2"),
52 }
53 }
54}
55
56#[derive(PartialEq, Eq, Debug, Clone, Copy)]
58pub struct MoveInstance {
59 pub basemove: BaseMoveToken,
60 pub dir: Direction,
61}
62
63impl MoveInstance {
64 pub fn new(basemove: BaseMoveToken, dir: Direction) -> Self {
65 Self { basemove, dir }
66 }
67
68 pub fn invert(&self) -> Self {
69 Self::new(
70 self.basemove,
71 match self.dir {
72 Direction::Normal => Direction::Prime,
73 Direction::Prime => Direction::Normal,
74 x => x,
75 },
76 )
77 }
78}
79
80impl std::fmt::Display for MoveInstance {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 write!(f, "{}{}", self.basemove, self.dir)
83 }
84}
85
86pub struct MoveSequence(pub Vec<MoveInstance>);
89
90impl MoveSequence {
91 pub fn get_moves(&self) -> &Vec<MoveInstance> {
92 &self.0
93 }
94 pub fn get_moves_mut(&mut self) -> &mut Vec<MoveInstance> {
95 &mut self.0
96 }
97
98 pub fn invert(&self) -> Self {
99 let mut moves = vec![];
100 for m in self.get_moves().iter().rev() {
101 moves.push(m.invert());
102 }
103 MoveSequence(moves)
104 }
105}
106
107impl std::fmt::Display for MoveSequence {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 let mut strs = vec![];
110 for m in self.get_moves().iter() {
111 strs.push(m.to_string());
112 }
113 write!(f, "{}", strs.join(" "))
114 }
115}
116
117pub struct Commutator(pub MoveSequence, pub MoveSequence);
141
142pub struct Conjugate(pub MoveSequence, pub Commutator);
172
173struct Move {
180 pub cp_change: [u8; 8], pub co_change: [i8; 8],
182 pub ep_change: [u8; 12],
183 pub eo_change: [i8; 12],
184}
185
186#[macro_export]
198macro_rules! cube_move {
199 ($basemove: ident, $dir:ident) => {{
200 MoveInstance {
201 basemove: BaseMoveToken::$basemove,
202 dir: Direction::$dir,
203 }
204 }};
205}
206
207macro_rules! apply_permutation {
208 ($og_state: expr, $delta: expr) => {{
209 if $og_state.len() != $delta.len() {
210 panic!("Size mismatch in applying permutation");
211 } else {
212 let mut new_array = $og_state.clone();
213 for i in 0..$og_state.len() {
214 new_array[$delta[i] as usize] = $og_state[i];
215 }
216 new_array
217 }
218 }};
219}
220
221macro_rules! apply_orientation {
222 ($og_state: expr, $delta: expr, $num_orientations: expr) => {{
223 if $og_state.len() != $delta.len() {
224 panic!("Size mismatch in applying orientation");
225 } else {
226 let mut new_array = $og_state.clone();
227 for i in 0..$og_state.len() {
228 new_array[i] = (($og_state[i] + $delta[i] + $num_orientations) % $num_orientations);
229 if new_array[i] == 2 {
230 new_array[i] = -1;
231 }
232 }
233 new_array
234 }
235 }};
236}
237
238pub(crate) fn get_basemove_pos(token: BaseMoveToken) -> u8 {
239 match token {
240 BaseMoveToken::U => 5,
241 BaseMoveToken::D => 4,
242 BaseMoveToken::L => 3,
243 BaseMoveToken::R => 2,
244 BaseMoveToken::F => 1,
245 BaseMoveToken::B => 0,
246 }
247}
248
249fn get_antipode(token: BaseMoveToken) -> BaseMoveToken {
250 match token {
251 BaseMoveToken::U => BaseMoveToken::D,
252 BaseMoveToken::D => BaseMoveToken::U,
253 BaseMoveToken::L => BaseMoveToken::R,
254 BaseMoveToken::R => BaseMoveToken::L,
255 BaseMoveToken::F => BaseMoveToken::B,
256 BaseMoveToken::B => BaseMoveToken::F,
257 }
258}
259
260pub(crate) fn get_allowed_post_moves(prev_bv: u8, last_move: Option<BaseMoveToken>) -> u8 {
262 if let Some(lm) = last_move {
263 let antipode = get_antipode(lm);
264 if prev_bv & (1 << get_basemove_pos(antipode)) != 0 {
265 (1 << get_basemove_pos(lm)) + (1 << get_basemove_pos(antipode))
267 } else {
268 1 << get_basemove_pos(lm)
269 }
270 } else {
271 0
272 }
273}
274
275pub fn allowed_moves_after_seq(moves: &MoveSequence) -> u8 {
283 let sol = moves.get_moves();
284 match sol.len() {
285 0 => 0,
286 1 => {
287 let last_move = sol[sol.len() - 1];
288 1 << get_basemove_pos(last_move.basemove)
289 }
290 _ => {
291 let last_move = sol[sol.len() - 1];
292 let second_to_last = sol[sol.len() - 2];
293 if get_antipode(last_move.basemove) == second_to_last.basemove {
294 (1 << get_basemove_pos(last_move.basemove))
295 + (1 << get_basemove_pos(second_to_last.basemove))
296 } else {
297 1 << get_basemove_pos(last_move.basemove)
298 }
299 }
300 }
301}
302
303#[derive(Clone, Debug, Hash, PartialEq, Eq)]
305pub struct CubeState {
306 pub cp: [u8; 8],
307 pub co: [i8; 8],
308 pub ep: [u8; 12],
309 pub eo: [i8; 12],
310}
311
312impl Default for CubeState {
313 fn default() -> CubeState {
314 CubeState {
315 cp: [0, 1, 2, 3, 4, 5, 6, 7],
316 co: [0 as i8; 8],
317 ep: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
318 eo: [0 as i8; 12],
319 }
320 }
321}
322
323fn get_move_matrix(mov: &BaseMoveToken) -> Move {
324 match mov {
325 BaseMoveToken::U => MOVE_U,
326 BaseMoveToken::D => MOVE_D,
327 BaseMoveToken::L => MOVE_L,
328 BaseMoveToken::R => MOVE_R,
329 BaseMoveToken::F => MOVE_F,
330 BaseMoveToken::B => MOVE_B,
331 }
332}
333
334fn factorial(num: u32) -> u32 {
335 match num {
336 0 => 1,
337 1 => 1,
338 _ => factorial(num - 1) * num,
339 }
340}
341
342fn get_index_of_permutation(perm: &[u8]) -> u32 {
346 let mut fin = 0;
348 for i in 0..perm.len() {
349 let mut res = 0;
350 for j in (i + 1)..perm.len() {
351 if perm[j] < perm[i] {
352 res += 1;
353 }
354 }
355 fin += res * factorial((perm.len() - i - 1) as u32);
356 }
357 fin as u32
358}
359
360fn get_index_of_orientation(ori: &[i8], num_orientations: u8) -> u16 {
364 let mut result = 0;
365 for (i, val) in ori.iter().enumerate() {
366 if i == ori.len() - 1 {
367 break;
368 }
369 let pos = (val + num_orientations as i8) % num_orientations as i8;
370 result += pos as u16;
371 if i != ori.len() - 2 {
372 result *= num_orientations as u16;
373 }
374 }
375 result
376}
377
378pub fn get_index_of_state(state: &CubeState) -> (u32, u16, u64) {
385 let cp_index = get_index_of_permutation(&state.cp);
386 let co_index = get_index_of_orientation(&state.co, 3);
387 let corner_index = cp_index * u32::pow(3, 7) + (co_index as u32);
388 let ep_index = get_index_of_permutation(&state.ep) as u64;
389 let eo_index = get_index_of_orientation(&state.eo, 2);
390 (corner_index, eo_index, ep_index)
391}
392
393impl CubeState {
394 fn apply_basemove(&self, m: &BaseMoveToken) -> Self {
395 let mov = get_move_matrix(m);
396 let oriented_corners = apply_orientation!(&self.co, &mov.co_change, 3);
397 let oriented_edges = apply_orientation!(&self.eo, &mov.eo_change, 2);
398 CubeState {
399 cp: apply_permutation!(&self.cp, &mov.cp_change),
400 co: apply_permutation!(oriented_corners, &mov.cp_change),
401 ep: apply_permutation!(&self.ep, &mov.ep_change),
402 eo: apply_permutation!(oriented_edges, &mov.ep_change),
403 }
404 }
405
406 pub fn apply_move_instance(&self, m: &MoveInstance) -> Self {
408 let num_turns = match &m.dir {
409 Direction::Normal => 1,
410 Direction::Prime => 3,
411 Direction::Double => 2,
412 };
413 (0..num_turns).fold(self.clone(), |acc, _| acc.apply_basemove(&m.basemove))
414 }
415
416 pub fn apply_move_instances(&self, moves: &MoveSequence) -> Self {
418 moves
419 .get_moves()
420 .iter()
421 .fold(self.clone(), |acc, mov| acc.apply_move_instance(&mov))
422 }
423
424 }
428
429pub const ALL_MOVES: [MoveInstance; 18] = [
431 cube_move!(U, Normal),
432 cube_move!(U, Prime),
433 cube_move!(U, Double),
434 cube_move!(D, Normal),
435 cube_move!(D, Prime),
436 cube_move!(D, Double),
437 cube_move!(L, Normal),
438 cube_move!(L, Prime),
439 cube_move!(L, Double),
440 cube_move!(R, Normal),
441 cube_move!(R, Prime),
442 cube_move!(R, Double),
443 cube_move!(F, Normal),
444 cube_move!(F, Prime),
445 cube_move!(F, Double),
446 cube_move!(B, Normal),
447 cube_move!(B, Prime),
448 cube_move!(B, Double),
449];
450
451const MOVE_U: Move = Move {
452 cp_change: [1, 2, 3, 0, 4, 5, 6, 7],
453 co_change: [0, 0, 0, 0, 0, 0, 0, 0],
454 ep_change: [1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11],
455 eo_change: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
456};
457
458const MOVE_D: Move = Move {
459 cp_change: [0, 1, 2, 3, 5, 6, 7, 4],
460 co_change: [0, 0, 0, 0, 0, 0, 0, 0],
461 ep_change: [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 8],
462 eo_change: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
463};
464
465const MOVE_R: Move = Move {
466 cp_change: [0, 6, 1, 3, 4, 2, 5, 7],
467 co_change: [0, -1, 1, 0, 0, -1, 1, 0],
468 ep_change: [0, 5, 2, 3, 4, 9, 1, 7, 8, 6, 10, 11],
469 eo_change: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
470};
471
472const MOVE_L: Move = Move {
473 cp_change: [3, 1, 2, 4, 7, 5, 6, 0],
474 co_change: [1, 0, 0, -1, 1, 0, 0, -1],
475 ep_change: [0, 1, 2, 7, 3, 5, 6, 11, 8, 9, 10, 4],
476 eo_change: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
477};
478
479const MOVE_F: Move = Move {
480 cp_change: [0, 1, 5, 2, 3, 4, 6, 7],
481 co_change: [0, 0, -1, 1, -1, 1, 0, 0],
482 ep_change: [0, 1, 6, 3, 4, 5, 8, 2, 7, 9, 10, 11],
483 eo_change: [0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0],
484};
485
486const MOVE_B: Move = Move {
487 cp_change: [7, 0, 2, 3, 4, 5, 1, 6],
488 co_change: [-1, 1, 0, 0, 0, 0, -1, 1],
489 ep_change: [4, 1, 2, 3, 10, 0, 6, 7, 8, 9, 5, 11],
490 eo_change: [1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0],
491};