use std::collections::HashMap;
use crate::{
cubie::{Corner, CubieCube, Edge, SOLVED_CUBIE_CUBE},
moves::Move::{self, *},
};
use super::{get_available_move, Pruner, SolverBase, SolverConfig};
#[derive(Debug)]
pub struct FBSolver {
pub cube: CubieCube,
config: SolverConfig,
pruner: FBPruner,
}
impl SolverBase for FBSolver {
fn new(cube: CubieCube) -> Self {
let pruner = FBPruner::new();
let moveset = pruner.moveset.clone();
let mut next_moves = HashMap::new();
for m in moveset.clone() {
next_moves.insert(m, get_available_move(m, &moveset));
}
let config = SolverConfig {
min_depth: 0,
max_depth: 9,
moveset,
next_moves,
};
Self {
cube,
config,
pruner,
}
}
fn is_solved(&self) -> bool {
if self.cube.center[4] != SOLVED_CUBIE_CUBE.center[4] {
return false;
}
let (corners, edges) = FBSolver::get_state(&self.cube);
let mut solved = 0;
for c in corners {
match c {
(Corner::DLF, 5, 0) | (Corner::DBL, 6, 0) => solved += 1,
_ => {}
};
}
for e in edges {
match e {
(Edge::FL, 9, 0) | (Edge::BL, 10, 0) | (Edge::DL, 6, 0) => solved += 1,
_ => {}
};
}
if solved == 5 {
return true;
}
false
}
fn solve(&mut self) -> Vec<Move> {
let mut solution = Vec::new();
for i in self.config.min_depth..=self.config.max_depth {
let min_depth = self.config.min_depth;
solution = Self::solve_depth(
&self.cube,
min_depth,
i,
&mut self.config,
&self.pruner,
FBPruner::encode,
);
if solution.len() > 0 {
break;
}
}
self.cube = self.cube.apply_moves(&solution);
solution
}
}
impl FBSolver {
fn get_state(state: &CubieCube) -> (Vec<(Corner, u8, u8)>, Vec<(Edge, u8, u8)>) {
let mut corners = Vec::new();
for i in 0..8 {
match state.cp[i] {
Corner::DLF | Corner::DBL => corners.push((state.cp[i], i as u8, state.co[i])),
_ => {}
}
}
let mut edges = Vec::new();
for i in 0..12 {
match state.ep[i] {
Edge::DL | Edge::FL | Edge::BL => edges.push((state.ep[i], i as u8, state.eo[i])),
_ => {}
}
}
(corners, edges)
}
}
#[derive(Debug)]
struct FBPruner {
max_depth: u8,
dist: Vec<u8>,
moveset: Vec<Move>,
}
impl Pruner for FBPruner {
fn new() -> Self {
let size = 24usize.pow(3) * 24usize.pow(2);
let max_depth = 4;
let moveset = vec![
R, R2, R3, L, L2, L3, U, U2, U3, D, D2, D3, F, F2, F3, B, B2, B3, M, M2, M3, Rw, Rw2,
Rw3,
];
let mut dist = Self::init(size, FBPruner::encode, &moveset, max_depth);
dist[FBPruner::encode(&CubieCube::default())] = 0;
Self {
max_depth,
dist,
moveset,
}
}
fn encode(cube: &CubieCube) -> usize {
let mut c1 = 0;
let mut c2 = 0;
for i in 0..8 {
match cube.cp[i] {
Corner::DLF => c1 = i * 3 + cube.co[i] as usize,
Corner::DBL => c2 = i * 3 + cube.co[i] as usize,
_ => {}
}
}
let enc_c = c1 * 24 + c2;
let mut e1 = 0;
let mut e2 = 0;
let mut e3 = 0;
for i in 0..12 {
match cube.ep[i] {
Edge::DL => e1 = i * 2 + cube.eo[i] as usize,
Edge::FL => e2 = i * 2 + cube.eo[i] as usize,
Edge::BL => e3 = i * 2 + cube.eo[i] as usize,
_ => {}
}
}
let enc_e = e1 * (24 * 24) + e2 * (24) + e3;
enc_e * (24 * 24) + enc_c
}
fn query(&self, cube: &CubieCube) -> u8 {
let d = self.dist[FBPruner::encode(cube)];
if d == 255 {
return self.max_depth + 1;
}
d
}
}
#[cfg(test)]
mod tests {
use super::FBSolver;
use crate::{
cubie::CubieCube,
moves::{Formula, Move::*},
solver::roux::SolverBase,
};
#[test]
fn test_fb() {
let cc = CubieCube::default();
let _f = Formula { moves: vec![L2] };
let _f = Formula::scramble();
println!("Scramble: {:?}", _f);
let cc = cc.apply_formula(&_f);
let mut solver = FBSolver::new(cc);
let _s = solver.solve();
assert!(solver.is_solved());
println!("First Block Solution: {:?}", _s);
}
}