diff --git a/src/playground.rs b/src/board.rs similarity index 89% rename from src/playground.rs rename to src/board.rs index 54963af..97aff23 100644 --- a/src/playground.rs +++ b/src/board.rs @@ -2,18 +2,18 @@ use std::collections::HashSet; #[derive(Clone)] -pub struct Playground { +pub struct Board { arr: [[u8; 9]; 9] } -impl Playground { +impl Board { - pub fn new() -> Playground { - return Playground { arr: [[0; 9]; 9] }; + pub fn new() -> Board { + return Board { arr: [[0; 9]; 9] }; } - pub fn from_array(arr: &[u8;81]) -> Playground { - let mut p = Playground::new(); + pub fn from_array(arr: &[u8;81]) -> Board { + let mut p = Board::new(); for y in 0..9 { for x in 0..9 { p.arr[y][x] = arr[y*9+x]; @@ -61,7 +61,7 @@ impl Playground { .all(|value| *value != 0u8); } - /** Checks if the playground has failures (not neccesary solved) */ + /** Checks if the Board has failures (not neccesary solved) */ pub fn is_valid(&self) -> bool { self.rows_valid() && self.columns_valid() && self.sections_valid() } @@ -113,7 +113,7 @@ impl Playground { true } - pub fn contains(&self, other : &Playground) -> bool { + pub fn contains(&self, other : &Board) -> bool { for x in 0..9 { for y in 0..9 { if other.arr[y][x] != 0 && self.arr[y][x] != other.arr[y][x] { diff --git a/src/hecht.rs b/src/hecht.rs index 8e3dbd4..cf74eab 100644 --- a/src/hecht.rs +++ b/src/hecht.rs @@ -1,39 +1,57 @@ + +use std::collections::HashSet; use crate::solver::Solver; -use crate::playground::Playground; -use crate::utils::Locatable; -use crate::utils::LocatableVec; -use crate::utils::FsVec; +use crate::board::Board; +use std::hash::{Hash, Hasher}; +use std::ops::ControlFlow; + +static CELLS : usize = 81; pub struct HechtSolver { } -#[derive(Hash, Clone, Copy, Eq, PartialEq, Debug, Default)] +#[derive(Hash, Clone, Eq, PartialEq, Debug)] struct Point { pub x: usize, pub y: usize, pub s: usize, } -impl Locatable for Point { - fn get_xy(&self) -> (usize, usize) { - (self.x,self.y) - } -} - #[derive(Debug)] struct Action { pub p : Point, pub value: u16 } -#[derive(Clone)] -pub struct SolverPlayground { +#[derive(Clone, Eq)] +struct SolverBoard { arr: [[u16; 9]; 9], - todos: LocatableVec, + todos: HashSet, valid: bool, - rec_level: usize, +} + +enum SolverBoardItem { + Recursive(SolverBoard, Vec), + Initial(SolverBoard), +} + +struct SolverBoardIterator { + cache: HashSet<[[u16; 9]; 9]>, // to avoid processing the same board twice + stack: Vec, +} + +impl PartialEq for SolverBoard { + fn eq(&self, other: &SolverBoard) -> bool { + self.arr == other.arr && self.valid == other.valid + } +} + +impl Hash for SolverBoard { + fn hash(&self, state: &mut H) { + self.arr.hash(state); + } } impl HechtSolver { @@ -43,33 +61,55 @@ impl HechtSolver { } impl Solver for HechtSolver { - fn solve(&self, pg: &Playground) -> Option { - SolverPlayground::new(&pg) - .solve() + fn solve(&self, pg: &Board) -> Option { + SolverBoardIterator::new(pg) + .find(|x| x.is_solved()) + .map(|x| x.to_board()) + .flatten() + } + + fn is_unique(&self, pg: &Board) -> bool { + let first_solution = SolverBoard::new(); + + SolverBoardIterator::new(pg) + .filter(|x| x.is_solved()) + .try_fold(first_solution, |prev, x| { + if !prev.is_solved() || prev == x { + ControlFlow::Continue(x) + } else { + ControlFlow::Break(prev) + } + }).is_continue() } } -impl SolverPlayground { +impl SolverBoard { - pub fn new(pg: &Playground) -> SolverPlayground { - let points : LocatableVec<_, 81> = [0usize..81].into_iter().flatten() + pub fn new() -> SolverBoard { + let points = [0usize..CELLS].into_iter().flatten() .map(|idx| [idx%9, idx/9]) .map(|p| Point{x: p[0], y:p[1], s: p[1] / 3 * 3 + p[0] / 3}) - .collect(); - - let mut spg = SolverPlayground { arr: [[0x1FFu16; 9]; 9], todos: points.clone(), valid: true, rec_level: 0}; + .collect::>(); + + SolverBoard { arr: [[0x1FFu16; 9]; 9], todos: points, valid: true} + } + + pub fn from(pg: &Board) -> SolverBoard { + let mut spg = SolverBoard::new(); - for p in points.iter() { + for p in spg.todos.clone().iter() { if let Some(value) = pg.get_value(p.x, p.y) { spg.set_value(&p, value); } } + spg.solve_logically(); + spg } - fn to_playground(&self) -> Option { - let mut pg = Playground::new(); + fn to_board(&self) -> Option { + let mut pg = Board::new(); for x in 0..9 { for y in 0..9 { @@ -85,16 +125,18 @@ impl SolverPlayground { Some(pg) } - pub fn solve(&mut self) -> Option { - while self.valid && self.todos.len() > 0 { + fn solve_logically(&mut self) { + while self.valid { if let Some(action) = self.get_simple_action() { - self.apply(&action); + self.set_bit_value(&action.p, action.value); } else { - return self.get_complex_action(); + break; } } - - self.to_playground() + } + + fn is_solved(&self) -> bool { + self.valid && self.todos.len() == 0 } fn set_value(&mut self, p : &Point, value : u8) { @@ -116,7 +158,7 @@ impl SolverPlayground { // Filter positions that were known not to hold the value .filter(|&pos| (self.arr[pos.y][pos.x] & bit_value) != 0) .cloned() - .collect::>() + .collect::>() .iter() .for_each(|pos| self.remove_value(&pos, bit_value)); @@ -128,8 +170,8 @@ impl SolverPlayground { } self.arr[p.y][p.x] &= !bit_value; - if let Some(action) = SolverPlayground::create_action(p, self.arr[p.y][p.x]) { - self.apply(&action); + if let Some(action) = SolverBoard::create_action(p, self.arr[p.y][p.x]) { + self.set_bit_value(&action.p, action.value); } else if self.arr[p.y][p.x] == 0 { self.valid = false; } @@ -144,6 +186,7 @@ impl SolverPlayground { fn apply(&mut self, action : &Action) { self.set_bit_value(&action.p, action.value); + self.solve_logically(); } #[inline] @@ -169,11 +212,11 @@ impl SolverPlayground { // println!("Simple Action"); self.todos.iter() .find_map(|lhs| { - let own_value = self.get_value(&lhs); + let own_value = self.get_value(lhs); self.todos.iter() .filter(|&rhs| lhs.x == rhs.x || lhs.y == rhs.y || lhs.s == rhs.s) .filter(|&rhs| lhs != rhs) - .map(|&rhs| { + .map(|rhs| { let value = self.get_value(&rhs) & own_value; let row_value = if lhs.x == rhs.x {value} else {0}; let col_value = if lhs.y == rhs.y {value} else {0}; @@ -186,26 +229,111 @@ impl SolverPlayground { .flatten() .map(|value| value ^ own_value) .filter(|value| value.count_ones() == 1) - .find_map(|value| SolverPlayground::create_action(lhs, value)) + .find_map(|value| SolverBoard::create_action(lhs, value)) } ) } + + fn get_all_actions(&self) -> Vec { + if self.valid && !self.is_solved() { + let mut vec = self.todos.iter() + .map(|point| Action{p: point.clone(), value: self.get_value(point)}) + .collect::>(); // collect into the vector so we can (optionally) apply sorting + + // now sort it (lowest value should be at the end (we use it as a stack)) + vec.sort_unstable_by_key(|action| CELLS as u32 - action.value.count_ones()); + + return vec.iter() + .map(|action| SolverBoard::get_bitvalue_sequence().iter() + .filter(|&value| action.value & value != 0) + .map(|&value| Action{p: action.p.clone(), value}) + .collect::>() + ) + .flatten() + .collect::>(); + } + Vec::new() + } + +} + +impl SolverBoardIterator { + fn new(board : &Board) -> SolverBoardIterator { + SolverBoardIterator::from_board(SolverBoard::from(board)) + } + + fn from_board(board: SolverBoard) -> SolverBoardIterator { + SolverBoardIterator{cache: HashSet::with_capacity(2048), stack: vec![SolverBoardItem::Initial(board)]} + } + + fn next_board(&mut self) -> Option { + let mut result : Option = None; + while let Some(mut item) = self.stack.pop() { + if let Some(action) = item.pop_action() { + if let Some(board) = item.get_board() { + let mut new_board = board.clone(); + self.stack.push(item); + new_board.apply(&action); + result = self.add_recursive(new_board); + } + } else if let Some(board) = item.get_board() { // initial + result = self.add_recursive(board.clone()); + } + + if result.is_some() { + return result + } + } + + println!("Cache size: {}", self.cache.len()); + None + } + + fn add_to_cache(&mut self, board: &SolverBoard) -> Option { + if !self.cache.contains(&board.arr) { + self.cache.insert(board.arr); + return Some(board.clone()) + } + None + } + + fn add_recursive(&mut self, board: SolverBoard) -> Option { + if board.valid { + let result = self.add_to_cache(&board); + if result.is_some() { + let actions = board.get_all_actions(); + self.stack.push(SolverBoardItem::Recursive(board, actions)); + } + return result; + } + None + } + +} + +impl SolverBoardItem { + + fn get_board(&self) -> Option<&SolverBoard> { + if let SolverBoardItem::Recursive(board, _) = self { + return Some(board) + } else if let SolverBoardItem::Initial(board) = self { + return Some(board) + } + None + } - fn get_complex_action(&self) -> Option { - // println!("Complex Action"); - let result = self.todos.iter() - .min_by_key(|x| self.get_value(x).count_ones()); - - if let Some(point) = result { - return SolverPlayground::get_bitvalue_sequence().iter() - .filter(|&value| value & self.get_value(point) != 0) - .find_map(|&value| { - let mut spg = self.clone(); - spg.rec_level += 1; - spg.set_bit_value(&point, value); - spg.solve() - }) + fn pop_action(&mut self) -> Option { + if let SolverBoardItem::Recursive(_, actions) = self { + return actions.pop() } None } } +impl Iterator for SolverBoardIterator { + type Item = SolverBoard; + + fn next(&mut self) -> Option { + self.next_board() + } + +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9971d4d..c6da2f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ -mod utils; -mod playground; +mod board; mod solver; mod mysolver; mod hecht; use hecht::HechtSolver; -use playground::Playground; +use board::Board; use solver::Solver; use clap::Parser; @@ -130,7 +129,7 @@ mod tests { #[case(7)] #[case(8)] fn solve_sudoku(#[case] field : usize) { - let pg = Playground::from_array(&FIELDS[field]); + let pg = Board::from_array(&FIELDS[field]); let solver = HechtSolver::new(); let result = solver.solve(&pg); @@ -157,7 +156,7 @@ fn main() { let args = Args::parse(); - let pg = Playground::from_array(&FIELDS[args.scenario as usize]); + let pg = Board::from_array(&FIELDS[args.scenario as usize]); pg.print(); @@ -166,17 +165,24 @@ fn main() { println!(""); if let Some(result) = solver.solve(&pg) { - result.print(); - if !result.contains(&pg) { println!("Solver modified predefined fields!") } if !result.is_valid() { println!("Solver has not correctly solved the sudoku!") } else if !result.is_solved() { println!("Solver was not able to solve the sudoku!") + } else { + println!("Solver found a solution for this sudoku!!") + } + result.print(); + + if solver.is_unique(&pg) { + println!("Solver states, that this sudoku has only one solution!") + } else { + println!("Solver states, that this sudoku has multiple solutions!") } - assert!(result.contains(&pg)); + } else { - println!("Solver was not able to resolve the sodoku!") + println!("Solver was not able to solve the sodoku!") } } diff --git a/src/mysolver.rs b/src/mysolver.rs index f6e68fc..f9b9f81 100644 --- a/src/mysolver.rs +++ b/src/mysolver.rs @@ -1,5 +1,5 @@ use crate::solver::Solver; -use crate::playground::Playground; +use crate::board::Board; pub struct MySolver { } @@ -11,8 +11,13 @@ impl MySolver { } impl Solver for MySolver { - fn solve(&self, _pg: &Playground) -> Option { + fn solve(&self, _pg: &Board) -> Option { // FIXME: Implement! None } + + fn is_unique(&self, _ : &Board) -> bool { + // FIXME: Implement + false + } } \ No newline at end of file diff --git a/src/solver.rs b/src/solver.rs index d133a30..67dbd21 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -1,11 +1,12 @@ -use crate::playground::Playground; +use crate::board::Board; pub trait Solver { - fn solve(&self, _: &Playground) -> Option; + fn solve(&self, _: &Board) -> Option; + fn is_unique(&self, _ : &Board) -> bool; } -enum SodokuComplexity { +pub enum SodokuComplexity { Trivial, Easy, Average, @@ -15,5 +16,5 @@ enum SodokuComplexity { } pub trait Generator{ - fn generate(&self, _complexity:&SodokuComplexity) -> Playground; + fn generate(&self, _complexity:&SodokuComplexity) -> Board; } \ No newline at end of file