Converted the project to use iterators instead of recursion

master
Hecht 2 years ago
parent 33684908d7
commit 13643c2df3

@ -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] {

@ -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<Point, 81>,
todos: HashSet<Point>,
valid: bool,
rec_level: usize,
}
enum SolverBoardItem {
Recursive(SolverBoard, Vec<Action>),
Initial(SolverBoard),
}
struct SolverBoardIterator {
cache: HashSet<[[u16; 9]; 9]>, // to avoid processing the same board twice
stack: Vec<SolverBoardItem>,
}
impl PartialEq for SolverBoard {
fn eq(&self, other: &SolverBoard) -> bool {
self.arr == other.arr && self.valid == other.valid
}
}
impl Hash for SolverBoard {
fn hash<H: Hasher>(&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<Playground> {
SolverPlayground::new(&pg)
.solve()
fn solve(&self, pg: &Board) -> Option<Board> {
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::<HashSet<_>>();
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<Playground> {
let mut pg = Playground::new();
fn to_board(&self) -> Option<Board> {
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<Playground> {
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::<FsVec::<_,81>>()
.collect::<Vec::<_>>()
.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<Action> {
if self.valid && !self.is_solved() {
let mut vec = self.todos.iter()
.map(|point| Action{p: point.clone(), value: self.get_value(point)})
.collect::<Vec<_>>(); // 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::<Vec<_>>()
)
.flatten()
.collect::<Vec<_>>();
}
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<SolverBoard> {
let mut result : Option<SolverBoard> = 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<SolverBoard> {
if !self.cache.contains(&board.arr) {
self.cache.insert(board.arr);
return Some(board.clone())
}
None
}
fn add_recursive(&mut self, board: SolverBoard) -> Option<SolverBoard> {
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<Playground> {
// 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<Action> {
if let SolverBoardItem::Recursive(_, actions) = self {
return actions.pop()
}
None
}
}
impl Iterator for SolverBoardIterator {
type Item = SolverBoard;
fn next(&mut self) -> Option<Self::Item> {
self.next_board()
}
}

@ -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!")
}
}

@ -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<Playground> {
fn solve(&self, _pg: &Board) -> Option<Board> {
// FIXME: Implement!
None
}
fn is_unique(&self, _ : &Board) -> bool {
// FIXME: Implement
false
}
}

@ -1,11 +1,12 @@
use crate::playground::Playground;
use crate::board::Board;
pub trait Solver {
fn solve(&self, _: &Playground) -> Option<Playground>;
fn solve(&self, _: &Board) -> Option<Board>;
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;
}
Loading…
Cancel
Save