Implement clippy suggestions
This commit is contained in:
parent
978d3ab3f3
commit
db15c30379
6 changed files with 164 additions and 213 deletions
|
@ -1,4 +1,5 @@
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use std::cmp::Ordering as ComparableOrdering;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
@ -152,29 +153,31 @@ fn main() {
|
||||||
|
|
||||||
let solve_controller = difficulty.map_to_solve_controller();
|
let solve_controller = difficulty.map_to_solve_controller();
|
||||||
|
|
||||||
let (result, num_attempts) = if threads < 1 {
|
let (result, num_attempts) = match threads.cmp(&1) {
|
||||||
eprintln!("--threads must be at least 1");
|
ComparableOrdering::Less => {
|
||||||
exit(1);
|
eprintln!("--threads must be at least 1");
|
||||||
} else if threads == 1 {
|
exit(1);
|
||||||
let mut rng = SmallRng::from_entropy();
|
}
|
||||||
get_puzzle_matching_conditions(
|
ComparableOrdering::Equal => {
|
||||||
&mut rng,
|
let mut rng = SmallRng::from_entropy();
|
||||||
&difficulty,
|
get_puzzle_matching_conditions(
|
||||||
&solve_controller,
|
&mut rng,
|
||||||
max_attempts,
|
&difficulty,
|
||||||
max_hints,
|
&solve_controller,
|
||||||
&AtomicBool::new(false),
|
max_attempts,
|
||||||
debug,
|
max_hints,
|
||||||
)
|
&AtomicBool::new(false),
|
||||||
} else {
|
debug,
|
||||||
run_multi_threaded(
|
)
|
||||||
|
}
|
||||||
|
ComparableOrdering::Greater => run_multi_threaded(
|
||||||
max_attempts,
|
max_attempts,
|
||||||
max_hints,
|
max_hints,
|
||||||
threads,
|
threads,
|
||||||
debug,
|
debug,
|
||||||
solve_controller,
|
solve_controller,
|
||||||
difficulty,
|
difficulty,
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (grid, solve_statistics, num_hints) = match result {
|
let (grid, solve_statistics, num_hints) = match result {
|
||||||
|
@ -209,18 +212,15 @@ fn main() {
|
||||||
println!("\t{} GUESS actions", solve_statistics.guesses);
|
println!("\t{} GUESS actions", solve_statistics.guesses);
|
||||||
}
|
}
|
||||||
|
|
||||||
match filename {
|
if let Some(filename) = filename {
|
||||||
Some(filename) => {
|
// check if we save to a csv or a pdf
|
||||||
// check if we save to a csv or a pdf
|
if filename.ends_with(".pdf") {
|
||||||
if filename.ends_with(".pdf") {
|
sudoku_solver::pdf::draw_grid(&grid, &filename, print_possibilities).unwrap();
|
||||||
sudoku_solver::pdf::draw_grid(&grid, &filename, print_possibilities).unwrap();
|
println!("Grid saved as pdf to {}", filename);
|
||||||
println!("Grid saved as pdf to {}", filename);
|
} else {
|
||||||
} else {
|
save_grid_csv(&grid, &filename).unwrap();
|
||||||
save_grid_csv(&grid, &filename).unwrap();
|
println!("Grid saved as CSV to {}", filename);
|
||||||
println!("Grid saved as CSV to {}", filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,16 +293,13 @@ fn run_multi_threaded(
|
||||||
let (result, attempts) = signal;
|
let (result, attempts) = signal;
|
||||||
attempt_count += attempts;
|
attempt_count += attempts;
|
||||||
|
|
||||||
match result {
|
if let Some((safe_grid, solve_statistics, num_hints)) = result {
|
||||||
Some((safe_grid, solve_statistics, num_hints)) => {
|
result_to_return = Some((safe_grid.0, solve_statistics, num_hints));
|
||||||
result_to_return = Some((safe_grid.0, solve_statistics, num_hints));
|
should_stop.store(true, Ordering::Relaxed);
|
||||||
should_stop.store(true, Ordering::Relaxed);
|
}
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (result_to_return, attempt_count);
|
(result_to_return, attempt_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_puzzle_matching_conditions(
|
fn get_puzzle_matching_conditions(
|
||||||
|
@ -330,7 +327,7 @@ fn get_puzzle_matching_conditions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (None, num_attempts);
|
(None, num_attempts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_grid_csv(grid: &Grid, filename: &str) -> Result<(), Box<dyn Error>> {
|
fn save_grid_csv(grid: &Grid, filename: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
@ -350,9 +347,9 @@ fn save_grid_csv(grid: &Grid, filename: &str) -> Result<(), Box<dyn Error>> {
|
||||||
if y < 8 {
|
if y < 8 {
|
||||||
text.push(',');
|
text.push(',');
|
||||||
}
|
}
|
||||||
file.write(text.as_bytes())?;
|
file.write_all(text.as_bytes())?;
|
||||||
}
|
}
|
||||||
file.write(b"\n")?;
|
file.write_all(b"\n")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -59,8 +59,7 @@ fn read_grid(filename: &str) -> Result<Grid, String> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let grid = Grid::new();
|
let grid = Grid::new();
|
||||||
let mut row = 0;
|
for (row, result) in reader.records().enumerate() {
|
||||||
for result in reader.records() {
|
|
||||||
if row > 8 {
|
if row > 8 {
|
||||||
return Err("Hit row limit".to_string());
|
return Err("Hit row limit".to_string());
|
||||||
}
|
}
|
||||||
|
@ -69,24 +68,19 @@ fn read_grid(filename: &str) -> Result<Grid, String> {
|
||||||
|
|
||||||
for column in 0..9 {
|
for column in 0..9 {
|
||||||
let value = record.get(column);
|
let value = record.get(column);
|
||||||
match value {
|
if let Some(x) = value {
|
||||||
Some(x) => {
|
let digit_result = u8::from_str(x);
|
||||||
let digit_result = u8::from_str(x);
|
match digit_result {
|
||||||
match digit_result {
|
Ok(digit) => {
|
||||||
Ok(digit) => {
|
if digit > 0 {
|
||||||
if digit > 0 {
|
grid.get(row, column).unwrap().set(digit);
|
||||||
grid.get(row, column).unwrap().set(digit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_error) => return Err("Invalid cell value".to_string()),
|
}
|
||||||
};
|
Err(_error) => return Err("Invalid cell value".to_string()),
|
||||||
}
|
};
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row = row + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(grid);
|
Ok(grid)
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,17 +76,11 @@ impl 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();
|
||||||
match value {
|
if let CellValue::Fixed(digit) = value {
|
||||||
CellValue::Fixed(digit) => {
|
let location = possibilities.binary_search(digit);
|
||||||
let location = possibilities.binary_search(digit);
|
if let Ok(location) = location {
|
||||||
match location {
|
possibilities.remove(location);
|
||||||
Ok(location) => {
|
|
||||||
possibilities.remove(location);
|
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CellValue::Unknown(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +102,7 @@ impl Cell {
|
||||||
self,
|
self,
|
||||||
);
|
);
|
||||||
|
|
||||||
return possibilities;
|
possibilities
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,20 +146,20 @@ pub fn generate_grid(
|
||||||
let mut statistics_option = None;
|
let mut statistics_option = None;
|
||||||
|
|
||||||
for (_index, cell) in non_empty_cells.iter().enumerate() {
|
for (_index, cell) in non_empty_cells.iter().enumerate() {
|
||||||
let mut grid_clone = grid.clone();
|
let grid_clone = grid.clone();
|
||||||
let cell_clone = grid_clone.get(cell.x, cell.y).unwrap();
|
let cell_clone = grid_clone.get(cell.x, cell.y).unwrap();
|
||||||
let cell_clone = &*cell_clone;
|
let cell_clone = &*cell_clone;
|
||||||
|
|
||||||
cell_clone.delete_value();
|
cell_clone.delete_value();
|
||||||
|
|
||||||
let (status, statistics) =
|
let (status, statistics) =
|
||||||
evaluate_grid_with_solve_controller(&mut grid_clone, solve_controller);
|
evaluate_grid_with_solve_controller(&grid_clone, solve_controller);
|
||||||
match status {
|
match status {
|
||||||
SolveStatus::Complete(uniqueness) => {
|
SolveStatus::Complete(uniqueness) => {
|
||||||
let uniqueness = uniqueness.unwrap();
|
let uniqueness = uniqueness.unwrap();
|
||||||
match uniqueness {
|
match uniqueness {
|
||||||
Uniqueness::Unique => {
|
Uniqueness::Unique => {
|
||||||
num_hints = num_hints - 1;
|
num_hints -= 1;
|
||||||
grid = grid_clone;
|
grid = grid_clone;
|
||||||
}
|
}
|
||||||
Uniqueness::NotUnique => continue, // We can't remove this cell; continue onto the next one (note that grid hasn't been modified because of solve_controller)
|
Uniqueness::NotUnique => continue, // We can't remove this cell; continue onto the next one (note that grid hasn't been modified because of solve_controller)
|
||||||
|
@ -181,7 +175,7 @@ pub fn generate_grid(
|
||||||
statistics_option = Some(statistics);
|
statistics_option = Some(statistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (grid, num_hints, statistics_option.unwrap());
|
(grid, num_hints, statistics_option.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// We generate a completed grid with no mind for difficulty; afterward generate_puzzle will take out as many fields as it can with regards to the difficulty
|
// We generate a completed grid with no mind for difficulty; afterward generate_puzzle will take out as many fields as it can with regards to the difficulty
|
||||||
|
@ -245,13 +239,13 @@ fn generate_completed_grid(rng: &mut SmallRng) -> Grid {
|
||||||
cell_possibilities.shuffle(rng);
|
cell_possibilities.shuffle(rng);
|
||||||
|
|
||||||
for (_index, digit) in cell_possibilities.iter().enumerate() {
|
for (_index, digit) in cell_possibilities.iter().enumerate() {
|
||||||
let mut grid_clone = grid.clone();
|
let grid_clone = grid.clone();
|
||||||
let cell = &*grid_clone.get(cell.x, cell.y).unwrap();
|
let cell = &*grid_clone.get(cell.x, cell.y).unwrap();
|
||||||
|
|
||||||
cell.set(*digit);
|
cell.set(*digit);
|
||||||
|
|
||||||
let (status, _statistics) =
|
let (status, _statistics) =
|
||||||
evaluate_grid_with_solve_controller(&mut grid_clone, &solve_controller);
|
evaluate_grid_with_solve_controller(&grid_clone, &solve_controller);
|
||||||
match status {
|
match status {
|
||||||
SolveStatus::Complete(uniqueness) => {
|
SolveStatus::Complete(uniqueness) => {
|
||||||
let uniqueness = uniqueness.unwrap();
|
let uniqueness = uniqueness.unwrap();
|
||||||
|
@ -279,7 +273,7 @@ fn generate_completed_grid(rng: &mut SmallRng) -> Grid {
|
||||||
|
|
||||||
crate::solver::solve_grid(&mut grid);
|
crate::solver::solve_grid(&mut grid);
|
||||||
|
|
||||||
return grid;
|
grid
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
63
src/grid.rs
63
src/grid.rs
|
@ -71,7 +71,7 @@ impl Cell {
|
||||||
/// Get a copy of the `CellValue`
|
/// 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();
|
value.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the cell value with a provided `CellValue`; if `value` is Fixed then the related cell's
|
/// Set the cell value with a provided `CellValue`; if `value` is Fixed then the related cell's
|
||||||
|
@ -80,7 +80,6 @@ impl Cell {
|
||||||
match value {
|
match value {
|
||||||
CellValue::Fixed(digit) => {
|
CellValue::Fixed(digit) => {
|
||||||
self.set(digit);
|
self.set(digit);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
CellValue::Unknown(_) => {
|
CellValue::Unknown(_) => {
|
||||||
self.set_value_exact(value);
|
self.set_value_exact(value);
|
||||||
|
@ -164,12 +163,9 @@ impl Cell {
|
||||||
CellValue::Unknown(possibilities) => {
|
CellValue::Unknown(possibilities) => {
|
||||||
let mut new_possibilities = possibilities.clone();
|
let mut new_possibilities = possibilities.clone();
|
||||||
|
|
||||||
match new_possibilities.binary_search(&digit) {
|
if let Ok(index_remove) = new_possibilities.binary_search(&digit) {
|
||||||
Ok(index_remove) => {
|
new_possibilities.remove(index_remove);
|
||||||
new_possibilities.remove(index_remove);
|
}
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(CellValue::Unknown(new_possibilities))
|
Some(CellValue::Unknown(new_possibilities))
|
||||||
/*
|
/*
|
||||||
|
@ -186,11 +182,8 @@ impl Cell {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match new_value_option {
|
if let Some(new_value) = new_value_option {
|
||||||
Some(new_value) => {
|
cell.set_value(new_value);
|
||||||
cell.set_value(new_value);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,7 +231,7 @@ impl Section {
|
||||||
let do_update = &self.do_update.borrow();
|
let do_update = &self.do_update.borrow();
|
||||||
let do_update = &**do_update;
|
let do_update = &**do_update;
|
||||||
|
|
||||||
return do_update.clone();
|
*do_update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,11 +298,11 @@ impl Grid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Grid {
|
Grid {
|
||||||
rows,
|
rows,
|
||||||
columns,
|
columns,
|
||||||
sections,
|
sections,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `Cell` (in an `Rc`) at the specified coordinates.
|
/// Returns the `Cell` (in an `Rc`) at the specified coordinates.
|
||||||
|
@ -330,10 +323,10 @@ impl Grid {
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Some(Rc::clone(cell));
|
Some(Rc::clone(cell))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_unknown(x: &Vec<u8>, digit: u8, row: &mut String) {
|
fn process_unknown(x: &[u8], digit: u8, row: &mut String) {
|
||||||
if x.contains(&digit) {
|
if x.contains(&digit) {
|
||||||
row.push('*');
|
row.push('*');
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,14 +346,11 @@ impl Grid {
|
||||||
let cell = &*self.get(x, y).unwrap();
|
let cell = &*self.get(x, y).unwrap();
|
||||||
let cell_value = &*cell.value.borrow();
|
let cell_value = &*cell.value.borrow();
|
||||||
|
|
||||||
match cell_value {
|
if let CellValue::Unknown(possibilities) = cell_value {
|
||||||
CellValue::Unknown(possibilities) => {
|
if (possibilities.len() < smallest_size) && (!possibilities.is_empty()) {
|
||||||
if (possibilities.len() < smallest_size) && (possibilities.len() > 0) {
|
smallest_size = possibilities.len();
|
||||||
smallest_size = possibilities.len();
|
smallest_cell = Some(cell_rc);
|
||||||
smallest_cell = Some(cell_rc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,12 +358,18 @@ impl Grid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Grid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Clone for Grid {
|
impl Clone for Grid {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
let mut new = Grid::new();
|
let mut new = Grid::new();
|
||||||
new.clone_from(&self);
|
new.clone_from(&self);
|
||||||
|
|
||||||
return new;
|
new
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_from(&mut self, source: &Self) {
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
@ -446,20 +442,17 @@ impl std::fmt::Display for Grid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "{}", row1)?;
|
writeln!(f, "{}", row1)?;
|
||||||
write!(f, "\n")?;
|
writeln!(f, "{}", row2)?;
|
||||||
write!(f, "{}", row2)?;
|
writeln!(f, "{}", row3)?;
|
||||||
write!(f, "\n")?;
|
|
||||||
write!(f, "{}", row3)?;
|
|
||||||
write!(f, "\n")?;
|
|
||||||
|
|
||||||
if (r % 3 == 2) && (r < 8) {
|
if (r % 3 == 2) && (r < 8) {
|
||||||
write!(f, "━━━┿━━━┿━━━╋━━━┿━━━┿━━━╋━━━┿━━━┿━━━\n")?;
|
writeln!(f, "━━━┿━━━┿━━━╋━━━┿━━━┿━━━╋━━━┿━━━┿━━━")?;
|
||||||
} else if r < 8 {
|
} else if r < 8 {
|
||||||
write!(f, "┄┄┄┼┄┄┄┼┄┄┄╂┄┄┄┼┄┄┄┼┄┄┄╂┄┄┄┼┄┄┄┼┄┄┄\n")?;
|
writeln!(f, "┄┄┄┼┄┄┄┼┄┄┄╂┄┄┄┼┄┄┄┼┄┄┄╂┄┄┄┼┄┄┄┼┄┄┄")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub fn draw_grid(
|
||||||
|
|
||||||
doc.save(&mut BufWriter::new(File::create(filename)?))?;
|
doc.save(&mut BufWriter::new(File::create(filename)?))?;
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_empty_grid(layer: &PdfLayerReference) {
|
fn draw_empty_grid(layer: &PdfLayerReference) {
|
||||||
|
|
179
src/solver.rs
179
src/solver.rs
|
@ -28,24 +28,19 @@ enum SolveAction {
|
||||||
impl SolveStatus {
|
impl SolveStatus {
|
||||||
fn increment(self, additional_status: SolveStatus) -> SolveStatus {
|
fn increment(self, additional_status: SolveStatus) -> SolveStatus {
|
||||||
match self {
|
match self {
|
||||||
SolveStatus::Complete(uniqueness_option) => {
|
SolveStatus::Complete(uniqueness_option) => match uniqueness_option {
|
||||||
if uniqueness_option.is_none() {
|
None => SolveStatus::Complete(None),
|
||||||
return SolveStatus::Complete(None);
|
Some(uniqueness_option) => match uniqueness_option {
|
||||||
} else {
|
Uniqueness::NotUnique => SolveStatus::Complete(Some(Uniqueness::NotUnique)),
|
||||||
match uniqueness_option.unwrap() {
|
Uniqueness::Unique => match additional_status {
|
||||||
Uniqueness::NotUnique => SolveStatus::Complete(Some(Uniqueness::NotUnique)),
|
SolveStatus::Complete(_) => {
|
||||||
Uniqueness::Unique => match additional_status {
|
SolveStatus::Complete(Some(Uniqueness::NotUnique))
|
||||||
SolveStatus::Complete(_) => {
|
}
|
||||||
SolveStatus::Complete(Some(Uniqueness::NotUnique))
|
SolveStatus::Unfinished => SolveStatus::Complete(Some(Uniqueness::Unique)),
|
||||||
}
|
SolveStatus::Invalid => SolveStatus::Complete(Some(Uniqueness::Unique)),
|
||||||
SolveStatus::Unfinished => {
|
},
|
||||||
SolveStatus::Complete(Some(Uniqueness::Unique))
|
},
|
||||||
}
|
},
|
||||||
SolveStatus::Invalid => SolveStatus::Complete(Some(Uniqueness::Unique)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SolveStatus::Unfinished => match additional_status {
|
SolveStatus::Unfinished => match additional_status {
|
||||||
SolveStatus::Invalid => SolveStatus::Unfinished,
|
SolveStatus::Invalid => SolveStatus::Unfinished,
|
||||||
_ => additional_status,
|
_ => additional_status,
|
||||||
|
@ -137,15 +132,21 @@ impl SolveStatistics {
|
||||||
|
|
||||||
fn increment(&mut self, action: &SolveAction) {
|
fn increment(&mut self, action: &SolveAction) {
|
||||||
match action {
|
match action {
|
||||||
SolveAction::Single => self.singles = self.singles + 1,
|
SolveAction::Single => self.singles += 1,
|
||||||
SolveAction::HiddenSingle => self.hidden_singles = self.hidden_singles + 1,
|
SolveAction::HiddenSingle => self.hidden_singles += 1,
|
||||||
SolveAction::PossibilityGroup => self.possibility_groups = self.possibility_groups + 1,
|
SolveAction::PossibilityGroup => self.possibility_groups += 1,
|
||||||
SolveAction::UsefulConstraints => self.useful_constraints = self.useful_constraints + 1,
|
SolveAction::UsefulConstraints => self.useful_constraints += 1,
|
||||||
SolveAction::Guess => self.guesses = self.guesses + 1,
|
SolveAction::Guess => self.guesses += 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for SolveStatistics {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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::{CellValue, Section};
|
use crate::grid::{CellValue, Section};
|
||||||
|
@ -233,7 +234,7 @@ mod process_possibility_groups {
|
||||||
CellValue::Unknown(possibilities) => {
|
CellValue::Unknown(possibilities) => {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
for (_index, digit) in possibilities.iter().enumerate() {
|
for (_index, digit) in possibilities.iter().enumerate() {
|
||||||
set.insert(digit.clone());
|
set.insert(*digit);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
}
|
}
|
||||||
|
@ -282,7 +283,7 @@ mod process_possibility_groups {
|
||||||
smallest_cell.in_group = true;
|
smallest_cell.in_group = true;
|
||||||
|
|
||||||
// Step 2
|
// Step 2
|
||||||
count = count + smallest_size;
|
count += smallest_size;
|
||||||
|
|
||||||
let possibilities_to_remove = smallest_cell.possibilities.clone(); // Necessary because of mutable borrow rules
|
let possibilities_to_remove = smallest_cell.possibilities.clone(); // Necessary because of mutable borrow rules
|
||||||
|
|
||||||
|
@ -313,11 +314,11 @@ mod process_possibility_groups {
|
||||||
for (_index, cell) in faux_line.0.iter().enumerate() {
|
for (_index, cell) in faux_line.0.iter().enumerate() {
|
||||||
if cell.in_group {
|
if cell.in_group {
|
||||||
cell.possibilities.iter().for_each(|digit| {
|
cell.possibilities.iter().for_each(|digit| {
|
||||||
in_group_possibilities.insert(digit.clone());
|
in_group_possibilities.insert(digit);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cell.possibilities.iter().for_each(|digit| {
|
cell.possibilities.iter().for_each(|digit| {
|
||||||
out_group_possibilities.insert(digit.clone());
|
out_group_possibilities.insert(digit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,12 +343,9 @@ mod process_possibility_groups {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (_i, possibility) in possibilities_to_remove.iter().enumerate() {
|
for (_i, possibility) in possibilities_to_remove.iter().enumerate() {
|
||||||
match possibilities.binary_search(possibility) {
|
if let Ok(x) = possibilities.binary_search(possibility) {
|
||||||
Ok(x) => {
|
possibilities.remove(x);
|
||||||
possibilities.remove(x);
|
}
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if possibilities.len() < starting_possibility_size {
|
if possibilities.len() < starting_possibility_size {
|
||||||
|
@ -384,7 +382,7 @@ mod process_possibility_groups {
|
||||||
bisect_possibility_groups(line, out_group_indices);
|
bisect_possibility_groups(line, out_group_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
return made_change;
|
made_change
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,19 +400,16 @@ fn search_single_possibility(line: &Section) -> bool {
|
||||||
let mut made_change = false;
|
let mut made_change = false;
|
||||||
|
|
||||||
for (_index, cell) in line.vec.iter().enumerate() {
|
for (_index, cell) in line.vec.iter().enumerate() {
|
||||||
match cell.get_value_possibilities() {
|
if let Some(x) = cell.get_value_possibilities() {
|
||||||
Some(x) => {
|
if x.len() == 1 {
|
||||||
if x.len() == 1 {
|
let new_value = CellValue::Fixed(*x.first().unwrap());
|
||||||
let new_value = CellValue::Fixed(x.first().unwrap().clone());
|
cell.set_value(new_value);
|
||||||
cell.set_value(new_value);
|
made_change = true;
|
||||||
made_change = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return made_change;
|
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
|
// 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
|
||||||
|
@ -423,7 +418,7 @@ fn search_hidden_single(line: &Section) -> bool {
|
||||||
None,
|
None,
|
||||||
One(Rc<Cell>),
|
One(Rc<Cell>),
|
||||||
Many,
|
Many,
|
||||||
};
|
}
|
||||||
|
|
||||||
impl Count {
|
impl Count {
|
||||||
fn increment(&self, cell: Rc<Cell>) -> Count {
|
fn increment(&self, cell: Rc<Cell>) -> Count {
|
||||||
|
@ -464,16 +459,13 @@ fn search_hidden_single(line: &Section) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (digit, count) in counts.iter().enumerate() {
|
for (digit, count) in counts.iter().enumerate() {
|
||||||
match count {
|
if let Count::One(cell) = count {
|
||||||
Count::One(cell) => {
|
cell.set((digit + 1) as u8);
|
||||||
cell.set((digit + 1) as u8);
|
made_change = true;
|
||||||
made_change = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return made_change;
|
made_change
|
||||||
}
|
}
|
||||||
|
|
||||||
mod search_useful_constraint {
|
mod search_useful_constraint {
|
||||||
|
@ -489,10 +481,7 @@ mod search_useful_constraint {
|
||||||
|
|
||||||
impl PossibilityLines {
|
impl PossibilityLines {
|
||||||
fn is_invalid(&self) -> bool {
|
fn is_invalid(&self) -> bool {
|
||||||
match &self {
|
matches!(&self, PossibilityLines::Invalid)
|
||||||
PossibilityLines::Invalid => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,45 +552,33 @@ mod search_useful_constraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check each line and see if we can determine anything
|
// Check each line and see if we can determine anything
|
||||||
match rows {
|
if let PossibilityLines::Unique(index) = rows {
|
||||||
PossibilityLines::Unique(index) => {
|
made_change |= remove_possibilities_line(
|
||||||
made_change = made_change
|
grid.rows.get(index).unwrap(),
|
||||||
| remove_possibilities_line(
|
possibility,
|
||||||
grid.rows.get(index).unwrap(),
|
&line.section_type,
|
||||||
possibility,
|
line.index,
|
||||||
&line.section_type,
|
);
|
||||||
line.index,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
match columns {
|
if let PossibilityLines::Unique(index) = columns {
|
||||||
PossibilityLines::Unique(index) => {
|
made_change |= remove_possibilities_line(
|
||||||
made_change = made_change
|
grid.columns.get(index).unwrap(),
|
||||||
| remove_possibilities_line(
|
possibility,
|
||||||
grid.columns.get(index).unwrap(),
|
&line.section_type,
|
||||||
possibility,
|
line.index,
|
||||||
&line.section_type,
|
);
|
||||||
line.index,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
match sections {
|
if let PossibilityLines::Unique(index) = sections {
|
||||||
PossibilityLines::Unique(index) => {
|
made_change |= remove_possibilities_line(
|
||||||
made_change = made_change
|
grid.sections.get(index).unwrap(),
|
||||||
| remove_possibilities_line(
|
possibility,
|
||||||
grid.sections.get(index).unwrap(),
|
&line.section_type,
|
||||||
possibility,
|
line.index,
|
||||||
&line.section_type,
|
);
|
||||||
line.index,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return made_change;
|
made_change
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -645,8 +622,7 @@ mod search_useful_constraint {
|
||||||
|
|
||||||
let new_value;
|
let new_value;
|
||||||
if new_possibilities.len() == 1 {
|
if new_possibilities.len() == 1 {
|
||||||
new_value =
|
new_value = CellValue::Fixed(*new_possibilities.first().unwrap());
|
||||||
CellValue::Fixed(new_possibilities.first().unwrap().clone());
|
|
||||||
} else {
|
} else {
|
||||||
new_value = CellValue::Unknown(new_possibilities);
|
new_value = CellValue::Unknown(new_possibilities);
|
||||||
}
|
}
|
||||||
|
@ -663,7 +639,7 @@ mod search_useful_constraint {
|
||||||
made_change = true;
|
made_change = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return made_change;
|
made_change
|
||||||
}
|
}
|
||||||
|
|
||||||
// We detected a useful constraint
|
// We detected a useful constraint
|
||||||
|
@ -777,7 +753,7 @@ pub fn solve_grid(grid: &mut Grid) -> (SolveStatus, SolveStatistics) {
|
||||||
let solve_status =
|
let solve_status =
|
||||||
solve_grid_with_solve_controller(grid, &solve_controller, &mut solve_statistics);
|
solve_grid_with_solve_controller(grid, &solve_controller, &mut solve_statistics);
|
||||||
|
|
||||||
return (solve_status, solve_statistics);
|
(solve_status, solve_statistics)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solves (and modifies) the input `Grid` & `SolveStatistics`. Returns a `SolveStatus`.
|
/// Solves (and modifies) the input `Grid` & `SolveStatistics`. Returns a `SolveStatus`.
|
||||||
|
@ -805,7 +781,7 @@ pub fn solve_grid_with_solve_controller(
|
||||||
_ => status,
|
_ => status,
|
||||||
};
|
};
|
||||||
|
|
||||||
return status;
|
status
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to `solve_grid_with_solve_controller` except that we don't modify the input Grid; we
|
/// Similar to `solve_grid_with_solve_controller` except that we don't modify the input Grid; we
|
||||||
|
@ -821,7 +797,7 @@ pub fn evaluate_grid_with_solve_controller(
|
||||||
let solve_status =
|
let solve_status =
|
||||||
solve_grid_with_solve_controller(&mut mut_grid, solve_controller, &mut solve_statistics);
|
solve_grid_with_solve_controller(&mut mut_grid, solve_controller, &mut solve_statistics);
|
||||||
|
|
||||||
return (solve_status, solve_statistics);
|
(solve_status, solve_statistics)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_grid_no_guess(
|
fn solve_grid_no_guess(
|
||||||
|
@ -867,12 +843,12 @@ fn solve_grid_no_guess(
|
||||||
for y in 0..9 {
|
for y in 0..9 {
|
||||||
let cell = grid.get(x, y).unwrap();
|
let cell = grid.get(x, y).unwrap();
|
||||||
let cell = &*cell;
|
let cell = &*cell;
|
||||||
let value = &**(&cell.value.borrow());
|
let value = &*cell.value.borrow();
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
CellValue::Unknown(possibilities) => {
|
CellValue::Unknown(possibilities) => {
|
||||||
appears_complete = false;
|
appears_complete = false;
|
||||||
if possibilities.len() == 0 {
|
if possibilities.is_empty() {
|
||||||
return SolveStatus::Invalid;
|
return SolveStatus::Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -917,11 +893,8 @@ fn solve_grid_guess(
|
||||||
solve_grid_with_solve_controller(&mut grid_copy, solve_controller, solve_statistics);
|
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
|
// Keep a copy of grid_copy in case we later mutate grid with it
|
||||||
match status {
|
if let SolveStatus::Complete(_) = status {
|
||||||
SolveStatus::Complete(_) => {
|
grid_solution = Some(grid_copy);
|
||||||
grid_solution = Some(grid_copy);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
current_status = current_status.increment(status);
|
current_status = current_status.increment(status);
|
||||||
|
@ -963,7 +936,7 @@ fn solve_grid_guess(
|
||||||
SolveStatus::Invalid => {}
|
SolveStatus::Invalid => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_status;
|
current_status
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in a new issue