225 lines
5.2 KiB
Rust
225 lines
5.2 KiB
Rust
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<u32> for State {
|
|
fn from(value: u32) -> Self {
|
|
State::unpack(value)
|
|
}
|
|
}
|
|
|
|
impl From<State> 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()))
|
|
}
|
|
} |