You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
11 KiB

use std::collections::HashSet;
use crate::solver::{Solver, Generator, SodokuComplexity};
use crate::board::Board;
use crate::utils::*;
use std::rc::Rc;
use std::hash::{Hash, Hasher};
const NUM : usize = 3;
const VALUES : usize = NUM * NUM;
const CELLS : usize = VALUES * VALUES;
type TodoType<T> = IndexableVec<T, CELLS>;
// type TodoType<T> = HashSet<T>;
pub struct HechtSolver {
}
#[derive(Hash, Clone, Eq, PartialEq, Debug, Copy)]
struct Point {
pub x: usize,
pub y: usize,
pub s: usize,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum Action {
Trivial(Point, u16),
Logic(Point, u16),
Probe(Point, u16),
}
#[derive(Clone)]
struct SolverBoard {
arr: [[u16; VALUES]; VALUES],
todos: TodoType<Point>,
valid: bool,
}
enum SolverBoardItem {
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 {
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 {
pub fn new() -> HechtSolver {
return HechtSolver {};
}
pub fn test(&self, board: &Board) {
let count = SolverBoardIterator::new(board)
.filter(|x| x.is_solved())
.inspect(|_| println!("Solved Board detected"))
.count();
println!("Count: {}", count);
}
}
impl Solver for HechtSolver {
fn solve(&self, board: &Board) -> Option<Board> {
SolverBoardIterator::new(board)
.find(|x| x.is_solved())
.map(|x| x.to_board())
.flatten()
}
fn is_unique(&self, board: &Board) -> bool {
SolverBoardIterator::new(board)
.filter(|x| x.is_solved())
.nth(1).is_none()
}
}
impl Generator for HechtSolver {
fn generate(&self, _complexity:SodokuComplexity) -> Board {
let board = Board::new();
SolverBoardIterator::new(&board)
.filter_map(|x| x.to_board())
.filter(|board| self.is_unique(board))
.next()
.unwrap_or(board)
}
}
impl SolverBoard {
pub fn new() -> SolverBoard {
let points = [0usize..CELLS].into_iter().flatten()
.map(|idx| Point::from_index(idx))
.collect::<TodoType<_>>();
SolverBoard { arr: [[0x1FFu16; VALUES]; VALUES], todos: points, valid: true}
}
pub fn from(pg: &Board) -> SolverBoard {
let mut spg = SolverBoard::new();
for idx in 0usize..CELLS {
let p = Point::from_index(idx);
if let Some(value) = pg.get_value(p.x, p.y) {
spg.set_value(&p, value);
assert!(spg.valid);
}
}
spg.solve_logically();
spg
}
fn to_board(&self) -> Option<Board> {
let mut pg = Board::new();
for x in 0..VALUES {
for y in 0..VALUES {
let value = self.arr[y][x];
if value.count_ones() == 1 {
pg.set_value(x, y, (value.trailing_zeros() + 1) as u8);
}
}
}
Some(pg)
}
fn solve_logically(&mut self) {
while self.valid {
if let Some(action) = self.get_simple_action() {
self.apply(action);
} else {
break;
}
}
}
fn is_solved(&self) -> bool {
self.valid && self.todos.len() == 0
}
fn set_value(&mut self, p : &Point, value : u8) {
let bit_value = 1u16 << (value - 1);
self.set_bit_value(p, bit_value)
}
fn get_sector(index : usize) -> usize {
static SECTOR_ARRAY : [usize; CELLS] = [
0,0,0,1,1,1,2,2,2,
0,0,0,1,1,1,2,2,2,
0,0,0,1,1,1,2,2,2,
3,3,3,4,4,4,5,5,5,
3,3,3,4,4,4,5,5,5,
3,3,3,4,4,4,5,5,5,
6,6,6,7,7,7,8,8,8,
6,6,6,7,7,7,8,8,8,
6,6,6,7,7,7,8,8,8,
];
SECTOR_ARRAY[index]
}
#[inline]
fn get_y(index : usize) -> usize {
index / VALUES
}
#[inline]
fn get_x(index : usize) -> usize {
index % VALUES
}
fn set_bit_value(&mut self, p : &Point, bit_value : u16) {
assert!(bit_value.count_ones() == 1);
if !self.todos.remove(p) {
// Point was not in todo-list -> no further action required
return;
}
self.arr[p.y][p.x] = bit_value;
for idx in 0..CELLS {
let other = Point::from_index(idx);
if p != &other && (other.x == p.x || other.y == p.y || other.s == p.s) {
self.remove_value(other, bit_value)
}
}
}
fn remove_value(&mut self, p : Point, bit_value: u16) {
if (self.arr[p.y][p.x] & bit_value) == 0 {
return;
}
self.arr[p.y][p.x] &= !bit_value;
let point_value = self.arr[p.y][p.x];
match point_value.count_ones() {
1 => self.apply(Action::Trivial(p, point_value)),
0 => self.valid = false,
_ => {}
}
}
fn create_action(p : &Point, bit_value: u16) -> Option<Action> {
if bit_value.count_ones() != 1 {
return None;
}
Some(Action::Logic(p.clone(), bit_value))
}
fn apply(&mut self, action : Action) {
let (point, value) = action.get();
self.set_bit_value(point, value);
if let Action::Probe(_,_) = action {
self.solve_logically();
}
}
#[inline]
fn get_bitvalue_sequence() -> &'static[u16; VALUES] {
return &[0b0_0000_0001u16, // 1
0b0_0000_0010u16, // 2
0b0_0000_0100u16, // 3
0b0_0000_1000u16, // 4
0b0_0001_0000u16, // 5
0b0_0010_0000u16, // 6
0b0_0100_0000u16, // 7
0b0_1000_0000u16, // 8
0b1_0000_0000u16]; // 9
}
#[inline]
fn get_value(&self, p : &Point) -> u16 {
return self.arr[p.y][p.x];
}
fn get_simple_action(&self) -> Option<Action> {
// println!("Simple Action");
self.todos.iter()
.find_map(|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| {
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};
let sec_value = if lhs.s == rhs.s {value} else {0};
[row_value, col_value, sec_value]
} )
.reduce(|a,b| [a[0]|b[0], a[1]|b[1], a[2]|b[2]])
.iter()
.flatten()
.map(|value| value ^ own_value)
.filter(|value| value.count_ones() == 1)
.find_map(|value| SolverBoard::create_action(lhs, value))
} )
}
fn get_probe_actions(&self) -> Vec<Action> {
let result = self.todos.iter()
.min_by_key(|point| self.get_value(point).count_ones());
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 {
fn new(board : &Board) -> SolverBoardIterator {
SolverBoardIterator::from_board(SolverBoard::from(board))
}
fn from_board(board: SolverBoard) -> SolverBoardIterator {
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());
if let Some(board) = self.pop_board(item) {
return Some(board);
}
}
None
}
fn pop_board(&mut self, item : SolverBoardItem) -> Option<SolverBoard> {
match item {
SolverBoardItem::Initial(board) => {
self.handle_initial(board)
}
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))
}
None
}
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);
// println!(" {} --> {:?}", board.todos.len(), action);
board.apply(action);
if !board.valid || !self.cache.insert(board.arr) {
return None;
}
return self.handle_board(board_ref);
}
fn handle_board(&mut self, board: Rc<SolverBoard>)-> Option<SolverBoard> {
if !board.is_solved() {
let actions = board.get_probe_actions();
actions.into_iter()
.for_each(|action| self.stack.push(SolverBoardItem::Process(Rc::clone(&board), action)))
}
Some((*board).clone())
}
}
impl Iterator for SolverBoardIterator {
type Item = SolverBoard;
fn next(&mut self) -> Option<Self::Item> {
self.next_board()
}
}
impl Action {
fn get(&self) -> (&Point, u16) {
match self {
Action::Logic(point,value) | Action::Probe(point,value) | Action::Trivial(point,value) => return (point, *value)
}
}
}
impl Indexable for Point {
fn to_index(&self) -> usize {
self.y * VALUES + self.x
}
}
impl Point {
fn from_xy(x: usize, y: usize) -> Point {
Point{x, y, s: (y / NUM) * NUM + x / NUM}
}
fn from_index(idx: usize) -> Point {
Point{x: SolverBoard::get_x(idx), y: SolverBoard::get_y(idx), s: SolverBoard::get_sector(idx)}
}
}