Files
pyramix/src/state.rs
2023-11-26 21:29:23 +01:00

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()))
}
}