Difficulty settings now look for a minimum number of complicated Solve Actions
This commit is contained in:
parent
fa370ed9a9
commit
b933f4c281
3 changed files with 156 additions and 47 deletions
|
@ -3,7 +3,7 @@ use rand::prelude::*;
|
|||
use sudoku_solver::grid::{Grid, CellValue};
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use sudoku_solver::solver::SolveController;
|
||||
use sudoku_solver::solver::{SolveController, SolveStatistics};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone)] // Needed for argparse
|
||||
|
@ -38,6 +38,18 @@ impl Difficulty {
|
|||
|
||||
controller
|
||||
}
|
||||
|
||||
fn meets_minimum_requirements(&self, solve_statistics: &SolveStatistics) -> bool {
|
||||
match self {
|
||||
Difficulty::Hard => {
|
||||
(solve_statistics.guesses > 0) && (solve_statistics.possibility_groups > 10) && (solve_statistics.useful_constraints > 10)
|
||||
}
|
||||
Difficulty::Medium => {
|
||||
(solve_statistics.possibility_groups > 10) && (solve_statistics.useful_constraints > 10)
|
||||
}
|
||||
Difficulty::Easy => {true} // easy has no minimum
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Difficulty { // Needed for argparse
|
||||
|
@ -92,6 +104,7 @@ fn main() {
|
|||
ap.parse_args_or_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
if debug {
|
||||
unsafe {
|
||||
sudoku_solver::grid::DEBUG = true;
|
||||
|
@ -99,6 +112,8 @@ fn main() {
|
|||
sudoku_solver::generator::DEBUG = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
if debug {
|
||||
println!("Using seed {}", seed);
|
||||
|
@ -111,25 +126,34 @@ fn main() {
|
|||
|
||||
let mut num_attempts = 0;
|
||||
|
||||
let grid = loop {
|
||||
let (grid, solve_statistics) = loop {
|
||||
if num_attempts >= max_attempts{
|
||||
println!("Unable to find a puzzle with only {} hints in {} attempts", max_hints, max_attempts);
|
||||
return;
|
||||
}
|
||||
|
||||
let (grid, num_hints) = sudoku_solver::generator::generate_grid(&mut rng, &solve_controller);
|
||||
let (grid, num_hints, solve_statistics) = sudoku_solver::generator::generate_grid(&mut rng, &solve_controller);
|
||||
num_attempts = num_attempts + 1;
|
||||
|
||||
if num_hints <= max_hints {
|
||||
if difficulty.meets_minimum_requirements(&solve_statistics) && num_hints <= max_hints {
|
||||
println!("{}", grid);
|
||||
println!("Puzzle has {} hints", num_hints);
|
||||
if num_attempts > 1 {
|
||||
println!("It took {} attempts to find this puzzle.", num_attempts);
|
||||
}
|
||||
break grid;
|
||||
break (grid, solve_statistics);
|
||||
}
|
||||
};
|
||||
|
||||
if debug {
|
||||
println!("Solving this puzzle involves roughly:");
|
||||
println!("\t{} SINGLE actions", solve_statistics.singles);
|
||||
println!("\t{} HIDDEN_SINGLE actions", solve_statistics.hidden_singles);
|
||||
println!("\t{} USEFUL_CONSTRAINT actions", solve_statistics.useful_constraints);
|
||||
println!("\t{} POSSIBILITY_GROUP actions", solve_statistics.possibility_groups);
|
||||
println!("\t{} GUESS actions", solve_statistics.guesses);
|
||||
}
|
||||
|
||||
match filename {
|
||||
Some(filename) => {
|
||||
// check if we save to a csv or a pdf
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::grid::{Cell, Grid, CellValue, Line};
|
||||
use crate::solver::{SolveStatus, SolveController, Uniqueness, evaluate_grid_with_solve_controller};
|
||||
use crate::solver::{SolveStatus, SolveController, Uniqueness, evaluate_grid_with_solve_controller, SolveStatistics};
|
||||
use std::rc::Rc;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
@ -108,7 +108,7 @@ impl Line {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_grid(rng: &mut ChaCha8Rng, solve_controller: &SolveController) -> (Grid, i32) {
|
||||
pub fn generate_grid(rng: &mut ChaCha8Rng, solve_controller: &SolveController) -> (Grid, i32, SolveStatistics) {
|
||||
|
||||
let mut grid = generate_completed_grid(rng);
|
||||
let mut num_hints = 81;
|
||||
|
@ -124,6 +124,8 @@ pub fn generate_grid(rng: &mut ChaCha8Rng, solve_controller: &SolveController) -
|
|||
// Need to randomly reorder non_empty_cells
|
||||
non_empty_cells.shuffle(rng);
|
||||
|
||||
let mut statistics_option = None;
|
||||
|
||||
for (_index, cell) in non_empty_cells.iter().enumerate() {
|
||||
let mut grid_clone = grid.clone();
|
||||
let cell_clone = grid_clone.get(cell.x, cell.y).unwrap();
|
||||
|
@ -132,7 +134,7 @@ pub fn generate_grid(rng: &mut ChaCha8Rng, solve_controller: &SolveController) -
|
|||
cell_clone.delete_value();
|
||||
|
||||
|
||||
let status = evaluate_grid_with_solve_controller(&mut grid_clone, solve_controller);
|
||||
let (status, statistics) = evaluate_grid_with_solve_controller(&mut grid_clone, solve_controller);
|
||||
match status {
|
||||
SolveStatus::Complete(uniqueness) => {
|
||||
let uniqueness = uniqueness.unwrap();
|
||||
|
@ -147,9 +149,10 @@ pub fn generate_grid(rng: &mut ChaCha8Rng, solve_controller: &SolveController) -
|
|||
SolveStatus::Unfinished => panic!("evaluate_grid_with_solve_controller should never return UNFINISHED"),
|
||||
SolveStatus::Invalid => panic!("Removing constraints should not have set the # of solutions to zero")
|
||||
}
|
||||
statistics_option = Some(statistics);
|
||||
}
|
||||
|
||||
return (grid, num_hints);
|
||||
return (grid, num_hints, statistics_option.unwrap());
|
||||
|
||||
}
|
||||
|
||||
|
@ -179,7 +182,7 @@ fn generate_completed_grid(rng: &mut ChaCha8Rng) -> Grid {
|
|||
}
|
||||
}
|
||||
|
||||
let status = evaluate_grid_with_solve_controller(&grid, &solve_controller);
|
||||
let (status, _statistics) = evaluate_grid_with_solve_controller(&grid, &solve_controller);
|
||||
match status {
|
||||
SolveStatus::Complete(uniqueness) => {
|
||||
let uniqueness = uniqueness.unwrap();
|
||||
|
@ -213,7 +216,7 @@ fn generate_completed_grid(rng: &mut ChaCha8Rng) -> Grid {
|
|||
|
||||
cell.set(*digit);
|
||||
|
||||
let status = evaluate_grid_with_solve_controller(&mut grid_clone, &solve_controller);
|
||||
let (status, _statistics) = evaluate_grid_with_solve_controller(&mut grid_clone, &solve_controller);
|
||||
match status {
|
||||
SolveStatus::Complete(uniqueness) => {
|
||||
let uniqueness = uniqueness.unwrap();
|
||||
|
@ -247,7 +250,7 @@ fn generate_completed_grid(rng: &mut ChaCha8Rng) -> Grid {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::grid::*;
|
||||
use crate::solver::{solve_grid_with_solve_controller, SolveController, Uniqueness, SolveStatus};
|
||||
use crate::solver::{solve_grid_with_solve_controller, SolveController, Uniqueness, SolveStatus, SolveStatistics};
|
||||
use crate::generator::generate_grid;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
|
@ -297,7 +300,7 @@ mod tests {
|
|||
find_possibility_groups: true,
|
||||
search_useful_constraint: true,
|
||||
make_guesses: true
|
||||
});
|
||||
}, &mut SolveStatistics::new());
|
||||
|
||||
assert_eq!(status, SolveStatus::Complete(Some(Uniqueness::NotUnique)));
|
||||
|
||||
|
@ -316,7 +319,7 @@ mod tests {
|
|||
};
|
||||
|
||||
// Note that the puzzle itself doesn't matter
|
||||
let (grid, _num_hints) = generate_grid(&mut ChaCha8Rng::seed_from_u64(123), &solve_controller);
|
||||
let (grid, _num_hints, _statistics) = generate_grid(&mut ChaCha8Rng::seed_from_u64(123), &solve_controller);
|
||||
|
||||
let mut observed_empty_cell = false;
|
||||
'outer : for x in 0..9 {
|
||||
|
|
148
src/solver.rs
148
src/solver.rs
|
@ -17,6 +17,15 @@ pub enum SolveStatus {
|
|||
Invalid
|
||||
}
|
||||
|
||||
|
||||
enum SolveAction{
|
||||
Single,
|
||||
HiddenSingle,
|
||||
PossibilityGroup,
|
||||
UsefulConstraints,
|
||||
Guess
|
||||
}
|
||||
|
||||
impl SolveStatus {
|
||||
|
||||
fn increment(self, additional_status : SolveStatus) -> SolveStatus {
|
||||
|
@ -64,9 +73,7 @@ impl SolveController {
|
|||
}
|
||||
|
||||
fn search_hidden_singles(&self) -> bool {
|
||||
// search_hidden_singles is a special case of find_possibility_groups, so if find_possibility_groups
|
||||
// is enabled then it's a waste of resources to keep this on
|
||||
self.search_hidden_singles && !self.find_possibility_groups
|
||||
self.search_hidden_singles
|
||||
}
|
||||
|
||||
fn find_possibility_groups(&self) -> bool {
|
||||
|
@ -82,6 +89,40 @@ impl SolveController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Tracks when we relied on a method to make progress. We'll consider 'relied on' to mean that the method make at least
|
||||
one change to the line it was originally called on, whether that be setting a value or adjusting the possibilities in a cell.
|
||||
*/
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SolveStatistics {
|
||||
pub singles: u32,
|
||||
pub hidden_singles: u32,
|
||||
pub possibility_groups: u32,
|
||||
pub useful_constraints: u32,
|
||||
pub guesses: u32
|
||||
}
|
||||
|
||||
impl SolveStatistics {
|
||||
pub(crate) fn new() -> SolveStatistics {
|
||||
SolveStatistics{
|
||||
singles: 0,
|
||||
hidden_singles: 0,
|
||||
possibility_groups: 0,
|
||||
useful_constraints: 0,
|
||||
guesses: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&mut self, action: &SolveAction) {
|
||||
match action {
|
||||
SolveAction::Single => {self.singles = self.singles + 1}
|
||||
SolveAction::HiddenSingle => {self.hidden_singles = self.hidden_singles + 1}
|
||||
SolveAction::PossibilityGroup => {self.possibility_groups = self.possibility_groups + 1}
|
||||
SolveAction::UsefulConstraints => {self.useful_constraints = self.useful_constraints + 1}
|
||||
SolveAction::Guess => {self.guesses = self.guesses + 1}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_smallest_cell(grid: &Grid) -> Option<Rc<Cell>>{
|
||||
// Find a cell of smallest size (in terms of possibilities) and make a guess
|
||||
|
@ -154,17 +195,17 @@ mod process_possibility_groups {
|
|||
|
||||
// See if there's a set of cells with possibilities that exclude those possibilities from other cells.
|
||||
// Runs recursively on each group to identify all groups in case there's more than 2.
|
||||
pub fn identify_and_process_possibility_groups(line: &Line){
|
||||
pub fn identify_and_process_possibility_groups(line: &Line) -> bool{
|
||||
unsafe {
|
||||
if super::DEBUG {
|
||||
println!("Looking for possibility groups on line {:?} {}", line.line_type, line.index);
|
||||
}
|
||||
}
|
||||
|
||||
bisect_possibility_groups(line, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
bisect_possibility_groups(line, vec![0, 1, 2, 3, 4, 5, 6, 7, 8])
|
||||
}
|
||||
|
||||
fn bisect_possibility_groups(line: &Line, cells_of_interest: Vec<usize>){
|
||||
fn bisect_possibility_groups(line: &Line, cells_of_interest: Vec<usize>) -> bool{
|
||||
|
||||
/*
|
||||
Algorithm -
|
||||
|
@ -180,6 +221,8 @@ mod process_possibility_groups {
|
|||
let mut out_group_indices = Vec::new();
|
||||
let mut run_recursion = false;
|
||||
|
||||
let mut made_change = false;
|
||||
|
||||
{
|
||||
// <Setup>
|
||||
let mut count = 0;
|
||||
|
@ -219,7 +262,7 @@ mod process_possibility_groups {
|
|||
|
||||
// No point in continuing.
|
||||
if faux_line.num_out_group() <= 2 {
|
||||
return;
|
||||
return made_change;
|
||||
}
|
||||
|
||||
// A kind of do-while loop
|
||||
|
@ -301,6 +344,7 @@ mod process_possibility_groups {
|
|||
}
|
||||
|
||||
if possibilities.len() < starting_possibility_size { // We have a change to make
|
||||
made_change = true;
|
||||
let new_value = {
|
||||
if possibilities.len() == 1 {
|
||||
CellValue::Fixed(possibilities.pop().unwrap())
|
||||
|
@ -331,32 +375,39 @@ mod process_possibility_groups {
|
|||
bisect_possibility_groups(line, in_group_indices);
|
||||
bisect_possibility_groups(line, out_group_indices);
|
||||
}
|
||||
|
||||
return made_change;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for a cell with only one possibility so that we can set it to FIXED
|
||||
fn search_single_possibility(line: &Line){
|
||||
fn search_single_possibility(line: &Line) -> bool{
|
||||
unsafe {
|
||||
if DEBUG {
|
||||
println!("search_single_possibility on line {:?} {}", line.line_type, line.index);
|
||||
}
|
||||
}
|
||||
|
||||
let mut made_change = false;
|
||||
|
||||
for (_index, cell) in line.vec.iter().enumerate(){
|
||||
match cell.get_value_possibilities(){
|
||||
Some(x) => {
|
||||
if x.len() == 1 {
|
||||
let new_value = CellValue::Fixed(x.first().unwrap().clone());
|
||||
cell.set_value(new_value);
|
||||
made_change = true;
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
return made_change;
|
||||
}
|
||||
|
||||
// Count up how many times each possibility occurs in the Line. If it only occurs once, that's a hidden single that we can set
|
||||
fn search_hidden_single(line: &Line){
|
||||
fn search_hidden_single(line: &Line) -> bool{
|
||||
enum Count {
|
||||
None,
|
||||
One(Rc<Cell>),
|
||||
|
@ -373,6 +424,8 @@ fn search_hidden_single(line: &Line){
|
|||
}
|
||||
}
|
||||
|
||||
let mut made_change = false;
|
||||
|
||||
let mut counts = [Count::None, Count::None, Count::None, Count::None, Count::None, Count::None, Count::None, Count::None, Count::None];
|
||||
|
||||
for (_index, cell) in line.vec.iter().enumerate() {
|
||||
|
@ -393,11 +446,13 @@ fn search_hidden_single(line: &Line){
|
|||
match count {
|
||||
Count::One(cell) => {
|
||||
cell.set((digit + 1) as u8);
|
||||
made_change = true;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return made_change;
|
||||
}
|
||||
|
||||
mod search_useful_constraint{
|
||||
|
@ -424,13 +479,15 @@ mod search_useful_constraint{
|
|||
// I.e. If possibility '1' only occurs in the first row for section 0, then you can remove that possibility
|
||||
// from row 0 across the other sections. Conversely, if the possibility only occurs in the first section
|
||||
// for row 0, then you can remove the possibility from the rest of section 0.
|
||||
pub fn search_useful_constraint(grid: &Grid, line: &Line){
|
||||
pub fn search_useful_constraint(grid: &Grid, line: &Line) -> bool{
|
||||
unsafe {
|
||||
if super::DEBUG {
|
||||
println!("Searching for a useful constraint on line {:?} {}", line.line_type, line.index);
|
||||
}
|
||||
}
|
||||
|
||||
let mut made_change = false;
|
||||
|
||||
let (check_row, check_column, check_section) = match line.line_type {
|
||||
LineType::Row => {(false, false, true)},
|
||||
LineType::Column => {(false, false, true)},
|
||||
|
@ -474,29 +531,35 @@ mod search_useful_constraint{
|
|||
// Check each line and see if we can determine anything
|
||||
match rows {
|
||||
PossibilityLines::Unique(index) => {
|
||||
remove_possibilities_line(grid.rows.get(index).unwrap(), possibility, &line.line_type, line.index);
|
||||
made_change = made_change |
|
||||
remove_possibilities_line(grid.rows.get(index).unwrap(), possibility, &line.line_type, line.index);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
match columns {
|
||||
PossibilityLines::Unique(index) => {
|
||||
remove_possibilities_line(grid.columns.get(index).unwrap(), possibility, &line.line_type, line.index);
|
||||
made_change = made_change |
|
||||
remove_possibilities_line(grid.columns.get(index).unwrap(), possibility, &line.line_type, line.index);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
match sections {
|
||||
PossibilityLines::Unique(index) => {
|
||||
remove_possibilities_line(grid.sections.get(index).unwrap(), possibility, &line.line_type, line.index);
|
||||
made_change = made_change |
|
||||
remove_possibilities_line(grid.sections.get(index).unwrap(), possibility, &line.line_type, line.index);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
return made_change;
|
||||
|
||||
}
|
||||
|
||||
// initial_line_type and initial_line_index are to identify the cells that should NOT have their possibilities removed
|
||||
fn remove_possibilities_line(line: &Rc<RefCell<Line>>, digit_to_remove: u8, initial_line_type: &LineType, initial_line_index: usize) {
|
||||
fn remove_possibilities_line(line: &Rc<RefCell<Line>>, digit_to_remove: u8, initial_line_type: &LineType, initial_line_index: usize) -> bool {
|
||||
let line = &*(&**line).borrow();
|
||||
let mut made_change = false;
|
||||
|
||||
for (_index, cell) in line.vec.iter().enumerate() {
|
||||
let new_value = {
|
||||
|
@ -538,8 +601,11 @@ mod search_useful_constraint{
|
|||
};
|
||||
|
||||
cell.set_value(new_value);
|
||||
made_change = true;
|
||||
|
||||
}
|
||||
|
||||
return made_change;
|
||||
}
|
||||
|
||||
// We detected a useful constraint
|
||||
|
@ -563,7 +629,7 @@ mod search_useful_constraint{
|
|||
}
|
||||
|
||||
|
||||
fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController){
|
||||
fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics){
|
||||
unsafe {
|
||||
if DEBUG {
|
||||
println!("Solving {:?} {}", line.line_type, line.index);
|
||||
|
@ -578,7 +644,9 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController){
|
|||
println!("Searching for singles on line {:?} of {}\n{}", line.line_type, line.index, grid);
|
||||
}
|
||||
}
|
||||
search_single_possibility(line);
|
||||
if search_single_possibility(line) {
|
||||
solve_statistics.increment(&SolveAction::Single);
|
||||
}
|
||||
}
|
||||
|
||||
if solve_controller.search_hidden_singles() {
|
||||
|
@ -587,7 +655,9 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController){
|
|||
println!("Searching for hidden singles on line {:?} of {}\n{}", line.line_type, line.index, grid);
|
||||
}
|
||||
}
|
||||
search_hidden_single(line);
|
||||
if search_hidden_single(line) {
|
||||
solve_statistics.increment(&SolveAction::HiddenSingle);
|
||||
}
|
||||
}
|
||||
|
||||
if solve_controller.find_possibility_groups() {
|
||||
|
@ -596,7 +666,9 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController){
|
|||
println!("Searching for possibility groups on line {:?} of {}\n{}", line.line_type, line.index, grid);
|
||||
}
|
||||
}
|
||||
process_possibility_groups::identify_and_process_possibility_groups(line);
|
||||
if process_possibility_groups::identify_and_process_possibility_groups(line) {
|
||||
solve_statistics.increment(&SolveAction::PossibilityGroup);
|
||||
}
|
||||
}
|
||||
|
||||
if solve_controller.search_useful_constraint() {
|
||||
|
@ -605,12 +677,14 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController){
|
|||
println!("Searching for useful constraints on line {:?} of {}\n{}", line.line_type, line.index, grid);
|
||||
}
|
||||
}
|
||||
search_useful_constraint::search_useful_constraint(grid, line);
|
||||
if search_useful_constraint::search_useful_constraint(grid, line) {
|
||||
solve_statistics.increment(&SolveAction::UsefulConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn solve_grid(grid: &mut Grid) -> SolveStatus {
|
||||
pub fn solve_grid(grid: &mut Grid) -> (SolveStatus, SolveStatistics) {
|
||||
// By default we enable everything
|
||||
let solve_controller = SolveController {
|
||||
determine_uniqueness: true,
|
||||
|
@ -621,11 +695,13 @@ pub fn solve_grid(grid: &mut Grid) -> SolveStatus {
|
|||
make_guesses: true
|
||||
};
|
||||
|
||||
solve_grid_with_solve_controller(grid, &solve_controller)
|
||||
let mut solve_statistics = SolveStatistics::new();
|
||||
let solve_status = solve_grid_with_solve_controller(grid, &solve_controller, &mut solve_statistics);
|
||||
|
||||
return (solve_status, solve_statistics);
|
||||
}
|
||||
|
||||
pub fn solve_grid_with_solve_controller(grid: &mut Grid, solve_controller: &SolveController) -> SolveStatus{
|
||||
pub fn solve_grid_with_solve_controller(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{
|
||||
// Code is kind of messy so here it goes - solve_grid first tries to solve without any guesses
|
||||
// If that's not enough and a guess is required, then solve_grid_guess is called
|
||||
// solve_grid_guess runs through all the possibilities for the smallest cell, trying to solve them
|
||||
|
@ -633,11 +709,11 @@ pub fn solve_grid_with_solve_controller(grid: &mut Grid, solve_controller: &Solv
|
|||
// solve_grid_no_guess tries to solve without any guesses.
|
||||
// Of course this is if the solve_controller lets everything be used for solving it
|
||||
|
||||
let mut status = solve_grid_no_guess(grid, solve_controller);
|
||||
let mut status = solve_grid_no_guess(grid, solve_controller, solve_statistics);
|
||||
status = match status {
|
||||
SolveStatus::Unfinished => {
|
||||
if solve_controller.make_guesses() {
|
||||
solve_grid_guess(grid, solve_controller)
|
||||
solve_grid_guess(grid, solve_controller, solve_statistics)
|
||||
} else {
|
||||
SolveStatus::Complete(Some(Uniqueness::NotUnique)) // solve_grid_no_guess couldn't finish and we can't make guesses, so it's 'not unique' in the sense that we need more guesses
|
||||
}
|
||||
|
@ -649,12 +725,16 @@ pub fn solve_grid_with_solve_controller(grid: &mut Grid, solve_controller: &Solv
|
|||
}
|
||||
|
||||
// Similar to solve_grid_with_solve_controller except that we don't modify the input Grid; we only determine SolveStatus
|
||||
pub fn evaluate_grid_with_solve_controller(grid: &Grid, solve_controller: &SolveController) -> SolveStatus{
|
||||
pub fn evaluate_grid_with_solve_controller(grid: &Grid, solve_controller: &SolveController) -> (SolveStatus, SolveStatistics){
|
||||
let mut mut_grid = grid.clone();
|
||||
return solve_grid_with_solve_controller(&mut mut_grid, solve_controller);
|
||||
let mut solve_statistics = SolveStatistics::new();
|
||||
|
||||
let solve_status = solve_grid_with_solve_controller(&mut mut_grid, solve_controller, &mut solve_statistics);
|
||||
|
||||
return (solve_status, solve_statistics);
|
||||
}
|
||||
|
||||
pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController) -> SolveStatus{
|
||||
pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{
|
||||
|
||||
loop {
|
||||
let mut ran_something = false;
|
||||
|
@ -662,7 +742,7 @@ pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController)
|
|||
//println!("Processing row {}", _index);
|
||||
let line_ref = &*(&**line_ref).borrow();
|
||||
if line_ref.do_update() {
|
||||
solve_line(&grid, line_ref, solve_controller);
|
||||
solve_line(&grid, line_ref, solve_controller, solve_statistics);
|
||||
ran_something = true;
|
||||
}
|
||||
}
|
||||
|
@ -670,7 +750,7 @@ pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController)
|
|||
//println!("Processing column {}", _index);
|
||||
let line_ref = &*(&**line_ref).borrow();
|
||||
if line_ref.do_update() {
|
||||
solve_line(&grid, line_ref, solve_controller);
|
||||
solve_line(&grid, line_ref, solve_controller, solve_statistics);
|
||||
ran_something = true;
|
||||
}
|
||||
}
|
||||
|
@ -678,7 +758,7 @@ pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController)
|
|||
//println!("Processing section {}", _index);
|
||||
let line_ref = &*(&**line_ref).borrow();
|
||||
if line_ref.do_update() {
|
||||
solve_line(&grid, line_ref, solve_controller);
|
||||
solve_line(&grid, line_ref, solve_controller, solve_statistics);
|
||||
ran_something = true;
|
||||
}
|
||||
}
|
||||
|
@ -717,7 +797,9 @@ pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController)
|
|||
|
||||
}
|
||||
|
||||
fn solve_grid_guess(grid: &mut Grid, solve_controller: &SolveController) -> SolveStatus{
|
||||
fn solve_grid_guess(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{
|
||||
solve_statistics.increment(&SolveAction::Guess);
|
||||
|
||||
let smallest_cell = find_smallest_cell(grid);
|
||||
let smallest_cell = match smallest_cell {
|
||||
Some(cell) => cell,
|
||||
|
@ -733,7 +815,7 @@ fn solve_grid_guess(grid: &mut Grid, solve_controller: &SolveController) -> Solv
|
|||
|
||||
let mut grid_copy = grid.clone();
|
||||
grid_copy.get(smallest_cell.x, smallest_cell.y).unwrap().set(digit);
|
||||
let status = solve_grid_with_solve_controller(&mut grid_copy, solve_controller);
|
||||
let status = solve_grid_with_solve_controller(&mut grid_copy, solve_controller, solve_statistics);
|
||||
|
||||
// Keep a copy of grid_copy in case we later mutate grid with it
|
||||
match status {
|
||||
|
|
Loading…
Reference in a new issue