use crate::cubie::{Corner, CubieCube};
use crate::facelet::Color;
use crate::moves::Formula;
use crate::moves::Move::{self, *};
use std::collections::HashMap;
pub struct CMLLSolver {
pub cube: CubieCube,
algos: HashMap<String, Vec<Move>>,
}
impl CMLLSolver {
pub fn new(cube: CubieCube) -> Self {
let mut algos = HashMap::new();
algos.insert("RFFLLBBR".to_string(), vec![]);
algos.insert(
"BRFLLBRF".to_string(),
vec![R, U, R3, F3, R, U, R3, U3, R3, F, R2, U3, R3],
);
algos.insert(
"LBFLRFBR".to_string(),
vec![F, R, U3, R3, U3, R, U, R3, F3, R, U, R3, U3, R3, F, R, F3],
);
algos.insert(
"RUURLUUL".to_string(),
vec![U3, R, U, R3, U, R, U3, R3, U, R, U2, R3],
);
algos.insert(
"LUURRUUL".to_string(),
vec![F, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, F3],
);
algos.insert(
"FUULLUUB".to_string(),
vec![U3, R, U2, R2, F, R, F3, U2, R3, F, R, F3],
);
algos.insert(
"LUURBUUB".to_string(),
vec![U2, Rw, U3, Rw2, D3, Rw, U3, Rw3, D, Rw2, U, Rw3],
);
algos.insert(
"FULUULUB".to_string(),
vec![F, R, U, R3, U3, R, U, R3, U3, F3],
);
algos.insert(
"BURUUFUR".to_string(),
vec![U, F, R3, F3, R, U2, R, U3, R3, U, R, U2, R3],
);
algos.insert(
"BUFUUFUB".to_string(),
vec![U3, R3, F, R, U, F, U3, R, U, R3, U3, F3],
);
algos.insert(
"FURUUFUL".to_string(),
vec![R, U2, R3, U3, R, U, R3, U2, R3, F, R, F3],
);
algos.insert(
"LURUULUR".to_string(),
vec![U3, Rw, U3, Rw2, D3, Rw, U, Rw3, D, Rw2, U, Rw3],
);
algos.insert(
"FURUULUF".to_string(),
vec![U3, R3, U3, R3, F, R, F3, R, U3, R3, U2, R],
);
algos.insert(
"LBRFLUUB".to_string(),
vec![U2, R2, D, R3, U2, R, D3, R3, U2, R3],
);
algos.insert("BRFLFUUL".to_string(), vec![R2, D3, R, U2, R3, D, R, U2, R]);
algos.insert(
"RFFLBUUB".to_string(),
vec![R3, U3, R, U3, R3, U2, R2, U, R3, U, R, U2, R3],
);
algos.insert(
"RFFLRUUL".to_string(),
vec![U3, F, R2, D, R3, U, R, D3, R2, U3, F3],
);
algos.insert(
"FLRFBUUB".to_string(),
vec![U2, Rw, U3, Rw3, U, Rw3, D3, Rw, U3, Rw3, D, Rw],
);
algos.insert("LBFLFUUB".to_string(), vec![F, R, U, R3, U3, F3]);
algos.insert("LBRFUBLU".to_string(), vec![U3, R, U, R3, U3, R3, F, R, F3]);
algos.insert("FLBRURBU".to_string(), vec![U, L3, U3, L, U, L, F3, L3, F]);
algos.insert(
"BRRFUFBU".to_string(),
vec![R, U2, R3, U3, R, U3, R2, U2, R, U, R3, U, R],
);
algos.insert(
"LBBRUFFU".to_string(),
vec![Rw3, U, Rw, U2, R2, F, R, F3, R],
);
algos.insert(
"BRLBURLU".to_string(),
vec![Rw3, D3, Rw, U, Rw3, D, Rw, U3, Rw, U, Rw3],
);
algos.insert(
"BRLBUFFU".to_string(),
vec![U2, Rw2, D3, Rw, U, Rw3, D, Rw2, U3, Rw3, U3, Rw],
);
algos.insert("BRFULUBU".to_string(), vec![R, U, R3, U, R, U2, R3]);
algos.insert("RFBULURU".to_string(), vec![L3, U2, L, U2, L, F3, L3, F]);
algos.insert("RFLURUBU".to_string(), vec![F, R3, F3, R, U2, R, U2, R3]);
algos.insert(
"RFBURULU".to_string(),
vec![R, U, R3, U3, R3, F, R, F3, R, U, R3, U, R, U2, R3],
);
algos.insert(
"LBLUFURU".to_string(),
vec![U2, R, U, R3, U, R3, F, R, F3, R, U2, R3],
);
algos.insert("RFRULUBU".to_string(), vec![R, U3, L3, U, R3, U3, L]);
algos.insert("UBRFUFUL".to_string(), vec![U3, R, U2, R3, U3, R, U3, R3]);
algos.insert(
"ULRFUBUF".to_string(),
vec![R2, D, R3, U, R, D3, R3, U, R3, U3, R, U3, R3],
);
algos.insert(
"URFLUBUL".to_string(),
vec![U3, F3, Rw, U, Rw3, U2, Rw3, F2, Rw],
);
algos.insert("UFBRURUL".to_string(), vec![R, U2, R3, U2, R3, F, R, F3]);
algos.insert("URBRUFUL".to_string(), vec![L3, U, R, U3, L, U, R3]);
algos.insert(
"ULFLURUB".to_string(),
vec![U2, R, U2, R3, F, R3, F3, R, U3, R, U3, R3],
);
algos.insert("RFLUBRUL".to_string(), vec![F, R, U3, R3, U3, R, U, R3, F3]);
algos.insert("FLRULBUR".to_string(), vec![F, R3, F3, R, U, R, U3, R3]);
algos.insert(
"RFLULBUB".to_string(),
vec![U2, R, U, R3, U, R, U3, R3, U, R, U3, R3, U, R, U2, R3],
);
algos.insert("RFRUFLUL".to_string(), vec![R, U2, R, D, R3, U2, R, D3, R2]);
algos.insert(
"BRBUFLUR".to_string(),
vec![U2, R3, U3, R, U, R3, F3, R, U, R3, U3, R3, F, R2],
);
algos.insert(
"BRLULBUR".to_string(),
vec![U3, R3, U2, R3, D3, R, U2, R3, D, R2],
);
Self { cube, algos }
}
fn recognise(&self) -> [Color; 8] {
let mut idx = [Color::U; 8];
for i in 0..4 {
let cp = self.cube.cp[i];
let co = self.cube.co[i];
let colors = get_colors(cp, co);
idx[i * 2] = colors.0;
idx[i * 2 + 1] = colors.1;
}
idx
}
pub fn solve(&mut self) -> Vec<Move> {
let mut result = Vec::new();
for i in 0..4 {
let mut i_put = match i {
1 => Formula {
moves: vec![Move::U],
},
2 => Formula {
moves: vec![Move::U2],
},
3 => Formula {
moves: vec![Move::U3],
},
_ => Formula { moves: vec![] },
};
self.cube = self.cube.apply_formula(&i_put);
let case = self.recognise();
for r in 0..4 {
let case: String = case
.iter()
.map(|c| rotate_color(*c, r))
.map(|c| format!("{:?}", c))
.collect();
let algo = self.algos.get(&case);
if algo.is_some() {
let algo = Formula {
moves: algo.unwrap().clone(),
};
for j in 0..4 {
let mut j_put = match j {
1 => Formula {
moves: vec![Move::U],
},
2 => Formula {
moves: vec![Move::U2],
},
3 => Formula {
moves: vec![Move::U3],
},
_ => Formula { moves: vec![] },
};
self.cube = self.cube.apply_formula(&j_put);
self.cube = self.cube.apply_formula(&algo);
for k in 0..4 {
let mut k_put = match k {
1 => Formula {
moves: vec![Move::U],
},
2 => Formula {
moves: vec![Move::U2],
},
3 => Formula {
moves: vec![Move::U3],
},
_ => Formula { moves: vec![] },
};
self.cube = self.cube.apply_formula(&k_put);
if self.is_solved() {
result.append(&mut i_put.moves);
result.append(&mut j_put.moves);
result.append(&mut self.algos[&case].clone());
result.append(&mut k_put.moves);
return result;
}
self.cube = self.cube.apply_formula(&k_put.inverse());
}
self.cube = self.cube.apply_formula(&algo.inverse());
self.cube = self.cube.apply_formula(&j_put.inverse());
}
}
}
self.cube = self.cube.apply_formula(&i_put.inverse());
}
result
}
pub fn is_solved(&self) -> bool {
let mut solved = 0;
for i in 0..4 {
match (i, self.cube.cp[i], self.cube.co[i]) {
(0, Corner::URF, 0)
| (1, Corner::UFL, 0)
| (2, Corner::ULB, 0)
| (3, Corner::UBR, 0) => solved += 1,
_ => {}
}
}
solved == 4
}
}
fn get_colors(cp: Corner, co: u8) -> (Color, Color) {
let color = corner_to_face(cp);
match co {
0 => (color.1, color.2),
1 => (color.0, color.1),
_ => (color.2, color.0),
}
}
fn rotate_color(color: Color, r: usize) -> Color {
match color {
Color::L => match r {
1 => Color::F,
2 => Color::R,
3 => Color::B,
_ => Color::L,
},
Color::F => match r {
1 => Color::R,
2 => Color::B,
3 => Color::L,
_ => Color::F,
},
Color::R => match r {
1 => Color::B,
2 => Color::L,
3 => Color::F,
_ => Color::R,
},
Color::B => match r {
1 => Color::L,
2 => Color::F,
3 => Color::R,
_ => Color::B,
},
_ => color,
}
}
fn corner_to_face(corner: Corner) -> (Color, Color, Color) {
let corner = format!("{:?}", corner);
let corner = corner.as_bytes();
(
Color::try_from(char::from(corner[0])).unwrap(),
Color::try_from(char::from(corner[1])).unwrap(),
Color::try_from(char::from(corner[2])).unwrap(),
)
}
#[cfg(test)]
mod tests {
use super::super::fb::FBSolver;
use super::super::sb::SBSolver;
use super::CMLLSolver;
use crate::{cubie::CubieCube, moves::Formula, solver::roux::SolverBase};
#[test]
fn test_cmll() {
let cc = CubieCube::default();
let f = Formula::scramble();
let cc = cc.apply_formula(&f);
let mut fb = FBSolver::new(cc);
let _fb = fb.solve();
assert!(fb.is_solved());
let mut sb = SBSolver::new(fb.cube);
let _sb = sb.solve();
assert!(sb.is_solved());
let mut cmll = CMLLSolver::new(sb.cube);
let _cmll = cmll.solve();
assert!(cmll.is_solved());
println!(
"Scramble: {:?}\nFirst Block: {:?}\nSecond Block: {:?}\nCMLL: {:?}",
f.moves, _fb, _sb, _cmll
);
}
}