use std::fmt::{Display, Formatter}; use std::ops::Add; #[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct State { pub edges: [(u8, bool); 6], pub corners: [u8; 4], } impl Default for State { fn default() -> Self { Self { edges: [ (0, false), (1, false), (2, false), (3, false), (4, false), (5, false) ], corners: [0; 4], } } } impl State { pub fn pack(self) -> u32 { // edges first; each edge is numberd 0..=5 and a bool let edges = self.edges.iter().copied().map(|(n,f)| (((f as u32) << 3) | n as u32)) .fold(0, |acc, new| acc << 4 | new); let corners = self.corners.iter().map(|c| *c as u32 % 3).fold(edges, |a,n| a << 2 | n); corners } pub fn unpack(mut v: u32) -> Self { let mut res = Self::default(); for i in 0..6 { let slot = v >> 28; let flip = slot & 8 != 0; let n = (slot & 7) as u8; res.edges[i] = (n, flip); v <<= 4; } for i in 0..4 { res.corners[i] = ((v >> 30) & 3) as u8; v <<= 2; } res } const fn mk_move(corner: usize, dir: bool) -> Self { let mut state = Self { edges: [ (0, false), (1, false), (2, false), (3, false), (4, false), (5, false) ], corners: [0; 4], } ; let mut edges = match corner { 0 => [4,5,3], 1 => [0,1,3], 2 => [2,4,1], 3 => [2,0,5], _ => unimplemented!() }; if !dir { // counter-clockwise let e0 = edges[1]; edges[1] = edges[2]; edges[2] = e0; } const fn mflip((n,f): (u8, bool), df: bool) -> (u8, bool) { (n, f ^ df) } state.edges[edges[1]] = (edges[0] as u8, true); state.edges[edges[2]] = (edges[1] as u8, false); state.edges[edges[0]] = (edges[2] as u8, true); state.corners[corner] = if dir { 1 } else { 2 }; state } } impl Display for State { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let mut res = String::with_capacity(11); for (n,flip) in self.edges { res.push(char::from(if flip { b'A'} else { b'a'} + n) ); } res.push('/'); for d in self.corners { res.push(match d { 0 => ',', 1 => '+', 2 => '-', _ => '?', }) } f.write_str(res.as_str()) } } impl From for State { fn from(value: u32) -> Self { State::unpack(value) } } impl From for u32 { fn from(value: State) -> Self { value.pack() } } impl Add for State { type Output = State; fn add(self, rhs: Self) -> Self::Output { let mut result = self; for i in 0..6 { let (sn, sf) = rhs.edges[i]; let (rn, rf) = self.edges[sn as usize]; result.edges[i] = (rn, rf ^ sf); } for i in 0..4 { result.corners[i] = (self.corners[i] + rhs.corners[i]) % 3; } result } } pub const M0P: State = State::mk_move(0, true); pub const M1P: State = State::mk_move(1, true); pub const M2P: State = State::mk_move(2, true); pub const M3P: State = State::mk_move(3, true); pub const M0N: State = State::mk_move(0, false); pub const M1N: State = State::mk_move(1, false); pub const M2N: State = State::mk_move(2, false); pub const M3N: State = State::mk_move(3, false); #[cfg(test)] mod test { use super::*; fn test_flipped(s: State, edge: usize) -> bool { s.edges[edge] == (edge as u8, true) } macro_rules! test_flipped { ($mov:expr, $edge:expr) => {{ let mov = $mov; assert_eq!(mov.edges[$edge], ($edge, true)); }} } #[test] fn test_203_2() { test_flipped!(M2P + M0P + M3P, 2) } #[test] fn test_312_2() { test_flipped!(M3P + M1P + M2P, 2) } #[test] fn test_102_1() { test_flipped!(M1P + M0P + M2P, 1) } #[test] fn test_231_1() { test_flipped!(M2P + M3P + M1P, 1) } #[test] fn test_301_0() { test_flipped!(M3P + M0P + M1P, 0) } #[test] fn test_123_0() { test_flipped!(M1P + M2P + M3P, 0) } #[test] fn test_210_4() { test_flipped!(M2P + M1P + M0P, 4) } #[test] fn test_032_4() { test_flipped!(M0P + M3P + M2P, 4) } #[test] fn test_130_3() { test_flipped!(M1P+M3P+M0P, 3) } #[test] fn test_021_3() { test_flipped!(M0P+M2P+M1P, 3) } #[test] fn test_320_5() { test_flipped!(M2P + M0P + M3P, 2) } #[test] fn test_013_5() { test_flipped!(M0P+M1P+M3P, 5) } #[test] fn test_pack() { let st = State::default(); assert_eq!(st, State::unpack(st.into())) } }