use std::fmt;
use crate::{cubie::CubieCube, error::Error};
#[rustfmt::skip]
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub enum Color {
U, R, F, D, L, B,
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl TryFrom<char> for Color {
type Error = Error;
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'U' => Ok(Color::U),
'R' => Ok(Color::R),
'F' => Ok(Color::F),
'D' => Ok(Color::D),
'L' => Ok(Color::L),
'B' => Ok(Color::B),
_ => Err(Error::InvalidColor),
}
}
}
#[derive(Debug, PartialEq)]
pub struct FaceCube {
pub f: [Color; 54],
}
#[rustfmt::skip]
pub const SOLVED_FACE_CUBE: FaceCube = FaceCube {
f: [
Color::U, Color::U, Color::U, Color::U, Color::U, Color::U, Color::U, Color::U, Color::U,
Color::R, Color::R, Color::R, Color::R, Color::R, Color::R, Color::R, Color::R, Color::R,
Color::F, Color::F, Color::F, Color::F, Color::F, Color::F, Color::F, Color::F, Color::F,
Color::D, Color::D, Color::D, Color::D, Color::D, Color::D, Color::D, Color::D, Color::D,
Color::L, Color::L, Color::L, Color::L, Color::L, Color::L, Color::L, Color::L, Color::L,
Color::B, Color::B, Color::B, Color::B, Color::B, Color::B, Color::B, Color::B, Color::B,
],
};
impl Default for FaceCube {
fn default() -> Self {
SOLVED_FACE_CUBE
}
}
impl TryFrom<&CubieCube> for FaceCube {
type Error = Error;
fn try_from(value: &CubieCube) -> Result<Self, Self::Error> {
if !value.is_solvable() {
return Err(Error::InvalidCubieValue);
}
let mut face = FaceCube::default();
for (i, corner_faces) in CORNER_FACELET.iter().enumerate() {
let corner = value.cp[i] as usize;
for (j, f) in corner_faces.iter().enumerate() {
face.f[*f as usize] = CORNER_COLOR[corner][(j + (3 - value.co[i] as usize)) % 3];
}
}
for (i, edge_faces) in EDGE_FACELET.iter().enumerate() {
let edge = value.ep[i] as usize;
for (j, f) in edge_faces.iter().enumerate() {
face.f[*f as usize] = EDGE_COLOR[edge][(j + value.eo[i] as usize) % 2];
}
}
Ok(face)
}
}
impl TryFrom<&str> for FaceCube {
type Error = Error;
fn try_from(cube_string: &str) -> Result<Self, Self::Error> {
if cube_string.len() != 54 {
return Err(Error::InvalidFaceletString);
}
let mut face_cube = FaceCube::default();
for (i, c) in cube_string.chars().enumerate() {
face_cube.f[i] = Color::try_from(c)?;
}
Ok(face_cube)
}
}
impl fmt::Display for FaceCube {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let acc = String::new();
let s = self.f.iter().fold(acc, |acc, f| format!("{acc}{f}"));
write!(f, "{s}")
}
}
#[rustfmt::skip]
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub enum Facelet {
U1, U2, U3, U4, _U5, U6, U7, U8, U9,
R1, R2, R3, R4, _R5, R6, R7, R8, R9,
F1, F2, F3, F4, _F5, F6, F7, F8, F9,
D1, D2, D3, D4, _D5, D6, D7, D8, D9,
L1, L2, L3, L4, _L5, L6, L7, L8, L9,
B1, B2, B3, B4, _B5, B6, B7, B8, B9,
}
pub const CORNER_FACELET: [[Facelet; 3]; 8] = [
[Facelet::U9, Facelet::R1, Facelet::F3], [Facelet::U7, Facelet::F1, Facelet::L3], [Facelet::U1, Facelet::L1, Facelet::B3], [Facelet::U3, Facelet::B1, Facelet::R3], [Facelet::D3, Facelet::F9, Facelet::R7], [Facelet::D1, Facelet::L9, Facelet::F7], [Facelet::D7, Facelet::B9, Facelet::L7], [Facelet::D9, Facelet::R9, Facelet::B7], ];
pub const EDGE_FACELET: [[Facelet; 2]; 12] = [
[Facelet::U6, Facelet::R2],
[Facelet::U8, Facelet::F2],
[Facelet::U4, Facelet::L2],
[Facelet::U2, Facelet::B2],
[Facelet::D6, Facelet::R8],
[Facelet::D2, Facelet::F8],
[Facelet::D4, Facelet::L8],
[Facelet::D8, Facelet::B8],
[Facelet::F6, Facelet::R4],
[Facelet::F4, Facelet::L6],
[Facelet::B6, Facelet::L4],
[Facelet::B4, Facelet::R6],
];
pub const CORNER_COLOR: [[Color; 3]; 8] = [
[Color::U, Color::R, Color::F],
[Color::U, Color::F, Color::L],
[Color::U, Color::L, Color::B],
[Color::U, Color::B, Color::R],
[Color::D, Color::F, Color::R],
[Color::D, Color::L, Color::F],
[Color::D, Color::B, Color::L],
[Color::D, Color::R, Color::B],
];
pub const EDGE_COLOR: [[Color; 2]; 12] = [
[Color::U, Color::R],
[Color::U, Color::F],
[Color::U, Color::L],
[Color::U, Color::B],
[Color::D, Color::R],
[Color::D, Color::F],
[Color::D, Color::L],
[Color::D, Color::B],
[Color::F, Color::R],
[Color::F, Color::L],
[Color::B, Color::L],
[Color::B, Color::R],
];
#[cfg(test)]
mod test {
use crate::cubie::{Corner::*, Edge::*, SOLVED_CUBIE_CUBE};
use crate::facelet::*;
#[test]
fn test_facelet_to_cubie() {
let faces = "DRBLUURLDRBLRRBFLFFUBFFDRUDURRBDFBBULDUDLUDLBUFFDBFLRL";
let face_cube = FaceCube::try_from(faces).unwrap();
let actual_state = CubieCube::try_from(&face_cube).unwrap();
assert_eq!(
actual_state,
CubieCube {
cp: [DRB, URF, DLF, ULB, DFR, UBR, DBL, UFL],
co: [0, 2, 0, 1, 1, 0, 2, 0],
ep: [UB, UL, DL, FR, FL, UR, BL, BR, DR, UF, DF, DB],
eo: [0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0]
}
);
}
#[test]
fn test_cubie_to_facelet() {
let face_cube = FaceCube::try_from(&SOLVED_CUBIE_CUBE).unwrap();
assert_eq!(face_cube, SOLVED_FACE_CUBE);
let face_string = "DRBLUURLDRBLRRBFLFFUBFFDRUDURRBDFBBULDUDLUDLBUFFDBFLRL";
let expected = FaceCube::try_from(face_string).unwrap();
let cubie = CubieCube {
cp: [DRB, URF, DLF, ULB, DFR, UBR, DBL, UFL],
co: [0, 2, 0, 1, 1, 0, 2, 0],
ep: [UB, UL, DL, FR, FL, UR, BL, BR, DR, UF, DF, DB],
eo: [0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0],
};
let face_cube = FaceCube::try_from(&cubie).unwrap();
assert_eq!(face_cube, expected);
}
}