diff --git a/src/main.rs b/src/main.rs index 4e48aa0..ae15aec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,7 @@ -use std::fmt; use clap::{Parser}; mod solver; -#[derive(Debug, Clone)] -struct Step { - position: (usize, usize), - state_set: usize, - num_allowed_states: usize, -} - -impl fmt::Display for Step { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({},{}) to {}, out of {}", self.position.0, self.position.1, self.state_set, self.num_allowed_states) - } -} - - - #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { diff --git a/src/solver/mod.rs b/src/solver/mod.rs index 99ee6b5..d281525 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -1,8 +1,7 @@ use std::collections::HashSet; +use std::time::Instant; use std::fmt; -use crate::Step; - pub mod sudoku; pub mod ui; mod cell; @@ -21,6 +20,19 @@ impl fmt::Display for WaveError { } } +#[derive(Debug, Clone)] +struct Step { + position: (usize, usize), + state_set: usize, + num_allowed_states: usize, +} + +impl fmt::Display for Step { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({},{}) to {}, out of {}", self.position.0, self.position.1, self.state_set, self.num_allowed_states) + } +} + pub struct Solver { grid: Vec>, history: Vec, @@ -48,27 +60,109 @@ impl Solver { collapse_option: cell::CollapseOption::Random, } } - - fn remove_allowed(&mut self, position: (usize, usize), blocking_cells: &Vec) -> Result<(), WaveError> { - match self.grid[position.0][position.1].remove_allowed(blocking_cells) { - Ok(result) => { - let cell::RemoveResult::Collapsed(state_set) = result else { - return Ok(()) - }; - self.debug(&format!("* collapsed by removal ({},{}) to {}", position.0, position.1, state_set)); - self.history.push(Step { - position, - state_set, - num_allowed_states: 1, - }); - return Ok(()) - } - Err(reason) => { - self.debug(&format!("x failed to update states allowed of ({},{}) : {}", position.0, position.1, reason)); - return Err(WaveError::Contradiction) - } + + pub fn random_mode(&mut self, collapse_random: bool) { + if collapse_random { + self.collapse_option = cell::CollapseOption::Random; + } else { + self.collapse_option = cell::CollapseOption::First } - } + } + + pub fn solve(&mut self, solver_limit: Option) -> Result<(), WaveError> { + let now = Instant::now(); + self.display(ui::DisplayMode::Full); + let mut number_cell_init: usize = 0; + for row in &self.grid { + for cell in row { + if !cell.is_none() { + number_cell_init += 1; + } + } + } + let mut propagation_counter: usize = 0; + let mut collapse_counter: usize = 0; + println!("# started"); + self.debug("--------"); + self.update_possibilities()?; + + while number_cell_init + self.history.len() < self.size * self.size { + self.debug(&format!("\n## while, h={}/{}", self.last_move_index, self.history.len())); + while self.last_move_index < self.history.len() && number_cell_init + self.history.len() < self.size * self.size { + let mut backtrack = 0; + match self.propagate_collapse() { + Ok(_) => {}, + Err(reason) => { + if let WaveError::Contradiction = reason { + backtrack = 1; + } else { + return Err(reason) + } + } + }; + while backtrack > 0 { + backtrack -=1; + self.backtrack()?; + } + propagation_counter += 1; + } + + if self.grid_display { + if !self.debug_display { + self.display(ui::DisplayMode::Erase); + } else { + self.display(ui::DisplayMode::Full); + } + } + + self.collapse()?; + collapse_counter += 1; + + if !self.debug_display && !self.grid_display { + self.progress_bar(number_cell_init); + } + if let Some(limit) = solver_limit { + if collapse_counter >= limit { + break; + } + } + } + self.debug("--------"); + let elapsed = now.elapsed(); + println!("\n# finished in {} propagations ({} forced collapse), {:.2?} ({:.2?}/propagation)", propagation_counter, collapse_counter, elapsed, elapsed/(propagation_counter as u32)); + if !self.grid_display || self.debug_display { + self.display(ui::DisplayMode::Full); + } + Ok(()) + } + + fn collapse(&mut self) -> Result<(), WaveError> { + let mut min_allowed_position: (usize, usize) = (0, 0); + let mut min_allowed_number: usize = self.size; + + let mut grid_has_empty_cell: bool = false; + + for row_index in 0..self.size { + for column_index in 0..self.size { + if !self.grid[row_index][column_index].is_none() { + continue; + } + grid_has_empty_cell = true; + let possibilities_len = self.grid[row_index][column_index].get_num_allowed(); + if possibilities_len < min_allowed_number { + min_allowed_position = (row_index, column_index); + min_allowed_number = possibilities_len; + } + } + } + + if !grid_has_empty_cell { + self.debug("x no empty cells"); + return Ok(()) + } + + return self.collapse_cell(min_allowed_position) + } fn collapse_cell(&mut self, position: (usize, usize)) -> Result<(), WaveError> { match self.grid[position.0][position.1].collapse(&self.collapse_option) { @@ -85,14 +179,6 @@ impl Solver { } } - pub fn random_mode(&mut self, collapse_random: bool) { - if collapse_random { - self.collapse_option = cell::CollapseOption::Random; - } else { - self.collapse_option = cell::CollapseOption::First - } - } - fn backtrack(&mut self) -> Result<(), WaveError> { let mut fork: Option = None; while let Some(step) = self.history.pop() { @@ -130,31 +216,24 @@ impl Solver { Ok(()) } - fn collapse(&mut self) -> Result<(), WaveError> { - let mut min_allowed_position: (usize, usize) = (0, 0); - let mut min_allowed_number: usize = self.size; - - let mut grid_has_empty_cell: bool = false; - - for row_index in 0..self.size { - for column_index in 0..self.size { - if !self.grid[row_index][column_index].is_none() { - continue; - } - grid_has_empty_cell = true; - let possibilities_len = self.grid[row_index][column_index].get_num_allowed(); - if possibilities_len < min_allowed_number { - min_allowed_position = (row_index, column_index); - min_allowed_number = possibilities_len; - } - } - } - - if !grid_has_empty_cell { - self.debug("x no empty cells"); - return Ok(()) - } - - return self.collapse_cell(min_allowed_position) - } + fn remove_allowed(&mut self, position: (usize, usize), blocking_cells: &Vec) -> Result<(), WaveError> { + match self.grid[position.0][position.1].remove_allowed(blocking_cells) { + Ok(result) => { + let cell::RemoveResult::Collapsed(state_set) = result else { + return Ok(()) + }; + self.debug(&format!("* collapsed by removal ({},{}) to {}", position.0, position.1, state_set)); + self.history.push(Step { + position, + state_set, + num_allowed_states: 1, + }); + return Ok(()) + } + Err(reason) => { + self.debug(&format!("x failed to update states allowed of ({},{}) : {}", position.0, position.1, reason)); + return Err(WaveError::Contradiction) + } + } + } } diff --git a/src/solver/sudoku.rs b/src/solver/sudoku.rs index e453976..9d00298 100644 --- a/src/solver/sudoku.rs +++ b/src/solver/sudoku.rs @@ -1,13 +1,11 @@ -use std::time::Instant; use std::collections::HashSet; use super::Solver; -use super::ui::DisplayMode; use super::WaveError; use super::cell; impl Solver { - fn update_possibilities(&mut self) -> Result<(), WaveError> { + pub fn update_possibilities(&mut self) -> Result<(), WaveError> { let mut row_used_states: Vec> = vec![vec![]; self.size]; let mut column_used_states: Vec> = vec![vec![]; self.size]; let mut square_used_states: Vec>> = vec![vec![vec![]; self.square_size]; self.square_size]; @@ -40,7 +38,7 @@ impl Solver { Ok(()) } - fn propagate_collapse(&mut self) -> Result<(), WaveError> { + pub fn propagate_collapse(&mut self) -> Result<(), WaveError> { if self.last_move_index >= self.history.len() { self.debug(&format!("x nothing to propagate")); return Ok(()) @@ -124,71 +122,4 @@ impl Solver { } } } - - pub fn solve(&mut self, solver_limit: Option) -> Result<(), WaveError> { - let now = Instant::now(); - self.display(DisplayMode::Full); - let mut number_cell_init: usize = 0; - for row in &self.grid { - for cell in row { - if !cell.is_none() { - number_cell_init += 1; - } - } - } - let mut propagation_counter: usize = 0; - let mut collapse_counter: usize = 0; - println!("# started"); - self.debug("--------"); - self.update_possibilities()?; - - while number_cell_init + self.history.len() < self.size * self.size { - self.debug(&format!("\n## while, h={}/{}", self.last_move_index, self.history.len())); - while self.last_move_index < self.history.len() && number_cell_init + self.history.len() < self.size * self.size { - let mut backtrack = 0; - match self.propagate_collapse() { - Ok(_) => {}, - Err(reason) => { - if let WaveError::Contradiction = reason { - backtrack = 1; - } else { - return Err(reason) - } - } - }; - while backtrack > 0 { - backtrack -=1; - self.backtrack()?; - } - propagation_counter += 1; - } - - if self.grid_display { - if !self.debug_display { - self.display(DisplayMode::Erase); - } else { - self.display(DisplayMode::Full); - } - } - - self.collapse()?; - collapse_counter += 1; - - if !self.debug_display && !self.grid_display { - self.progress_bar(number_cell_init); - } - if let Some(limit) = solver_limit { - if collapse_counter >= limit { - break; - } - } - } - self.debug("--------"); - let elapsed = now.elapsed(); - println!("\n# finished in {} propagations ({} forced collapse), {:.2?} ({:.2?}/propagation)", propagation_counter, collapse_counter, elapsed, elapsed/(propagation_counter as u32)); - if !self.grid_display || self.debug_display { - self.display(DisplayMode::Full); - } - Ok(()) - } }