Add some documentation; still needs a lot of work

This commit is contained in:
Joel Therrien 2020-09-26 15:49:22 -07:00
parent 12a3ea9eab
commit 235522224b
3 changed files with 180 additions and 100 deletions

View file

@ -1,4 +1,4 @@
use crate::grid::{Cell, Grid, CellValue, Line}; use crate::grid::{Cell, Grid, CellValue, Section};
use crate::solver::{SolveStatus, SolveController, Uniqueness, evaluate_grid_with_solve_controller, SolveStatistics}; use crate::solver::{SolveStatus, SolveController, Uniqueness, evaluate_grid_with_solve_controller, SolveStatistics};
use std::rc::Rc; use std::rc::Rc;
use rand::prelude::*; use rand::prelude::*;
@ -61,7 +61,7 @@ impl Cell {
fn calculate_possibilities(&self) -> Vec<u8> { fn calculate_possibilities(&self) -> Vec<u8> {
// Need to calculate possibilities for this cell // Need to calculate possibilities for this cell
let mut possibilities = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut possibilities = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
fn eliminate_possibilities(possibilities: &mut Vec<u8>, line: &Line, cell: &Cell){ fn eliminate_possibilities(possibilities: &mut Vec<u8>, line: &Section, cell: &Cell){
for (_index, other) in line.vec.iter().enumerate(){ for (_index, other) in line.vec.iter().enumerate(){
if other.x != cell.x || other.y != cell.y { if other.x != cell.x || other.y != cell.y {
let value = &*other.value.borrow(); let value = &*other.value.borrow();
@ -89,7 +89,7 @@ impl Cell {
} }
} }
impl Line { impl Section {
fn recalculate_and_set_possibilities(&self) { fn recalculate_and_set_possibilities(&self) {
for (_index, cell) in self.vec.iter().enumerate() { for (_index, cell) in self.vec.iter().enumerate() {
let cell = &**cell; let cell = &**cell;

View file

@ -10,16 +10,38 @@ pub enum CellValue {
Unknown(Vec<u8>) Unknown(Vec<u8>)
} }
/// A representation of a single cell in a Sudoku grid. Don't make this directly; make a Grid.
pub struct Cell { pub struct Cell {
pub x: usize, pub x: usize,
pub y: usize, pub y: usize,
pub value: RefCell<CellValue>, pub value: RefCell<CellValue>,
pub row: Weak<RefCell<Line>>, pub row: Weak<RefCell<Section>>,
pub column: Weak<RefCell<Line>>, pub column: Weak<RefCell<Section>>,
pub section: Weak<RefCell<Line>>, pub section: Weak<RefCell<Section>>,
} }
impl Cell { impl Cell {
/// Set the `Cell`'s value to be a fixed digit. This method also removes the digit from any
/// affected cells in the same row, column, or square.
///
/// # Examples
///
/// ```
/// use sudoku_solver::grid::{Grid, CellValue};
/// let grid = Grid::new();
///
/// let cell1 = grid.get(0,0).unwrap();
/// let cell2 = grid.get(0,1).unwrap();
///
/// assert_eq!(cell1.get_value_copy(), CellValue::Unknown(vec![1,2,3,4,5,6,7,8,9]));
/// assert_eq!(cell2.get_value_copy(), CellValue::Unknown(vec![1,2,3,4,5,6,7,8,9]));
///
/// cell1.set(1);
///
/// assert_eq!(cell1.get_value_copy(), CellValue::Fixed(1));
/// assert_eq!(cell2.get_value_copy(), CellValue::Unknown(vec![2,3,4,5,6,7,8,9]));
///
/// ```
pub fn set(&self, digit: u8){ pub fn set(&self, digit: u8){
unsafe { unsafe {
if DEBUG { if DEBUG {
@ -46,11 +68,14 @@ impl Cell {
Cell::process_possibilities(section, digit); Cell::process_possibilities(section, digit);
} }
/// Get a copy of the `CellValue`
pub fn get_value_copy(&self) -> CellValue { pub fn get_value_copy(&self) -> CellValue {
let value = &*self.value.borrow(); let value = &*self.value.borrow();
return value.clone(); return value.clone();
} }
/// Set the cell value with a provided `CellValue`; if `value` is Fixed then the related cell's
/// possibilities are adjusted like in `set`.
pub fn set_value(&self, value: CellValue){ pub fn set_value(&self, value: CellValue){
match value { match value {
CellValue::Fixed(digit) => { CellValue::Fixed(digit) => {
@ -63,6 +88,26 @@ impl Cell {
} }
} }
/// Set the `Cell`'s value to be a value **without** adjusting any of the nearby cells.
///
/// # Examples
///
/// ```
/// use sudoku_solver::grid::{Grid, CellValue};
/// let grid = Grid::new();
///
/// let cell1 = grid.get(0,0).unwrap();
/// let cell2 = grid.get(0,1).unwrap();
///
/// assert_eq!(cell1.get_value_copy(), CellValue::Unknown(vec![1,2,3,4,5,6,7,8,9]));
/// assert_eq!(cell2.get_value_copy(), CellValue::Unknown(vec![1,2,3,4,5,6,7,8,9]));
///
/// cell1.set_value_exact(CellValue::Fixed(1));
///
/// assert_eq!(cell1.get_value_copy(), CellValue::Fixed(1));
/// assert_eq!(cell2.get_value_copy(), CellValue::Unknown(vec![1,2,3,4,5,6,7,8,9])); // still contains 1
///
/// ```
pub fn set_value_exact(&self, value: CellValue){ pub fn set_value_exact(&self, value: CellValue){
unsafe { unsafe {
if DEBUG { if DEBUG {
@ -74,6 +119,7 @@ impl Cell {
self.mark_updates(); self.mark_updates();
} }
/// Return a copy of the cell's possibilities if it has them.
pub fn get_value_possibilities(&self) -> Option<Vec<u8>> { pub fn get_value_possibilities(&self) -> Option<Vec<u8>> {
let value = &*self.value.borrow(); let value = &*self.value.borrow();
match value { match value {
@ -82,6 +128,8 @@ impl Cell {
} }
} }
// Internal function - mark all the Sections the cell belongs to as having had a change
// so that the solver will look at it later
fn mark_updates(&self){ fn mark_updates(&self){
{ {
let row = &*self.row.upgrade().unwrap(); let row = &*self.row.upgrade().unwrap();
@ -100,7 +148,8 @@ impl Cell {
} }
} }
fn process_possibilities(line: &Line, digit: u8){ // Go through and remove digit from the Section's Cells' possibilities
fn process_possibilities(line: &Section, digit: u8){
for (_index, cell) in line.vec.iter().enumerate() { for (_index, cell) in line.vec.iter().enumerate() {
let cell = &**cell; let cell = &**cell;
@ -143,38 +192,44 @@ impl Cell {
} }
} }
pub struct Line { /// A representation of either a Row, Column, or Square in a Sudoku grid. Don't make this directly; make a Grid.
pub struct Section {
/// A vector of `Rc`s of the `Cell`s inside this Section. We use `Rc` because one of the
/// Sections needs to have ownership of the Cells but then the others have to have a different
/// signature.
pub vec: Vec<Rc<Cell>>, pub vec: Vec<Rc<Cell>>,
pub do_update: RefCell<bool>, pub do_update: RefCell<bool>,
pub index: usize, pub index: usize,
pub line_type: LineType pub section_type: SectionType
} }
#[derive(Debug)] #[derive(Debug)]
pub enum LineType { pub enum SectionType {
Row, Row,
Column, Column,
Section Square
} }
impl Line { impl Section {
fn push(&mut self, x: Rc<Cell>){ fn push(&mut self, x: Rc<Cell>){
self.vec.push(x); self.vec.push(x);
} }
/// Short-hand for accessing `vec` and calling it's `get` method.
pub fn get(&self, index: usize) -> Option<&Rc<Cell>>{ pub fn get(&self, index: usize) -> Option<&Rc<Cell>>{
self.vec.get(index) self.vec.get(index)
} }
fn new(index: usize, line_type: LineType) -> Line { fn new(index: usize, line_type: SectionType) -> Section {
Line { Section {
vec: Vec::new(), vec: Vec::new(),
do_update: RefCell::new(false), do_update: RefCell::new(false),
index, index,
line_type section_type: line_type
} }
} }
/// Return a copy of whether this `Section` has been marked for the solver to work on it or not.
pub fn do_update(&self) -> bool { pub fn do_update(&self) -> bool {
let do_update = &self.do_update.borrow(); let do_update = &self.do_update.borrow();
let do_update = &**do_update; let do_update = &**do_update;
@ -185,23 +240,25 @@ impl Line {
type MultiMut<T> = Rc<RefCell<T>>; type MultiMut<T> = Rc<RefCell<T>>;
/// A representation of a Sudoku grid.
pub struct Grid { pub struct Grid {
pub rows: Vec<MultiMut<Line>>, // Read from top to bottom pub rows: Vec<MultiMut<Section>>, // Read from top to bottom
pub columns: Vec<MultiMut<Line>>, pub columns: Vec<MultiMut<Section>>,
pub sections: Vec<MultiMut<Line>>, pub sections: Vec<MultiMut<Section>>,
} }
impl Grid { impl Grid {
/// Generate a new empty `Grid` with full empty possibilities for each `Cell`
pub fn new() -> Grid { pub fn new() -> Grid {
let mut rows: Vec<MultiMut<Line>> = Vec::new(); let mut rows: Vec<MultiMut<Section>> = Vec::new();
let mut columns: Vec<MultiMut<Line>> = Vec::new(); let mut columns: Vec<MultiMut<Section>> = Vec::new();
let mut sections: Vec<MultiMut<Line>> = Vec::new(); let mut sections: Vec<MultiMut<Section>> = Vec::new();
for i in 0..9 { for i in 0..9 {
rows.push(Rc::new(RefCell::new(Line::new(i, LineType::Row)))); rows.push(Rc::new(RefCell::new(Section::new(i, SectionType::Row))));
columns.push(Rc::new(RefCell::new(Line::new(i, LineType::Column)))); columns.push(Rc::new(RefCell::new(Section::new(i, SectionType::Column))));
sections.push(Rc::new(RefCell::new(Line::new(i, LineType::Section)))); sections.push(Rc::new(RefCell::new(Section::new(i, SectionType::Square))));
} }
for row_index in 0..9 { for row_index in 0..9 {
@ -248,24 +305,26 @@ impl Grid {
return Grid { rows, columns, sections }; return Grid { rows, columns, sections };
} }
pub fn get(&self, r: usize, c: usize) -> Result<Rc<Cell>, &str> { /// Returns the `Cell` (in an `Rc`) at the specified coordinates.
if (r > 9) | (c > 9) { /// * `r` is the row coordinate (first row starting at 0)
return Err("Row or column indices are out of bounds"); /// * `c` is the column coordinate (first column starting at 0)
} ///
/// Returns None if the coordinates are out of bounds.
pub fn get(&self, r: usize, c: usize) -> Option<Rc<Cell>> {
let row = match self.rows.get(r) { let row = match self.rows.get(r) {
Some(x) => x, Some(x) => x,
None => {return Err("Row index is out of bounds")} None => return None
}; };
let row = &*(&**row).borrow(); let row = &*(&**row).borrow();
let cell = match row.get(c) { let cell = match row.get(c) {
Some(x) => x, Some(x) => x,
None => {return Err("Column index is out of bounds")} None => return None
}; };
return Ok(Rc::clone(cell)); return Some(Rc::clone(cell));
} }
fn process_unknown(x: &Vec<u8>, digit: u8, row: &mut String){ fn process_unknown(x: &Vec<u8>, digit: u8, row: &mut String){
@ -275,6 +334,32 @@ impl Grid {
row.push(' '); row.push(' ');
} }
} }
/// Find the smallest empty `Cell` in terms of possibilities; returns `None` if all Cells have
/// `Fixed` `CellValue`s.
pub fn find_smallest_cell(&self) -> Option<Rc<Cell>>{
let mut smallest_cell : Option<Rc<Cell>> = None;
let mut smallest_size = usize::MAX;
for x in 0..9 {
for y in 0..9 {
let cell_rc = self.get(x, y).unwrap();
let cell = &*self.get(x, y).unwrap();
let cell_value = &*cell.value.borrow();
match cell_value {
CellValue::Unknown(possibilities) => {
if (possibilities.len() < smallest_size) && (possibilities.len() > 0){
smallest_size = possibilities.len();
smallest_cell = Some(cell_rc);
}
},
_ => {}
}
}
}
smallest_cell
}
} }
impl Clone for Grid { impl Clone for Grid {

View file

@ -1,6 +1,6 @@
use std::rc::Rc; use std::rc::Rc;
use crate::grid::{Cell, Line, Grid, CellValue}; use crate::grid::{Cell, Section, Grid, CellValue};
pub static mut DEBUG: bool = false; pub static mut DEBUG: bool = false;
@ -17,7 +17,7 @@ pub enum SolveStatus {
Invalid Invalid
} }
/// See `SolveController` for a description of the solving strategies.
enum SolveAction{ enum SolveAction{
Single, Single,
HiddenSingle, HiddenSingle,
@ -54,12 +54,29 @@ impl SolveStatus {
} }
} }
/// A struct representing some options & solving strategies for solving a `Grid`.
pub struct SolveController { pub struct SolveController {
/// Whether the solver should try to determine if the solution is unique at the cost of extra computation.
pub determine_uniqueness: bool, pub determine_uniqueness: bool,
/// Whether the solving strategy where Cells with a single possibility are set to their value is enabled.
/// Has never been tested with a `false` value.
pub search_singles: bool, pub search_singles: bool,
/// Whether the solving strategy where Cells that contain as a digit a possibility that only occurs
/// once in that `Section` should be set to that value, is enabled. Has never been tested with a `false` value.
pub search_hidden_singles: bool, pub search_hidden_singles: bool,
/// Whether the solving strategy where, in a given `Section`, the solver tries to divide the un-set cells up
/// into two or more exclusive groups based on their possibilities, is enabled.
pub find_possibility_groups: bool, pub find_possibility_groups: bool,
/// Whether the solving strategy where if you know that a digit must occur in a part of Section A that overlaps
/// entirely with Section B, that you can then determine that that digit cannot occur in the rest of
/// Section B, is enabled.
pub search_useful_constraint: bool, pub search_useful_constraint: bool,
/// Whether the solver can make guesses as a last resort to try to solve the puzzle.
pub make_guesses: bool, pub make_guesses: bool,
} }
@ -89,10 +106,10 @@ impl SolveController {
} }
} }
/** /// Tracks how often we relied on each solving strategy to make progress. We'll consider 'relied on' to mean
Tracks when we relied on a method to make progress. We'll consider 'relied on' to mean that the method make at least /// that the method make at least one change to the line it was originally called on, whether that
one change to the line it was originally called on, whether that be setting a value or adjusting the possibilities in a cell. /// be setting a value or adjusting the possibilities in a cell. Multiple contributions in one call
*/ /// of the strategy on a `Section` are only counted as one contribution.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct SolveStatistics { pub struct SolveStatistics {
pub singles: u32, pub singles: u32,
@ -103,7 +120,9 @@ pub struct SolveStatistics {
} }
impl SolveStatistics { impl SolveStatistics {
pub(crate) fn new() -> SolveStatistics {
/// Create a new SolveStatistics with `0` counts set for all the fields.
pub fn new() -> SolveStatistics {
SolveStatistics{ SolveStatistics{
singles: 0, singles: 0,
hidden_singles: 0, hidden_singles: 0,
@ -124,43 +143,13 @@ impl SolveStatistics {
} }
} }
pub fn find_smallest_cell(grid: &Grid) -> Option<Rc<Cell>>{
// Find a cell of smallest size (in terms of possibilities) and make a guess
// Can assume that no cells of only possibility 1 exist
let mut smallest_cell : Option<Rc<Cell>> = None;
let mut smallest_size = usize::MAX;
'outer: for x in 0..9 {
for y in 0..9 {
let cell_rc = grid.get(x, y).unwrap();
let cell = &*grid.get(x, y).unwrap();
let cell_value = &*cell.value.borrow();
match cell_value {
CellValue::Unknown(possibilities) => {
if (possibilities.len() < smallest_size) && (possibilities.len() > 0){
smallest_size = possibilities.len();
smallest_cell = Some(cell_rc);
}
},
_ => {}
}
if smallest_size <= 2 {
break 'outer; // We aren't going to get smaller
}
}
}
smallest_cell
}
// Code for identify_and_process_possibility_groups (it uses it's own structs) // Code for identify_and_process_possibility_groups (it uses it's own structs)
mod process_possibility_groups { mod process_possibility_groups {
use crate::grid::{Line, CellValue}; use crate::grid::{Section, CellValue};
use std::collections::HashSet; use std::collections::HashSet;
use std::rc::Rc; use std::rc::Rc;
@ -194,18 +183,18 @@ mod process_possibility_groups {
} }
// See if there's a set of cells with possibilities that exclude those possibilities from other cells. // 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. // 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) -> bool{ pub fn identify_and_process_possibility_groups(line: &Section) -> bool{
unsafe { unsafe {
if super::DEBUG { if super::DEBUG {
println!("Looking for possibility groups on line {:?} {}", line.line_type, line.index); println!("Looking for possibility groups on line {:?} {}", line.section_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>) -> bool{ fn bisect_possibility_groups(line: &Section, cells_of_interest: Vec<usize>) -> bool{
/* /*
Algorithm - Algorithm -
@ -381,10 +370,10 @@ mod process_possibility_groups {
} }
// Search for a cell with only one possibility so that we can set it to FIXED // Search for a cell with only one possibility so that we can set it to FIXED
fn search_single_possibility(line: &Line) -> bool{ fn search_single_possibility(line: &Section) -> bool{
unsafe { unsafe {
if DEBUG { if DEBUG {
println!("search_single_possibility on line {:?} {}", line.line_type, line.index); println!("search_single_possibility on line {:?} {}", line.section_type, line.index);
} }
} }
@ -407,7 +396,7 @@ fn search_single_possibility(line: &Line) -> bool{
} }
// 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 // 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) -> bool{ fn search_hidden_single(line: &Section) -> bool{
enum Count { enum Count {
None, None,
One(Rc<Cell>), One(Rc<Cell>),
@ -456,7 +445,7 @@ fn search_hidden_single(line: &Line) -> bool{
} }
mod search_useful_constraint{ mod search_useful_constraint{
use crate::grid::{Grid, Line, LineType, CellValue}; use crate::grid::{Grid, Section, SectionType, CellValue};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::cell::RefCell; use std::cell::RefCell;
@ -479,19 +468,19 @@ mod search_useful_constraint{
// I.e. If possibility '1' only occurs in the first row for section 0, then you can remove that possibility // 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 // 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. // for row 0, then you can remove the possibility from the rest of section 0.
pub fn search_useful_constraint(grid: &Grid, line: &Line) -> bool{ pub fn search_useful_constraint(grid: &Grid, line: &Section) -> bool{
unsafe { unsafe {
if super::DEBUG { if super::DEBUG {
println!("Searching for a useful constraint on line {:?} {}", line.line_type, line.index); println!("Searching for a useful constraint on line {:?} {}", line.section_type, line.index);
} }
} }
let mut made_change = false; let mut made_change = false;
let (check_row, check_column, check_section) = match line.line_type { let (check_row, check_column, check_section) = match line.section_type {
LineType::Row => {(false, false, true)}, SectionType::Row => {(false, false, true)},
LineType::Column => {(false, false, true)}, SectionType::Column => {(false, false, true)},
LineType::Section => {(true, true, false)}, SectionType::Square => {(true, true, false)},
}; };
for possibility in 0..9 { for possibility in 0..9 {
@ -532,21 +521,21 @@ mod search_useful_constraint{
match rows { match rows {
PossibilityLines::Unique(index) => { PossibilityLines::Unique(index) => {
made_change = made_change | made_change = made_change |
remove_possibilities_line(grid.rows.get(index).unwrap(), possibility, &line.line_type, line.index); remove_possibilities_line(grid.rows.get(index).unwrap(), possibility, &line.section_type, line.index);
}, },
_ => {} _ => {}
} }
match columns { match columns {
PossibilityLines::Unique(index) => { PossibilityLines::Unique(index) => {
made_change = made_change | made_change = made_change |
remove_possibilities_line(grid.columns.get(index).unwrap(), possibility, &line.line_type, line.index); remove_possibilities_line(grid.columns.get(index).unwrap(), possibility, &line.section_type, line.index);
}, },
_ => {} _ => {}
} }
match sections { match sections {
PossibilityLines::Unique(index) => { PossibilityLines::Unique(index) => {
made_change = made_change | made_change = made_change |
remove_possibilities_line(grid.sections.get(index).unwrap(), possibility, &line.line_type, line.index); remove_possibilities_line(grid.sections.get(index).unwrap(), possibility, &line.section_type, line.index);
}, },
_ => {} _ => {}
} }
@ -557,7 +546,7 @@ mod search_useful_constraint{
} }
// initial_line_type and initial_line_index are to identify the cells that should NOT have their possibilities removed // 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) -> bool { fn remove_possibilities_line(line: &Rc<RefCell<Section>>, digit_to_remove: u8, initial_line_type: &SectionType, initial_line_index: usize) -> bool {
let line = &*(&**line).borrow(); let line = &*(&**line).borrow();
let mut made_change = false; let mut made_change = false;
@ -567,9 +556,9 @@ mod search_useful_constraint{
match value { match value {
CellValue::Unknown(possibilities) => { CellValue::Unknown(possibilities) => {
let parent_line = match initial_line_type { let parent_line = match initial_line_type {
LineType::Row => &cell.row, SectionType::Row => &cell.row,
LineType::Column => &cell.column, SectionType::Column => &cell.column,
LineType::Section => &cell.section SectionType::Square => &cell.section
}; };
let parent_line = &*parent_line.upgrade().unwrap(); let parent_line = &*parent_line.upgrade().unwrap();
let parent_line = &*parent_line.borrow(); let parent_line = &*parent_line.borrow();
@ -609,7 +598,7 @@ mod search_useful_constraint{
} }
// We detected a useful constraint // We detected a useful constraint
fn process_possibility_line(possibility_line: PossibilityLines, line: &Weak<RefCell<Line>>) -> PossibilityLines { fn process_possibility_line(possibility_line: PossibilityLines, line: &Weak<RefCell<Section>>) -> PossibilityLines {
let line = line.upgrade().unwrap(); let line = line.upgrade().unwrap();
let line = &*(&*line).borrow(); let line = &*(&*line).borrow();
@ -629,10 +618,10 @@ mod search_useful_constraint{
} }
fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics){ fn solve_line(grid: &Grid, line: &Section, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics){
unsafe { unsafe {
if DEBUG { if DEBUG {
println!("Solving {:?} {}", line.line_type, line.index); println!("Solving {:?} {}", line.section_type, line.index);
} }
} }
@ -641,7 +630,7 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solv
if solve_controller.search_singles() { if solve_controller.search_singles() {
unsafe { unsafe {
if DEBUG { if DEBUG {
println!("Searching for singles on line {:?} of {}\n{}", line.line_type, line.index, grid); println!("Searching for singles on line {:?} of {}\n{}", line.section_type, line.index, grid);
} }
} }
if search_single_possibility(line) { if search_single_possibility(line) {
@ -652,7 +641,7 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solv
if solve_controller.search_hidden_singles() { if solve_controller.search_hidden_singles() {
unsafe { unsafe {
if DEBUG { if DEBUG {
println!("Searching for hidden singles on line {:?} of {}\n{}", line.line_type, line.index, grid); println!("Searching for hidden singles on line {:?} of {}\n{}", line.section_type, line.index, grid);
} }
} }
if search_hidden_single(line) { if search_hidden_single(line) {
@ -663,7 +652,7 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solv
if solve_controller.find_possibility_groups() { if solve_controller.find_possibility_groups() {
unsafe { unsafe {
if DEBUG { if DEBUG {
println!("Searching for possibility groups on line {:?} of {}\n{}", line.line_type, line.index, grid); println!("Searching for possibility groups on line {:?} of {}\n{}", line.section_type, line.index, grid);
} }
} }
if process_possibility_groups::identify_and_process_possibility_groups(line) { if process_possibility_groups::identify_and_process_possibility_groups(line) {
@ -674,7 +663,7 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solv
if solve_controller.search_useful_constraint() { if solve_controller.search_useful_constraint() {
unsafe { unsafe {
if DEBUG { if DEBUG {
println!("Searching for useful constraints on line {:?} of {}\n{}", line.line_type, line.index, grid); println!("Searching for useful constraints on line {:?} of {}\n{}", line.section_type, line.index, grid);
} }
} }
if search_useful_constraint::search_useful_constraint(grid, line) { if search_useful_constraint::search_useful_constraint(grid, line) {
@ -684,6 +673,9 @@ fn solve_line(grid: &Grid, line: &Line, solve_controller: &SolveController, solv
} }
/// Solves (and modifies) the input `Grid`. Returns a `SolveStatus` and `SolveStatistics`, and
/// enables all solving strategies. If you want to specify a `SolveController` you can
/// call `solve_grid_with_solve_controller` directly, but you also have to input an empty `SolveStatistics`.
pub fn solve_grid(grid: &mut Grid) -> (SolveStatus, SolveStatistics) { pub fn solve_grid(grid: &mut Grid) -> (SolveStatus, SolveStatistics) {
// By default we enable everything // By default we enable everything
let solve_controller = SolveController { let solve_controller = SolveController {
@ -701,6 +693,7 @@ pub fn solve_grid(grid: &mut Grid) -> (SolveStatus, SolveStatistics) {
return (solve_status, solve_statistics); return (solve_status, solve_statistics);
} }
/// Solves (and modifies) the input `Grid` & `SolveStatistics`. Returns a `SolveStatus`.
pub fn solve_grid_with_solve_controller(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> 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 // 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 // If that's not enough and a guess is required, then solve_grid_guess is called
@ -724,7 +717,9 @@ pub fn solve_grid_with_solve_controller(grid: &mut Grid, solve_controller: &Solv
return status; return status;
} }
// Similar to solve_grid_with_solve_controller except that we don't modify the input Grid; we only determine SolveStatus /// Similar to `solve_grid_with_solve_controller` except that we don't modify the input Grid; we
/// only determine SolveStatus. This is useful when generating puzzles where we need to know *if*
/// a puzzle can be solved but don't want to actually solve it.
pub fn evaluate_grid_with_solve_controller(grid: &Grid, solve_controller: &SolveController) -> (SolveStatus, SolveStatistics){ pub fn evaluate_grid_with_solve_controller(grid: &Grid, solve_controller: &SolveController) -> (SolveStatus, SolveStatistics){
let mut mut_grid = grid.clone(); let mut mut_grid = grid.clone();
let mut solve_statistics = SolveStatistics::new(); let mut solve_statistics = SolveStatistics::new();
@ -734,7 +729,7 @@ pub fn evaluate_grid_with_solve_controller(grid: &Grid, solve_controller: &Solve
return (solve_status, solve_statistics); return (solve_status, solve_statistics);
} }
pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{ fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{
loop { loop {
let mut ran_something = false; let mut ran_something = false;
@ -800,7 +795,7 @@ pub fn solve_grid_no_guess(grid: &mut Grid, solve_controller: &SolveController,
fn solve_grid_guess(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{ fn solve_grid_guess(grid: &mut Grid, solve_controller: &SolveController, solve_statistics: &mut SolveStatistics) -> SolveStatus{
solve_statistics.increment(&SolveAction::Guess); solve_statistics.increment(&SolveAction::Guess);
let smallest_cell = find_smallest_cell(grid); let smallest_cell = grid.find_smallest_cell();
let smallest_cell = match smallest_cell { let smallest_cell = match smallest_cell {
Some(cell) => cell, Some(cell) => cell,
None => return SolveStatus::Invalid None => return SolveStatus::Invalid