use rand::prelude::*; use std::fmt; use std::ops::Mul; use self::{Corner::*, Edge::*, Move::*}; use crate::constants::*; use crate::error::Error; use crate::symmetries; use crate::{facelet::*, moves::*}; /// Represents the 8 corners on the cube, described by the layer they are on. /// /// Example: `ULB` (Up, Left, Bottom). #[rustfmt::skip] #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] pub enum Corner { URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB, } impl fmt::Display for Corner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } impl TryFrom for Corner { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(URF), 1 => Ok(UFL), 2 => Ok(ULB), 3 => Ok(UBR), 4 => Ok(DFR), 5 => Ok(DLF), 6 => Ok(DBL), 7 => Ok(DRB), _ => Err(Error::InvalidCorner), } } } /// Represents the 12 edges on the cube, described by the layer they are on. /// /// Example: `BL` (Bottom, Left). #[rustfmt::skip] #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] pub enum Edge { UR, UF, UL, UB, DR, DF, DL, DB, FR, FL, BL, BR, } impl fmt::Display for Edge { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } impl TryFrom for Edge { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(UR), 1 => Ok(UF), 2 => Ok(UL), 3 => Ok(UB), 4 => Ok(DR), 5 => Ok(DF), 6 => Ok(DL), 7 => Ok(DB), 8 => Ok(FR), 9 => Ok(FL), 10 => Ok(BL), 11 => Ok(BR), _ => Err(Error::InvalidEdge), } } } /// Cube on the cubie level. #[derive(Debug, PartialEq, Clone, Copy)] pub struct CubieCube { /// Corner permutation, relative to SOLVED_STATE. pub cp: [Corner; 8], /// Corner orientation, 3 possible values: 0 (correctly oriented), 1 (twisted clockwise), 2 (twisted counter-clockwise). pub co: [u8; 8], /// Edge permutation, relative to SOLVED_STATE. pub ep: [Edge; 12], /// Edge orientation, 2 possible values: 0 (correctly oriented), 1 (flipped). pub eo: [u8; 12], } /// Solved cube on the Cubie level. pub const SOLVED_CUBIE_CUBE: CubieCube = CubieCube { cp: [URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB], co: [0, 0, 0, 0, 0, 0, 0, 0], ep: [UR, UF, UL, UB, DR, DF, DL, DB, FR, FL, BL, BR], eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }; impl Default for CubieCube { fn default() -> Self { SOLVED_CUBIE_CUBE } } impl Mul for CubieCube { type Output = Self; fn mul(self, rhs: CubieCube) -> Self::Output { let mut res = CubieCube::default(); // (A * B).c = A(B(x).c).c // (A * B).o = A(B(x).c).o + B(x).o for i in 0..8 { res.cp[i] = self.cp[rhs.cp[i] as usize]; res.co[i] = (self.co[rhs.cp[i] as usize] + rhs.co[i]) % 3; } for i in 0..12 { res.ep[i] = self.ep[rhs.ep[i] as usize]; res.eo[i] = (self.eo[rhs.ep[i] as usize] + rhs.eo[i]) % 2; } res } } impl fmt::Display for CubieCube { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Print string for a cubie cube. let mut s = String::new(); for i in 0..8 { let cs: String = format!("({},{})", self.cp[i], self.co[i]); s.push_str(&cs); } for i in 0..12 { let es: String = format!("({},{})", self.ep[i], self.eo[i]); s.push_str(&es); } write!(f, "{s}") } } impl From<&Vec> for CubieCube { fn from(moves: &Vec) -> Self { CubieCube::default().apply_moves(moves) } } /// Gives cubie representation of a face cube (facelet). impl TryFrom<&FaceCube> for CubieCube { type Error = Error; fn try_from(face_cube: &FaceCube) -> Result { let mut state = CubieCube::default(); let mut ori: usize = 0; let mut col1; let mut col2; for i in 0..8 { let i = Corner::try_from(i)?; // get the colors of the cubie at corner i, starting with U/D for index in 0..3 { ori = index; if face_cube.f[CORNER_FACELET[i as usize][ori] as usize] == Color::U || face_cube.f[CORNER_FACELET[i as usize][ori] as usize] == Color::D { break; } } col1 = face_cube.f[CORNER_FACELET[i as usize][(ori + 1) % 3] as usize]; col2 = face_cube.f[CORNER_FACELET[i as usize][(ori + 2) % 3] as usize]; for j in 0..8 { let j = Corner::try_from(j)?; if col1 == CORNER_COLOR[j as usize][1] && col2 == CORNER_COLOR[j as usize][2] { // in cornerposition i we have cornercubie j state.cp[i as usize] = j; state.co[i as usize] = ori as u8 % 3; break; } } } for i in 0..12 { let i = Edge::try_from(i)?; for j in 0..12 { let j = Edge::try_from(j)?; if face_cube.f[EDGE_FACELET[i as usize][0] as usize] == EDGE_COLOR[j as usize][0] && face_cube.f[EDGE_FACELET[i as usize][1] as usize] == EDGE_COLOR[j as usize][1] { state.ep[i as usize] = j; state.eo[i as usize] = 0; break; } if face_cube.f[EDGE_FACELET[i as usize][0] as usize] == EDGE_COLOR[j as usize][1] && face_cube.f[EDGE_FACELET[i as usize][1] as usize] == EDGE_COLOR[j as usize][0] { state.ep[i as usize] = j; state.eo[i as usize] = 1; break; } } } if !state.is_solvable() { Err(Error::InvalidFaceletValue) } else { Ok(state) } } } impl CubieCube { /// Applies a move to the current state. pub fn apply_move(self, move_name: Move) -> Self { let move_state = match move_name { U => U_MOVE, U2 => U_MOVE * U_MOVE, U3 => U_MOVE * U_MOVE * U_MOVE, D => D_MOVE, D2 => D_MOVE * D_MOVE, D3 => D_MOVE * D_MOVE * D_MOVE, R => R_MOVE, R2 => R_MOVE * R_MOVE, R3 => R_MOVE * R_MOVE * R_MOVE, L => L_MOVE, L2 => L_MOVE * L_MOVE, L3 => L_MOVE * L_MOVE * L_MOVE, F => F_MOVE, F2 => F_MOVE * F_MOVE, F3 => F_MOVE * F_MOVE * F_MOVE, B => B_MOVE, B2 => B_MOVE * B_MOVE, B3 => B_MOVE * B_MOVE * B_MOVE, }; self * move_state } /// Applies the sequence of moves to the current state. pub fn apply_moves(&self, moves: &[Move]) -> Self { moves.iter().fold(*self, |acc, &m| acc.apply_move(m)) } /// Returns the number of corner twist needed to orient the corners. pub fn count_corner_twist(&self) -> u8 { self.co.iter().fold(0, |acc, co| acc + ((3 - co) % 3)) } /// Returns the number of edge twist needed to orient the edges. pub fn count_edge_twist(&self) -> u8 { self.eo.iter().sum() } /// Returns the number of corner permutations needed to solve the corners. pub fn count_corner_perm(&self) -> u8 { let mut count = 0; let mut cp = self.cp; for i in 0..8 { if cp[i] as usize != i { if let Some(j) = (i + 1..8).find(|&j| cp[j] as usize == i) { cp.swap(i, j); count += 1; } } } count } /// Returns the number of edge permutations needed to solve the edges. pub fn count_edge_perm(&self) -> u8 { let mut count = 0; let mut ep = self.ep; for i in 0..12 { if ep[i] as usize != i { if let Some(j) = (i + 1..12).find(|&j| ep[j] as usize == i) { ep.swap(i, j); count += 1; } } } count } /// Checks if CubieCube is a valid cubie representation. pub fn is_solvable(&self) -> bool { let c_perm = self.count_corner_perm(); let e_perm = self.count_edge_perm(); let c_twist = self.count_corner_twist(); let e_twist = self.count_edge_twist(); let has_even_permutation = c_perm % 2 == e_perm % 2; let has_valid_twist = c_twist % 3 == 0 && e_twist % 2 == 0; has_even_permutation && has_valid_twist } /// Multiply this cubie cube with another cubie cube b, restricted to the corners. pub fn corner_multiply(&mut self, b: CubieCube) { let mut c_perm = [URF; 8]; let mut c_ori = [0; 8]; let mut ori = 0; for ci in ALL_CORNERS { let c = ci as usize; c_perm[c] = self.cp[b.cp[c] as usize]; let ori_a = self.co[b.cp[c] as usize]; let ori_b = b.co[c]; if ori_a < 3 && ori_b < 3 { // two regular cubes ori = ori_a + ori_b; if ori >= 3 { ori -= 3; } } else if ori_a < 3 && 3 <= ori_b { // cube b is in a mirrored state ori = ori_a + ori_b; if ori >= 6 { ori -= 3; // the composition also is in a mirrored state } } else if ori_a >= 3 && 3 > ori_b { // cube a is in a mirrored state ori = ori_a - ori_b; if ori < 3 { ori += 3; // the composition is a mirrored cube } } else if ori_a >= 3 && ori_b >= 3 { // if both cubes are in mirrored states if ori_a >= ori_b { ori = ori_a - ori_b; } else { ori = ori_b - ori_a; ori = 3 - ori; // the composition is a regular cube } } c_ori[c] = ori; } for c in ALL_CORNERS { let ci = c as usize; self.cp[ci] = c_perm[ci]; self.co[ci] = c_ori[ci]; } } /// Multiply this cubie cube with another cubie cube b, restricted to the edges. pub fn edge_multiply(&mut self, b: CubieCube) { let mut e_perm: [Edge; 12] = [UR; 12]; let mut e_ori = [0; 12]; for ei in ALL_EDGES { let e = ei as usize; e_perm[e] = self.ep[b.ep[e] as usize]; e_ori[e] = (b.eo[e] + self.eo[b.ep[e] as usize]) % 2; } for ei in ALL_EDGES { let e = ei as usize; self.ep[e] = e_perm[e]; self.eo[e] = e_ori[e]; } } /// Multiply this cubie cube with another cubie cube b. pub fn multiply(&mut self, b: CubieCube) { self.corner_multiply(b); self.edge_multiply(b); } /// Multiplay this cubie cube with a move vector. pub fn multiply_moves(&mut self, moves: &Vec) { moves .iter() .for_each(|&m| self.multiply(BSCT.bsc[m as usize])); } /// Return the inverse of this cubiecube. pub fn inverse_cubie_cube(&self) -> Self { let mut d = CubieCube::default(); for ei in ALL_EDGES { let e: usize = ei as usize; d.ep[self.ep[e] as usize] = ei; } for ei in ALL_EDGES { let e: usize = ei as usize; d.eo[e] = self.eo[d.ep[e] as usize]; } for ci in ALL_CORNERS { let c = ci as usize; d.cp[self.cp[c] as usize] = ci; } for ci in ALL_CORNERS { let c = ci as usize; let ori = self.co[d.cp[c] as usize]; if ori >= 3 { d.co[c] = ori; } else { d.co[c] = 3 - ori; if d.co[c] == 3 { d.co[c] = 0; } } } d } /// Give the parity of the corner permutation. pub fn corner_parity(&self) -> bool { let mut s = 0; for i in ((URF as usize + 1)..=(DRB as usize)).rev() { for j in ((URF as usize)..=(i - 1)).rev() { if self.cp[j] > self.cp[i] { s += 1 } } } (s % 2) == 0 } /// Give the parity of the edge permutation. A solvable cube has the same corner and edge parity. pub fn edge_parity(&self) -> bool { let mut s = 0; for i in ((UR as usize + 1)..=(BR as usize)).rev() { for j in ((UR as usize)..=(i - 1)).rev() { if self.ep[j] > self.ep[i] { s += 1; } } } (s % 2) == 0 } /// Generate a list of the symmetries and antisymmetries of the cubie cube. pub fn symmetries(&self) -> Vec { let sc = symmetries::sc(); let inv_idx = symmetries::inv_idx(); let mut s = Vec::new(); for j in 0..N_SYM { let mut c = CubieCube { cp: sc[j].cp, co: sc[j].co, ep: sc[j].ep, eo: sc[j].eo, }; c.multiply(*self); c.multiply(sc[inv_idx[j] as usize]); if *self == c { s.push(j); } let d = c.inverse_cubie_cube(); if *self == d { // then we have antisymmetry s.push(j + N_SYM); } } s } /// Get the twist of the 8 corners. 0 <= twist < 2187 in phase 1, twist = 0 in phase 2. pub fn get_twist(&self) -> u16 { let mut twist: u16 = 0; for i in (URF as usize)..(DRB as usize) { twist = 3 * twist + self.co[i] as u16; } twist } /// Set the twist of the 8 corners. 0 <= twist < 2187 in phase 1, twist = 0 in phase 2. pub fn set_twist(&mut self, twist: u16) { let mut twistparity = 0; let mut twist = twist; for i in ((URF as usize)..(DRB as usize)).rev() { self.co[i] = (twist % 3) as u8; twistparity += self.co[i]; twist /= 3; } self.co[DRB as usize] = (3 - twistparity % 3) % 3; } /// Get the flip of the 12 edges. 0 <= flip < 2048 in phase 1, flip = 0 in phase 2. pub fn get_flip(&self) -> u16 { let mut ret: u16 = 0; for i in (UR as usize)..(BR as usize) { ret = 2 * ret + self.eo[i] as u16; } ret } /// Set the flip of the 12 edges. 0 <= flip < 2048 in phase 1, flip = 0 in phase 2. pub fn set_flip(&mut self, flip: u16) { let mut flipparity = 0; let mut flip = flip; for i in ((UR as usize)..(BR as usize)).rev() { self.eo[i] = (flip % 2) as u8; flipparity += self.eo[i]; flip /= 2; } self.eo[BR as usize] = (2 - flipparity % 2) % 2; } /// Get the location of the UD-slice edges FR,FL,BL and BR ignoring their permutation. /// /// 0<= slice < 495 in phase 1, slice = 0 in phase 2. pub fn get_slice(&self) -> u16 { let mut a = 0; let mut x = 0; // Compute the index a < (12 choose 4) for j in ((UR as usize)..=(BR as usize)).rev() { if FR <= self.ep[j] && self.ep[j] <= BR { a += c_nk((11 - j) as u32, x + 1); x += 1; } } a as u16 } /// Set the location of the UD-slice edges FR,FL,BL and BR ignoring their permutation. /// /// 0<= slice < 495 in phase 1, slice = 0 in phase 2. pub fn set_slice(&mut self, idx: u16) { let slice_edge = [FR, FL, BL, BR]; let other_edge = [UR, UF, UL, UB, DR, DF, DL, DB]; let mut a = idx; // Location let mut ep = [-1; 12]; let mut x: i32 = 4; // set slice edges for j in ALL_EDGES { if a >= c_nk((11 - j as u32) as u32, x as u32) as u16 { self.ep[j as usize] = slice_edge[(4 - x) as usize]; ep[j as usize] = slice_edge[(4 - x) as usize] as i32; a -= c_nk(11 - j as u32, x as u32) as u16; x -= 1; } } let mut x = 0; // set the remaining edges UR..DB for j in ALL_EDGES { if ep[j as usize] == -1 { self.ep[j as usize] = other_edge[x]; x += 1; } } } /// Get the permutation and location of the UD-slice edges FR,FL,BL and BR. /// /// 0 <= slice_sorted < 11880 in phase 1, 0 <= slice_sorted < 24 in phase 2, slice_sorted = 0 for solved cube. pub fn get_slice_sorted(&self) -> u16 { let mut a = 0; let mut x = 0; let mut edge4 = [UR; 4]; // First compute the index a < (12 choose 4) and the permutation array perm. for j in ((UR as usize)..=(BR as usize)).rev() { if FR <= self.ep[j] && self.ep[j] <= BR { a += c_nk((11 - j) as u32, x + 1); edge4[(3 - x) as usize] = self.ep[j as usize]; x += 1; } } // Then compute the index b < 4! for the permutation in edge4 let mut b = 0; for j in (1..=3).rev() { let mut k = 0; while edge4[j] != ALL_EDGES[j + 8] { rotate_left(&mut edge4, 0, j); k += 1 } b = (j + 1) * b + k; } 24 * a as u16 + b as u16 } /// Set the permutation and location of the UD-slice edges FR,FL,BL and BR. /// /// 0 <= slice_sorted < 11880 in phase 1, 0 <= slice_sorted < 24 in phase 2, slice_sorted = 0 for solved cube. pub fn set_slice_sorted(&mut self, idx: u16) { let mut slice_edge = [FR, FL, BL, BR]; let other_edge = [UR, UF, UL, UB, DR, DF, DL, DB]; let mut b = idx % 24; // Permutation let mut a = idx / 24; // Location let mut ep = [-1; 12]; // Invalidate all edge positions let mut j = 1; // generate permutation from index b while j < 4 { let mut k = b % (j + 1); b /= j + 1; while k > 0 { rotate_right(&mut slice_edge, 0, j as usize); k -= 1; } j += 1; } let mut x = 4; // set slice edges for j in ALL_EDGES { if a >= c_nk(11 - j as u32, x) as u16 { self.ep[j as usize] = slice_edge[4 - x as usize]; ep[j as usize] = slice_edge[4 - x as usize] as i32; a -= c_nk(11 - j as u32, x) as u16; x -= 1; } } let mut x = 0; // set the remaining edges UR..DB for j in ALL_EDGES { if ep[j as usize] == -1 { self.ep[j as usize] = other_edge[x]; x += 1; } } } /// Get the permutation and location of edges UR, UF, UL and UB. /// /// 0 <= u_edges < 11880 in phase 1, 0 <= u_edges < 1680 in phase 2, u_edges = 1656 for solved cube. pub fn get_u_edges(&self) -> u16 { let mut a = 0; let mut x = 0; let mut edge4 = [UR; 4]; let mut ep_mod = self.ep.clone(); for _j in 0..4 { rotate_right(&mut ep_mod, 0, 11); } // First compute the index a < (12 choose 4) and the permutation array perm. for j in ((UR as usize)..=(BR as usize)).rev() { if UR <= ep_mod[j] && ep_mod[j] <= UB { a += c_nk(11 - j as u32, x + 1); edge4[3 - x as usize] = ep_mod[j]; x += 1; } } // Then compute the index b < 4! for the permutation in edge4 let mut b = 0; for j in (1..=3).rev() { let mut k = 0; while edge4[j] != ALL_EDGES[j] { rotate_left(&mut edge4, 0, j); k += 1; } b = (j + 1) * b + k; } 24 * a as u16 + b as u16 } /// Set the permutation and location of edges UR, UF, UL and UB. /// /// 0 <= u_edges < 11880 in phase 1, 0 <= u_edges < 1680 in phase 2, u_edges = 1656 for solved cube. pub fn set_u_edges(&mut self, idx: u16) { let mut slice_edge = [UR, UF, UL, UB]; let other_edge = [DR, DF, DL, DB, FR, FL, BL, BR]; let mut b = idx % 24; // Permutation let mut a = idx / 24; // Location let mut ep = [-1; 12]; let mut j = 1; // generate permutation from index b while j < 4 { let mut k = b % (j + 1); b /= j + 1; while k > 0 { rotate_right(&mut slice_edge, 0, j as usize); k -= 1; } j += 1; } let mut x = 4; // set slice edges for j in ALL_EDGES { if a >= c_nk(11 - j as u32, x) as u16 { self.ep[j as usize] = slice_edge[4 - x as usize]; ep[j as usize] = slice_edge[4 - x as usize] as i32; a -= c_nk(11 - j as u32, x) as u16; x -= 1; } } let mut x = 0; // set the remaining edges UR..DB for j in ALL_EDGES { if ep[j as usize] == -1 { self.ep[j as usize] = other_edge[x]; x += 1; } } for _j in 0..4 { rotate_left(&mut self.ep, 0, 11); } } /// Get the permutation and location of the edges DR, DF, DL and DB. /// /// 0 <= d_edges < 11880 in phase 1, 0 <= d_edges < 1680 in phase 2, d_edges = 0 for solved cube. pub fn get_d_edges(&self) -> u16 { let mut a = 0; let mut x = 0; let mut edge4 = [UR; 4]; let mut ep_mod = self.ep.clone(); for _j in 0..4 { rotate_right(&mut ep_mod, 0, 11); } // First compute the index a < (12 choose 4) and the permutation array perm. for j in ((UR as usize)..=(BR as usize)).rev() { if DR <= ep_mod[j] && ep_mod[j] <= DB { a += c_nk(11 - j as u32, x + 1); edge4[3 - x as usize] = ep_mod[j]; x += 1; } } // Then compute the index b < 4! for the permutation in edge4 let mut b = 0; for j in (1..=3).rev() { let mut k = 0; while edge4[j] != ALL_EDGES[j + 4] { rotate_left(&mut edge4, 0, j); k += 1; } b = (j + 1) * b + k; } 24 * a as u16 + b as u16 } /// Set the permutation and location of the edges DR, DF, DL and DB. /// /// 0 <= d_edges < 11880 in phase 1, 0 <= d_edges < 1680 in phase 2, d_edges = 0 for solved cube. pub fn set_d_edges(&mut self, idx: u16) { let mut slice_edge = [DR, DF, DL, DB]; let other_edge = [FR, FL, BL, BR, UR, UF, UL, UB]; let mut b = idx % 24; // Permutation let mut a = idx / 24; // Location let mut ep = [-1; 12]; // Invalidate all edge positions let mut j = 1; // generate permutation from index b while j < 4 { let mut k = b % (j + 1); b /= j + 1; while k > 0 { rotate_right(&mut slice_edge, 0, j as usize); k -= 1; } j += 1; } let mut x = 4; // set slice edges for j in ALL_EDGES { if a >= c_nk(11 - j as u32, x as u32) as u16 { self.ep[j as usize] = slice_edge[4 - x]; ep[j as usize] = slice_edge[4 - x] as i32; a -= c_nk(11 - j as u32, x as u32) as u16; x -= 1; } } let mut x = 0; // set the remaining edges UR..DB for j in ALL_EDGES { if ep[j as usize] == -1 { self.ep[j as usize] = other_edge[x]; x += 1; } } for _j in 0..4 { rotate_left(&mut self.ep, 0, 11); } } /// Get the permutation of the 8 corners. /// /// 0 <= corners < 40320 defined but unused in phase 1, 0 <= corners < 40320 in phase 2, /// /// corners = 0 for solved cube pub fn get_corners(&self) -> u16 { let mut perm = self.cp.clone(); // duplicate cp let mut b = 0; for j in ((URF as usize + 1)..=(DRB as usize)).rev() { let mut k = 0; while perm[j] != ALL_CORNERS[j] { rotate_left(&mut perm, 0, j); k += 1; } b = (j + 1) * b + k; } b as u16 } /// Set the permutation of the 8 corners. /// /// 0 <= corners < 40320 defined but unused in phase 1, 0 <= corners < 40320 in phase 2, /// /// corners = 0 for solved cube pub fn set_corners(&mut self, idx: u16) { self.cp = ALL_CORNERS.clone(); let mut x = idx; for j in ALL_CORNERS { let mut k = x % (j as u16 + 1); x /= j as u16 + 1; while k > 0 { rotate_right(&mut self.cp, 0, j as usize); k -= 1; } } } /// Get the permutation of the 8 U and D edges. /// /// ud_edges undefined in phase 1, 0 <= ud_edges < 40320 in phase 2, ud_edges = 0 for solved cube. pub fn get_ud_edges(&self) -> u16 { let mut perm = [UR; 8]; for i in 0..8 { perm[i] = self.ep[i]; // duplicate first 8 elements of ep } let mut b = 0; for j in ((UR as usize + 1)..=(DB as usize)).rev() { let mut k = 0; while perm[j] != ALL_EDGES[j] { rotate_left(&mut perm, 0, j); k += 1; } b = (j + 1) * b + k; } b as u16 } /// Set the permutation of the 8 U and D edges. /// /// ud_edges undefined in phase 1, 0 <= ud_edges < 40320 in phase 2, ud_edges = 0 for solved cube. pub fn set_ud_edges(&mut self, idx: usize) { let mut x: usize = idx; // positions of FR FL BL BR edges are not affected for i in 0..8 { self.ep[i] = ALL_EDGES[i]; } for j in 0..8 { let mut k = x % (j + 1); x /= j + 1; while k > 0 { rotate_right(&mut self.ep, 0, j); k -= 1; } } } /// Generate a random cube. The probability is the same for all possible states. pub fn randomize(&mut self) { // The permutation of the 12 edges. 0 <= idx < 12!.""" let mut idx = random::() % 479001600; // 12! self.cp = ALL_CORNERS.clone(); for j in ALL_EDGES { let mut k = idx % (j as usize + 1); idx /= j as usize + 1; while k > 0 { rotate_right(&mut self.ep, 0, j as usize); k -= 1; } } let p = self.edge_parity(); loop { self.set_corners(random::() % 40320); // 8! if p == self.corner_parity() { // parities of edge and corner permutations must be the same break; } } self.set_flip(random::() % 2048); // 2^11 self.set_twist(random::() % 2187); // 3^7 } /// Check if cubiecube is valid. pub fn verify(&self) -> Result { let mut edge_count = [0; 12]; for i in ALL_EDGES { edge_count[self.ep[i as usize] as usize] += 1; } for i in ALL_EDGES { if edge_count[i as usize] != 1 { return Err(Error::InvalidEdge); } } let mut s = 0; for i in ALL_EDGES { s += self.eo[i as usize]; } if s % 2 != 0 { return Err(Error::InvalidEdge); } let mut corner_count = [0; 8]; for i in ALL_CORNERS { corner_count[self.cp[i as usize] as usize] += 1; } for i in ALL_CORNERS { if corner_count[i as usize] != 1 { return Err(Error::InvalidCorner); } } let mut s = 0; for i in ALL_CORNERS { s += self.co[i as usize]; } if s % 3 != 0 { return Err(Error::InvalidCorner); } if self.edge_parity() != self.corner_parity() { return Err(Error::InvalidCubieValue); } Ok(true) } } /// these cubes represent the basic cube moves. pub fn basic_move_cubes() -> [CubieCube; 6] { let mut basic_move_cube = [CubieCube::default(); 6]; basic_move_cube[Color::U as usize] = U_MOVE; basic_move_cube[Color::R as usize] = R_MOVE; basic_move_cube[Color::F as usize] = F_MOVE; basic_move_cube[Color::D as usize] = D_MOVE; basic_move_cube[Color::L as usize] = L_MOVE; basic_move_cube[Color::B as usize] = B_MOVE; basic_move_cube } /// these cubes represent the all 18 cube moves pub fn move_cubes() -> [CubieCube; 18] { let basic_move_cube = basic_move_cubes(); let mut move_cube = [CubieCube::default(); 18]; for c1 in ALL_COLORS { let mut cc = CubieCube::default(); for k1 in 0..3 { cc.multiply(basic_move_cube[c1 as usize]); move_cube[3 * c1 as usize + k1] = CubieCube { cp: cc.cp, co: cc.co, ep: cc.ep, eo: cc.eo, }; } } move_cube } pub struct BasicMoveCubeTables { bsc: [CubieCube; 18], } impl BasicMoveCubeTables { pub fn new() -> Self { Self { bsc: move_cubes() } } } lazy_static! { static ref BSCT: BasicMoveCubeTables = BasicMoveCubeTables::new(); } /// Rotate array arr right between left and right. right is includ pub fn rotate_right(arr: &mut [T], left: usize, right: usize) { let temp = arr[right]; for i in (left + 1..=right).rev() { arr[i] = arr[i - 1]; } arr[left] = temp; } /// Rotate array arr left between left and right. right is includ pub fn rotate_left(arr: &mut [T], left: usize, right: usize) { let temp = arr[left]; for i in left..right { arr[i] = arr[i + 1]; } arr[right] = temp; } /// Binomial coefficient [n choose k]. pub fn c_nk(n: u32, k: u32) -> u32 { let mut k = k; if n < k { return 0; } if k > (n / 2) { k = n - k; } let mut s = 1; let mut i = n; let mut j = 1; while i != n - k { s *= i; s /= j; i -= 1; j += 1; } s } #[cfg(test)] mod test { use crate::cubie::BSCT; use crate::cubie::*; #[test] fn test_eq() { let state = CubieCube::default(); let state2 = CubieCube::default(); assert_eq!(state, state2); } #[test] fn test_apply() { let state = CubieCube::default().apply_move(R); assert_eq!(state, R_MOVE); let r2_state = CubieCube::default().apply_move(R).apply_move(R); assert_eq!(r2_state, R_MOVE * R_MOVE); let r3_state = r2_state.apply_move(R); assert_eq!(r3_state, r2_state * R_MOVE); let fr_state = CubieCube { //URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB, cp: [URF, DLF, ULB, UFL, DRB, DFR, DBL, UBR], co: [1, 2, 0, 2, 1, 1, 0, 2], ep: [UF, FL, UL, UB, BR, FR, DL, DB, DR, DF, BL, UR], eo: [1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], }; assert_eq!(F_MOVE * R_MOVE, fr_state); } #[test] fn test_multiply() { let mut state = CubieCube::default().apply_move(F); let s2 = CubieCube::default().apply_move(R); state.multiply(s2); let fr_state = CubieCube { //URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB, cp: [URF, DLF, ULB, UFL, DRB, DFR, DBL, UBR], co: [1, 2, 0, 2, 1, 1, 0, 2], ep: [UF, FL, UL, UB, BR, FR, DL, DB, DR, DF, BL, UR], eo: [1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], }; assert_eq!(state, fr_state); let mut state = CubieCube::default(); state.multiply(BSCT.bsc[F as usize]); state.multiply(BSCT.bsc[R as usize]); assert_eq!(state, fr_state); } #[test] fn test_inverse() { let state = CubieCube { cp: [DLF, ULB, DBL, DRB, UBR, UFL, DFR, URF], co: [2, 1, 2, 1, 2, 2, 0, 2], ep: [BR, BL, UB, UR, DR, FR, FL, UF, DF, DL, DB, UL], eo: [1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1], }; let twist = state.get_twist(); assert_eq!(twist, 1914); let ic = state.inverse_cubie_cube(); let d = CubieCube { cp: [DRB, DLF, UFL, DFR, DBL, URF, ULB, UBR], co: [1, 1, 2, 1, 0, 1, 1, 2], ep: [UB, DB, BR, UL, DR, FR, FL, BL, DF, DL, UF, UR], eo: [0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1], }; assert_eq!(ic, d); let d2 = ic.inverse_cubie_cube(); assert_eq!(state, d2); } #[test] fn test_parity() { let state = CubieCube::default(); assert_eq!(state.corner_parity(), true); assert_eq!(state.edge_parity(), true); let state = CubieCube::from(&vec![R, U, R3, U3, R3, F, R, F3]); assert_eq!(state.corner_parity(), true); assert_eq!(state.edge_parity(), true); } #[test] fn test_symmetries() { let state = CubieCube::default(); let syms = state.symmetries(); let d = CubieCube { cp: [URF, UBR, DRB, DFR, UFL, ULB, DBL, DLF], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, UR, BR, DR, FL, UL, BL, DL, UF, UB, DB, DF], eo: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], }; let syms32 = d.symmetries(); assert_eq!(syms.len(), 96); assert_eq!(syms32.len(), 12); // println!("{:?}", syms32); // assert_eq!(syms.sort(), [0..96].into()); // assert_eq!(state.edge_parity(), true); // let state = CubieCube::from(&vec![R, U, R3, U3, R3, F, R, F3]); // assert_eq!(state.corner_parity(), true); // assert_eq!(state.edge_parity(), true); } #[test] fn test_twist() { let mut state = CubieCube::default(); let d = CubieCube { cp: [UFL, URF, UBR, ULB, DLF, DFR, DRB, DBL], co: [3, 3, 3, 3, 3, 3, 3, 3], ep: [UL, UF, UR, UB, DL, DF, DR, DB, FL, FR, BR, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }; assert_eq!(state.get_twist(), 0); assert_eq!(d.get_twist(), 3279); state.set_twist(3279); assert_eq!(state.cp[0], URF); assert_eq!(state.get_twist(), 1092); } #[test] fn test_flip() { let mut state = CubieCube::default(); let d = CubieCube { cp: [UBR, URF, UFL, ULB, DRB, DFR, DLF, DBL], co: [0; 8], ep: [UB, UR, UF, UL, DB, DR, DF, DL, BR, FR, FL, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], }; assert_eq!(state.get_flip(), 0); assert_eq!(d.get_flip(), 7); state.set_flip(7); assert_eq!(state.eo[8], 1); assert_eq!(state.get_flip(), 7); } #[test] fn test_slice() { let mut state = CubieCube::default(); let d = CubieCube { cp: [URF, UBR, DRB, DFR, UFL, ULB, DBL, DLF], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, UR, BR, DR, FL, UL, BL, DL, UF, UB, DB, DF], eo: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], }; assert_eq!(state.get_slice(), 0); assert_eq!(d.get_slice(), 440); state.set_slice(440); assert_eq!(state.ep[11], DB); assert_eq!(state.ep[0], FR); assert_eq!(state.get_slice(), 440); } #[test] fn test_slice_sorted() { let mut state = CubieCube::default(); let d = CubieCube { cp: [URF, UBR, DRB, DFR, UFL, ULB, DBL, DLF], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, UR, BR, DR, FL, UL, BL, DL, UF, UB, DB, DF], eo: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], }; assert_eq!(state.get_slice_sorted(), 0); assert_eq!(d.get_slice_sorted(), 10576); state.set_slice_sorted(11576); assert_eq!(state.ep[11], DB); assert_eq!(state.ep[0], BR); assert_eq!(state.get_slice_sorted(), 11576); } #[test] fn test_u_edges() { let mut state = CubieCube::default(); let d = CubieCube { cp: [URF, UBR, DRB, DFR, UFL, ULB, DBL, DLF], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, UR, BR, DR, FL, UL, BL, DL, UF, UB, DB, DF], eo: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], }; assert_eq!(state.get_u_edges(), 1656); assert_eq!(d.get_u_edges(), 11225); state.set_u_edges(11576); assert_eq!(state.ep[11], UR); assert_eq!(state.ep[0], DF); assert_eq!(state.get_u_edges(), 11576); } #[test] fn test_d_edges() { let mut state = CubieCube::default(); let d = CubieCube { cp: [URF, UBR, DRB, DFR, UFL, ULB, DBL, DLF], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, UR, BR, DR, FL, UL, BL, DL, UF, UB, DB, DF], eo: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], }; assert_eq!(state.get_d_edges(), 0); assert_eq!(d.get_d_edges(), 4519); state.set_d_edges(11576); assert_eq!(state.ep[11], DR); assert_eq!(state.ep[0], FL); assert_eq!(state.get_d_edges(), 11576); } #[test] fn test_corners() { let mut state = CubieCube::default(); let d = CubieCube { cp: [URF, UBR, DRB, DFR, UFL, ULB, DBL, DLF], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, UR, BR, DR, FL, UL, BL, DL, UF, UB, DB, DF], eo: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], }; assert_eq!(state.get_corners(), 0); assert_eq!(d.get_corners(), 18196); state.set_corners(11576); assert_eq!(state.cp[1], DRB); assert_eq!(state.cp[2], DLF); assert_eq!(state.get_corners(), 11576); } #[test] fn test_ud_edges() { let mut state = CubieCube::default(); let d = CubieCube { cp: [UFL, URF, UBR, ULB, DLF, DFR, DRB, DBL], co: [3, 3, 3, 3, 3, 3, 3, 3], ep: [UL, UF, UR, UB, DL, DF, DR, DB, FL, FR, BR, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }; assert_eq!(state.get_ud_edges(), 0); assert_eq!(d.get_ud_edges(), 3747); state.set_ud_edges(11576); assert_eq!(state.ep[11], BR); assert_eq!(state.ep[0], UR); assert_eq!(state.get_ud_edges(), 11576); } #[test] fn test_randomize() { let mut state = CubieCube::default(); state.randomize(); assert_eq!(state.verify().unwrap(), true); // println!("{}", state); } #[test] fn test_move_sequence() { // (R U R' U') * 6 let moves = vec![ R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, ]; let state = CubieCube::default().apply_moves(&moves); assert_eq!(state, SOLVED_CUBIE_CUBE); let mut state = CubieCube::default(); state.multiply_moves(&moves); assert_eq!(state, SOLVED_CUBIE_CUBE); } #[test] fn test_scramble() { // U F' D' F2 D B2 D' R2 U' F2 R2 D2 R2 U' L B L R F' D B' let scramble = vec![ U, F3, D3, F2, D, B2, D3, R2, U3, F2, R2, D2, R2, U3, L, B, L, R, F3, D, B3, ]; let state = CubieCube::default().apply_moves(&scramble); let expected = CubieCube { cp: [DFR, UBR, DLF, ULB, DRB, UFL, URF, DBL], co: [2, 0, 1, 2, 0, 0, 2, 2], ep: [DF, UB, FL, BL, BR, UL, DR, FR, DL, DB, UF, UR], eo: [1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1], }; assert_eq!(state, expected); let mut state = CubieCube::default(); state.multiply_moves(&scramble); assert_eq!(state, expected); } #[test] fn test_perm_count() { let state = CubieCube::default(); assert_eq!(state.count_corner_perm(), 0); assert_eq!(state.count_edge_perm(), 0); let state = CubieCube::from(&vec![R, U, R3, U3]); assert_eq!(state.count_corner_perm(), 2); assert_eq!(state.count_edge_perm(), 2); let state = CubieCube::from(&vec![ R, U3, R3, U3, R, U, R, D, R3, U3, R, D3, R3, U2, R3, U3, ]); assert_eq!(state.count_corner_perm(), 1); assert_eq!(state.count_edge_perm(), 1); } #[test] fn test_twist_count() { let state = CubieCube::default(); assert_eq!(state.count_corner_twist(), 0); assert_eq!(state.count_edge_twist(), 0); let state = CubieCube::from(&vec![R, U, R3, U3, R3, F, R, F3]); assert_eq!(state.count_corner_twist(), 3); assert_eq!(state.count_edge_twist(), 2); } }