Add some documentation; still needs a lot of work
This commit is contained in:
parent
12a3ea9eab
commit
235522224b
3 changed files with 180 additions and 100 deletions
|
@ -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;
|
||||||
|
|
141
src/grid.rs
141
src/grid.rs
|
@ -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 {
|
||||||
|
|
131
src/solver.rs
131
src/solver.rs
|
@ -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;
|
||||||
|
|
||||||
|
@ -195,17 +184,17 @@ 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
|
||||||
|
|
Loading…
Reference in a new issue