pub mod cmll;
pub mod fb;
pub mod lse;
pub mod sb;
use std::collections::{HashMap, HashSet};
pub use cmll::CMLLSolver;
pub use fb::FBSolver;
pub use lse::LSESolver;
pub use sb::SBSolver;
use crate::{
cubie::{CubieCube, SOLVED_CUBIE_CUBE},
moves::Move::{self, *},
};
#[derive(Debug)]
pub struct RouxSolver {
pub cube: CubieCube,
}
impl RouxSolver {
pub fn new(cube: CubieCube) -> Self {
Self { cube }
}
pub fn is_solved(&self) -> bool {
self.cube == SOLVED_CUBIE_CUBE
}
pub fn solve(&mut self) -> Vec<Move> {
let mut result = Vec::new();
let mut fb = FBSolver::new(self.cube);
let mut _fb = fb.solve();
assert!(fb.is_solved());
self.cube = fb.cube;
result.append(&mut _fb);
let mut sb = SBSolver::new(self.cube);
let mut _sb = sb.solve();
assert!(sb.is_solved());
self.cube = sb.cube;
result.append(&mut _sb);
let mut cmll = CMLLSolver::new(self.cube);
let mut _cmll = cmll.solve();
assert!(cmll.is_solved());
self.cube = cmll.cube;
result.append(&mut _cmll);
let mut lse = LSESolver::new(self.cube);
let mut _lse = lse.solve();
assert!(lse.is_solved());
self.cube = lse.cube;
result.append(&mut _lse);
assert!(self.is_solved());
result
}
}
#[derive(Debug)]
pub struct SolverConfig {
min_depth: i32,
max_depth: i32,
moveset: Vec<Move>,
next_moves: HashMap<Move, Vec<Move>>,
}
pub trait SolverBase {
fn new(cube: CubieCube) -> Self;
fn is_solved(&self) -> bool;
fn solve(&mut self) -> Vec<Move>;
fn solve_depth(
cube: &CubieCube,
min_depth: i32,
max_depth: i32,
config: &mut SolverConfig,
pruner: &impl Pruner,
encode: fn(&CubieCube) -> usize,
) -> Vec<Move> {
config.min_depth = min_depth;
config.max_depth = max_depth;
let cube = cube.clone();
let solution = Self::search(
&cube,
0,
&Vec::new(),
pruner.query(&cube),
&config,
pruner,
encode,
);
solution
}
fn search(
cube: &CubieCube,
depth: i32,
solution: &Vec<Move>,
d: u8,
config: &SolverConfig,
pruner: &impl Pruner,
encode: fn(&CubieCube) -> usize,
) -> Vec<Move> {
if d == 0 {
return solution.clone();
} else {
if depth >= config.max_depth {
return Vec::new();
};
if d as i32 + depth > config.max_depth {
return Vec::new();
} else {
return Self::expand(cube, depth, solution, config, pruner, encode);
}
}
}
fn expand(
cube: &CubieCube,
depth: i32,
solution: &Vec<Move>,
config: &SolverConfig,
pruner: &impl Pruner,
encode: fn(&CubieCube) -> usize,
) -> Vec<Move> {
let mut solution = solution.clone();
let available_moves = match solution.len() > 0 {
true => config
.next_moves
.get(&solution[solution.len() - 1])
.unwrap()
.clone(),
false => config.moveset.clone(),
};
let mut seen_encodings = HashSet::new();
seen_encodings.insert(encode(cube));
for m in available_moves.iter() {
let new_cube = cube.apply_move(*m);
let enc = encode(&new_cube);
if seen_encodings.len() == 0 || !seen_encodings.contains(&enc) {
seen_encodings.insert(enc);
solution.push(*m);
let st = Self::search(
&new_cube,
depth + 1,
&solution,
pruner.query(&new_cube),
config,
pruner,
encode,
);
solution.pop();
if st.len() > 0 {
return st;
}
}
}
Vec::new()
}
}
pub trait Pruner {
fn new() -> Self;
fn encode(cube: &CubieCube) -> usize;
fn query(&self, cube: &CubieCube) -> u8;
fn init(
size: usize,
encode: fn(&CubieCube) -> usize,
moveset: &Vec<Move>,
max_depth: u8,
) -> Vec<u8> {
let solved_states = vec![CubieCube::default()];
let mut dist: Vec<u8> = Vec::with_capacity(size);
for _ in 0..size {
dist.push(255);
}
let mut frontier = solved_states.clone();
for i in 0..max_depth {
let mut new_frontier = Vec::new();
for state in frontier {
for m in moveset.iter() {
let new_state = state.apply_move(*m);
let idx = encode(&new_state);
if dist[idx] == 255 {
dist[idx] = i as u8 + 1;
new_frontier.push(new_state);
}
}
}
frontier = new_frontier;
}
dist
}
}
fn get_available_move(m: Move, moveset: &Vec<Move>) -> Vec<Move> {
match m {
U | U2 | U3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "U")
.collect::<Vec<_>>(),
R | R2 | R3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "R")
.collect::<Vec<_>>(),
Rw | Rw2 | Rw3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "Rw")
.collect::<Vec<_>>(),
F | F2 | F3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "F")
.collect::<Vec<_>>(),
D | D2 | D3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "D")
.collect::<Vec<_>>(),
L | L2 | L3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "L")
.collect::<Vec<_>>(),
B | B2 | B3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "B")
.collect::<Vec<_>>(),
M | M2 | M3 => moveset
.clone()
.into_iter()
.filter(|k| k.get_face() != "M")
.collect::<Vec<_>>(),
_ => moveset.clone(),
}
}
#[cfg(test)]
mod tests {
use super::RouxSolver;
use crate::{cubie::CubieCube, moves::Formula};
#[test]
fn test_roux() {
let cc = CubieCube::default();
let f = Formula::scramble();
let cc = cc.apply_formula(&f);
let mut roux = RouxSolver::new(cc);
let _roux = roux.solve();
assert!(roux.is_solved());
println!("Scramble: {:?}\nRoux Solution: {:?}", f.moves, _roux);
}
}