|
|
|
@ -1,44 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use crate::solver::Solver;
|
|
|
|
|
use crate::solver::{Solver, Generator, SodokuComplexity};
|
|
|
|
|
use crate::board::Board;
|
|
|
|
|
use crate::utils::*;
|
|
|
|
|
use std::hash::{Hash, Hasher};
|
|
|
|
|
use std::ops::ControlFlow;
|
|
|
|
|
|
|
|
|
|
static CELLS : usize = 81;
|
|
|
|
|
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)]
|
|
|
|
|
#[derive(Hash, Clone, Eq, PartialEq, Debug, Copy)]
|
|
|
|
|
struct Point {
|
|
|
|
|
pub x: usize,
|
|
|
|
|
pub y: usize,
|
|
|
|
|
pub s: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct Action {
|
|
|
|
|
pub p : Point,
|
|
|
|
|
pub value: u16
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
enum Action {
|
|
|
|
|
Trivial(Point, u16),
|
|
|
|
|
Logic(Point, u16),
|
|
|
|
|
Probe(Point, u16),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Eq)]
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
struct SolverBoard {
|
|
|
|
|
arr: [[u16; 9]; 9],
|
|
|
|
|
todos: HashSet<Point>,
|
|
|
|
|
arr: [[u16; VALUES]; VALUES],
|
|
|
|
|
todos: TodoType<Point>,
|
|
|
|
|
valid: bool,
|
|
|
|
|
audit: Vec<Action>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum SolverBoardItem {
|
|
|
|
|
Recursive(SolverBoard, Vec<Action>),
|
|
|
|
|
Initial(SolverBoard),
|
|
|
|
|
Recursive(Vec<Action>),
|
|
|
|
|
Initial,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SolverBoardIterator {
|
|
|
|
|
cache: HashSet<[[u16; 9]; 9]>, // to avoid processing the same board twice
|
|
|
|
|
cache: HashSet<[[u16; VALUES]; VALUES]>, // to avoid processing the same board twice
|
|
|
|
|
board: SolverBoard,
|
|
|
|
|
stack: Vec<SolverBoardItem>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -58,52 +67,79 @@ impl HechtSolver {
|
|
|
|
|
pub fn new() -> HechtSolver {
|
|
|
|
|
return HechtSolver {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn test(&self, board: &Board) -> bool {
|
|
|
|
|
let mut invalid_counter = 0;
|
|
|
|
|
let mut unsolved_counter = 0;
|
|
|
|
|
let mut solved_counter = 0;
|
|
|
|
|
|
|
|
|
|
SolverBoardIterator::new(board)
|
|
|
|
|
.inspect(|_| invalid_counter += 1)
|
|
|
|
|
.filter(|x| x.valid)
|
|
|
|
|
.inspect(|_| unsolved_counter += 1)
|
|
|
|
|
.filter(|x| x.is_solved())
|
|
|
|
|
.inspect(|_| solved_counter += 1)
|
|
|
|
|
.inspect(|x| x.to_board().unwrap().print() )
|
|
|
|
|
.nth(1024).and_then(|_| {println!("I: {}, U: {}, S: {}", invalid_counter, unsolved_counter, solved_counter); Some(true)})
|
|
|
|
|
.is_some()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Solver for HechtSolver {
|
|
|
|
|
fn solve(&self, pg: &Board) -> Option<Board> {
|
|
|
|
|
SolverBoardIterator::new(pg)
|
|
|
|
|
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, pg: &Board) -> bool {
|
|
|
|
|
let first_solution = SolverBoard::new();
|
|
|
|
|
|
|
|
|
|
SolverBoardIterator::new(pg)
|
|
|
|
|
fn is_unique(&self, board: &Board) -> bool {
|
|
|
|
|
SolverBoardIterator::new(board)
|
|
|
|
|
.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()
|
|
|
|
|
.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| [idx%9, idx/9])
|
|
|
|
|
.map(|p| Point{x: p[0], y:p[1], s: p[1] / 3 * 3 + p[0] / 3})
|
|
|
|
|
.collect::<HashSet<_>>();
|
|
|
|
|
.map(|idx| Point::from_index(idx))
|
|
|
|
|
.collect::<TodoType<_>>();
|
|
|
|
|
|
|
|
|
|
SolverBoard { arr: [[0x1FFu16; 9]; 9], todos: points, valid: true}
|
|
|
|
|
SolverBoard { arr: [[0x1FFu16; VALUES]; VALUES], todos: points, valid: true, audit: Vec::new()}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from(pg: &Board) -> SolverBoard {
|
|
|
|
|
let mut spg = SolverBoard::new();
|
|
|
|
|
|
|
|
|
|
for p in spg.todos.clone().iter() {
|
|
|
|
|
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();
|
|
|
|
|
assert!(spg.valid);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
spg
|
|
|
|
|
}
|
|
|
|
@ -111,8 +147,8 @@ impl SolverBoard {
|
|
|
|
|
fn to_board(&self) -> Option<Board> {
|
|
|
|
|
let mut pg = Board::new();
|
|
|
|
|
|
|
|
|
|
for x in 0..9 {
|
|
|
|
|
for y in 0..9 {
|
|
|
|
|
for x in 0..VALUES {
|
|
|
|
|
for y in 0..VALUES {
|
|
|
|
|
let value = self.arr[y][x];
|
|
|
|
|
if value.count_ones() != 1 {
|
|
|
|
|
return None
|
|
|
|
@ -128,7 +164,7 @@ impl SolverBoard {
|
|
|
|
|
fn solve_logically(&mut self) {
|
|
|
|
|
while self.valid {
|
|
|
|
|
if let Some(action) = self.get_simple_action() {
|
|
|
|
|
self.set_bit_value(&action.p, action.value);
|
|
|
|
|
self.apply(&action);
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -144,7 +180,33 @@ impl SolverBoard {
|
|
|
|
|
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;
|
|
|
|
@ -152,28 +214,36 @@ impl SolverBoard {
|
|
|
|
|
|
|
|
|
|
self.arr[p.y][p.x] = bit_value;
|
|
|
|
|
|
|
|
|
|
self.todos.iter()
|
|
|
|
|
// Filter positions that do not have the right coordinates
|
|
|
|
|
.filter(|&pos| pos.x == p.x || pos.y == p.y || pos.s == p.s)
|
|
|
|
|
// Filter positions that were known not to hold the value
|
|
|
|
|
.filter(|&pos| (self.arr[pos.y][pos.x] & bit_value) != 0)
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<Vec::<_>>()
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|pos| self.remove_value(&pos, bit_value));
|
|
|
|
|
// self.todos.iter()
|
|
|
|
|
// // Filter positions that do not have the right coordinates
|
|
|
|
|
// .filter(|&pos| pos.x == p.x || pos.y == p.y || pos.s == p.s)
|
|
|
|
|
// // Filter positions that were known not to hold the value
|
|
|
|
|
// .filter(|&pos| (self.arr[pos.y][pos.x] & bit_value) != 0)
|
|
|
|
|
// .cloned()
|
|
|
|
|
// .collect::<Vec::<_>>()
|
|
|
|
|
// .iter()
|
|
|
|
|
// .for_each(|pos| self.remove_value(&pos, 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.get_value(p) & bit_value) == 0 {
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
match point_value.count_ones() {
|
|
|
|
|
1 => self.apply(&Action::Trivial(p, point_value)),
|
|
|
|
|
0 => self.valid = false,
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -181,16 +251,23 @@ impl SolverBoard {
|
|
|
|
|
if bit_value.count_ones() != 1 {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
Some(Action{p : p.clone(), value : bit_value})
|
|
|
|
|
Some(Action::Logic(p.clone(), bit_value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply(&mut self, action : &Action) {
|
|
|
|
|
self.set_bit_value(&action.p, action.value);
|
|
|
|
|
self.solve_logically();
|
|
|
|
|
let (point, value) = action.get();
|
|
|
|
|
self.set_bit_value(point, value);
|
|
|
|
|
|
|
|
|
|
if let Action::Probe(_,_) = action {
|
|
|
|
|
self.solve_logically();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.audit.push(action.clone());
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn get_bitvalue_sequence() -> &'static[u16; 9] {
|
|
|
|
|
fn get_bitvalue_sequence() -> &'static[u16; VALUES] {
|
|
|
|
|
return &[0b0_0000_0001u16, // 1
|
|
|
|
|
0b0_0000_0010u16, // 2
|
|
|
|
|
0b0_0000_0100u16, // 3
|
|
|
|
@ -236,16 +313,16 @@ impl SolverBoard {
|
|
|
|
|
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)})
|
|
|
|
|
.map(|point| (point, 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());
|
|
|
|
|
vec.sort_unstable_by_key(|action| CELLS as u32 - action.1.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})
|
|
|
|
|
.filter(|&value| (action.1 & value) != 0)
|
|
|
|
|
.map(|&value| Action::Probe(action.0.clone(), value))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
)
|
|
|
|
|
.flatten()
|
|
|
|
@ -262,70 +339,96 @@ impl SolverBoardIterator {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn from_board(board: SolverBoard) -> SolverBoardIterator {
|
|
|
|
|
SolverBoardIterator{cache: HashSet::with_capacity(2048), stack: vec![SolverBoardItem::Initial(board)]}
|
|
|
|
|
SolverBoardIterator{cache: HashSet::with_capacity(128), board, stack: vec![SolverBoardItem::Initial]}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
let mut result;
|
|
|
|
|
while let Some(item) = self.stack.pop() {
|
|
|
|
|
if let SolverBoardItem::Recursive(actions) = item {
|
|
|
|
|
result = self.push_actions(&actions);
|
|
|
|
|
} else {
|
|
|
|
|
// initial
|
|
|
|
|
result = self.push_actions(&Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if result.is_some() {
|
|
|
|
|
return result
|
|
|
|
|
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())
|
|
|
|
|
fn push_actions(&mut self, actions: &Vec<Action>) -> Option<SolverBoard> {
|
|
|
|
|
if self.already_processed(actions) {
|
|
|
|
|
println!("Early hit! Stack: {}, Cache: {}", self.stack.len(), self.cache.len());
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
let mut new_board = self.board.clone();
|
|
|
|
|
|
|
|
|
|
let mut iter = actions.iter().peekable();
|
|
|
|
|
let mut arr_backup : Option<[[u16; VALUES]; VALUES]>= None;
|
|
|
|
|
while let Some(action) = iter.next() {
|
|
|
|
|
if iter.peek().is_some() {
|
|
|
|
|
new_board.apply(&action);
|
|
|
|
|
} else {
|
|
|
|
|
arr_backup = Some(new_board.arr);
|
|
|
|
|
new_board.apply(&action);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !new_board.valid {
|
|
|
|
|
if let Some(mut arr) = arr_backup {
|
|
|
|
|
if let Some(action) = actions.last() {
|
|
|
|
|
let (point, value) = action.get();
|
|
|
|
|
arr[point.y][point.x] = value;
|
|
|
|
|
self.insert_to_cache(arr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.insert_to_cache(new_board.arr) {
|
|
|
|
|
if !new_board.is_solved() {
|
|
|
|
|
let mut actions_copy = actions.to_vec();
|
|
|
|
|
for action in new_board.get_all_actions() {
|
|
|
|
|
actions_copy.push(action);
|
|
|
|
|
self.stack.push(SolverBoardItem::Recursive(actions_copy.to_vec()));
|
|
|
|
|
actions_copy.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Some(new_board);
|
|
|
|
|
} else {
|
|
|
|
|
println!("Late hit! Stack: {}, Cache: {}", self.stack.len(), self.cache.len())
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
/**
|
|
|
|
|
* Check if we can find a board in the cache, where all actions in the
|
|
|
|
|
* vector have been applied.
|
|
|
|
|
*/
|
|
|
|
|
fn already_processed(&mut self, actions : &Vec<Action>) -> bool {
|
|
|
|
|
for board in self.cache.iter() {
|
|
|
|
|
let applied = actions.iter()
|
|
|
|
|
.map(|a| a.get())
|
|
|
|
|
.all(|a| board[a.0.y][a.0.x] == a.1);
|
|
|
|
|
if applied {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pop_action(&mut self) -> Option<Action> {
|
|
|
|
|
if let SolverBoardItem::Recursive(_, actions) = self {
|
|
|
|
|
return actions.pop()
|
|
|
|
|
fn insert_to_cache(&mut self, mut array : [[u16; VALUES]; VALUES]) -> bool {
|
|
|
|
|
for x in array.iter_mut()
|
|
|
|
|
.flatten()
|
|
|
|
|
.filter(|value| value.count_ones() != 1) {
|
|
|
|
|
*x = 0;
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
self.cache.insert(array)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -335,5 +438,29 @@ impl Iterator for SolverBoardIterator {
|
|
|
|
|
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)}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|