|
|
|
@ -4,7 +4,6 @@ use std::collections::HashSet;
|
|
|
|
|
use crate::solver::{Solver, Generator, SodokuComplexity};
|
|
|
|
|
use crate::board::Board;
|
|
|
|
|
use crate::utils::*;
|
|
|
|
|
use std::borrow::Borrow;
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
use std::hash::{Hash, Hasher};
|
|
|
|
|
|
|
|
|
@ -42,14 +41,14 @@ struct SolverBoard {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum SolverBoardItem {
|
|
|
|
|
Prepare(Rc<SolverBoard>, Rc<HashSet<Action>>, Rc<Vec<Action>>, usize),
|
|
|
|
|
Process(Rc<SolverBoard>, Rc<HashSet<Action>>, Action),
|
|
|
|
|
Process(Rc<SolverBoard>, Action),
|
|
|
|
|
Initial(SolverBoard),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SolverBoardIterator {
|
|
|
|
|
cache: HashSet<[[u16; VALUES]; VALUES]>, // to avoid processing the same board twice
|
|
|
|
|
stack: Vec<SolverBoardItem>,
|
|
|
|
|
solve: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PartialEq for SolverBoard {
|
|
|
|
@ -70,24 +69,12 @@ impl HechtSolver {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn test(&self, board: &Board) {
|
|
|
|
|
let mut invalid_counter = 0;
|
|
|
|
|
let mut unsolved_counter = 0;
|
|
|
|
|
let mut solved_counter = 0;
|
|
|
|
|
let count = 1024;
|
|
|
|
|
|
|
|
|
|
let mut boards : HashSet<Board> = HashSet::with_capacity(count);
|
|
|
|
|
|
|
|
|
|
let count = SolverBoardIterator::new(board)
|
|
|
|
|
.inspect(|_| invalid_counter += 1)
|
|
|
|
|
.filter(|x| x.valid)
|
|
|
|
|
.inspect(|_| unsolved_counter += 1)
|
|
|
|
|
.filter(|x| x.is_solved())
|
|
|
|
|
.inspect(|_| solved_counter += 1)
|
|
|
|
|
.filter_map(|x| x.to_board() )
|
|
|
|
|
.take(1024)
|
|
|
|
|
.for_each(|board| {boards.insert(board); });
|
|
|
|
|
.inspect(|_| println!("Solved Board detected"))
|
|
|
|
|
.count();
|
|
|
|
|
println!("Count: {}", count);
|
|
|
|
|
|
|
|
|
|
println!("I: {}, U: {}, S: {}, B: {}", invalid_counter, unsolved_counter, solved_counter, boards.len())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -298,28 +285,20 @@ impl SolverBoard {
|
|
|
|
|
} )
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_all_actions(&self) -> Vec<Action> {
|
|
|
|
|
if self.valid && !self.is_solved() {
|
|
|
|
|
let mut vec = self.todos.iter()
|
|
|
|
|
.map(|point| (point, self.get_value(point)))
|
|
|
|
|
.collect::<Vec<_>>(); // collect into the vector so we can (optionally) apply sorting
|
|
|
|
|
fn get_probe_actions(&self) -> Vec<Action> {
|
|
|
|
|
let result = self.todos.iter()
|
|
|
|
|
.min_by_key(|point| self.get_value(point).count_ones());
|
|
|
|
|
|
|
|
|
|
vec.sort_unstable_by_key(|action| action.1.count_ones());
|
|
|
|
|
|
|
|
|
|
// println!(" {} --> {:?} has only {} possibilities", self.todos.len(), vec[0].0, vec[0].1.count_ones());
|
|
|
|
|
|
|
|
|
|
return vec.iter()
|
|
|
|
|
.map(|action| SolverBoard::get_bitvalue_sequence().iter()
|
|
|
|
|
.filter(|&value| (action.1 & value) != 0)
|
|
|
|
|
.map(|&value| Action::Probe(action.0.clone(), value))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
)
|
|
|
|
|
.flatten()
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
if let Some(point) = result {
|
|
|
|
|
let p_value = self.get_value(point);
|
|
|
|
|
return SolverBoard::get_bitvalue_sequence().iter()
|
|
|
|
|
.filter(|&value| (p_value & value ) != 0)
|
|
|
|
|
.map(|&value| Action::Probe(point.clone(), value))
|
|
|
|
|
.collect();
|
|
|
|
|
}
|
|
|
|
|
Vec::new()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SolverBoardIterator {
|
|
|
|
@ -328,12 +307,12 @@ impl SolverBoardIterator {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn from_board(board: SolverBoard) -> SolverBoardIterator {
|
|
|
|
|
SolverBoardIterator{cache: HashSet::with_capacity(128), stack: vec![SolverBoardItem::Initial(board)]}
|
|
|
|
|
SolverBoardIterator{cache: HashSet::with_capacity(128), stack: vec![SolverBoardItem::Initial(board)], solve: true}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn next_board(&mut self) -> Option<SolverBoard> {
|
|
|
|
|
while let Some(item) = self.stack.pop() {
|
|
|
|
|
// println!("Stack size {}, cache size {}", self.stack.len(), self.cache.len());
|
|
|
|
|
println!("Stack size {}, cache size {}", self.stack.len(), self.cache.len());
|
|
|
|
|
if let Some(board) = self.pop_board(item) {
|
|
|
|
|
return Some(board);
|
|
|
|
|
}
|
|
|
|
@ -347,43 +326,20 @@ impl SolverBoardIterator {
|
|
|
|
|
SolverBoardItem::Initial(board) => {
|
|
|
|
|
self.handle_initial(board)
|
|
|
|
|
}
|
|
|
|
|
SolverBoardItem::Process(board, excludes, action) => {
|
|
|
|
|
self.handle_process(board, excludes, action)
|
|
|
|
|
}
|
|
|
|
|
SolverBoardItem::Prepare(board, excludes, actions, index) => {
|
|
|
|
|
self.handle_prepare(board, excludes, actions, index)
|
|
|
|
|
SolverBoardItem::Process(board, action) => {
|
|
|
|
|
self.handle_process(board, action)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_initial(&mut self, board: SolverBoard) -> Option<SolverBoard> {
|
|
|
|
|
if board.valid {
|
|
|
|
|
return self.handle_board(Rc::new(board), Rc::new(HashSet::with_capacity(CELLS * VALUES)))
|
|
|
|
|
return self.handle_board(Rc::new(board))
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The preparation step is quite memory and time consuming.
|
|
|
|
|
* For this reason it has been extracted into a separate computing step.
|
|
|
|
|
* Best case is, that only the last process step requires preparation.
|
|
|
|
|
*/
|
|
|
|
|
fn handle_prepare(&mut self, board_ref: Rc<SolverBoard>, excludes_ref: Rc<HashSet<Action>>, actions: Rc<Vec<Action>>, index : usize) -> Option<SolverBoard> {
|
|
|
|
|
let mut excludes_ref = excludes_ref; // re-label
|
|
|
|
|
let excludes = Rc::make_mut(&mut excludes_ref);
|
|
|
|
|
|
|
|
|
|
// we always start with a high index??
|
|
|
|
|
assert!(board_ref.valid);
|
|
|
|
|
|
|
|
|
|
// extend the current exclude list by the exceeded actions in the list (before the own index)
|
|
|
|
|
actions.iter().take(index)
|
|
|
|
|
.for_each(|action| {excludes.insert(action.clone());});
|
|
|
|
|
|
|
|
|
|
self.stack.push(SolverBoardItem::Process(Rc::clone(&board_ref), Rc::clone(&excludes_ref), actions[index].clone()));
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_process(&mut self, board_ref: Rc<SolverBoard>, excludes: Rc<HashSet<Action>>, action: Action) -> Option<SolverBoard> {
|
|
|
|
|
fn handle_process(&mut self, board_ref: Rc<SolverBoard>, action: Action) -> Option<SolverBoard> {
|
|
|
|
|
let mut board_ref = board_ref; // re-label
|
|
|
|
|
let board = Rc::make_mut(&mut board_ref);
|
|
|
|
|
|
|
|
|
@ -394,19 +350,16 @@ impl SolverBoardIterator {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self.handle_board(board_ref, excludes);
|
|
|
|
|
return self.handle_board(board_ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn handle_board(&mut self, board: Rc<SolverBoard>, excludes: Rc<HashSet<Action>>)-> Option<SolverBoard> {
|
|
|
|
|
fn handle_board(&mut self, board: Rc<SolverBoard>)-> Option<SolverBoard> {
|
|
|
|
|
if !board.is_solved() {
|
|
|
|
|
|
|
|
|
|
let actions = Rc::new(board.get_all_actions());
|
|
|
|
|
actions.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.rev() //
|
|
|
|
|
.filter(|it| !excludes.contains(it.1))
|
|
|
|
|
.for_each(|it| self.stack.push(SolverBoardItem::Prepare(Rc::clone(&board), Rc::clone(&excludes), Rc::clone(&actions), it.0)))
|
|
|
|
|
let actions = board.get_probe_actions();
|
|
|
|
|
actions.into_iter()
|
|
|
|
|
.for_each(|action| self.stack.push(SolverBoardItem::Process(Rc::clone(&board), action)))
|
|
|
|
|
}
|
|
|
|
|
Some((*board).clone())
|
|
|
|
|
}
|
|
|
|
|