split clean
This commit is contained in:
parent
2678a5d957
commit
3e3245a977
16
src/main.rs
16
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 {
|
||||
|
|
|
@ -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<Vec<cell::Cell>>,
|
||||
history: Vec<Step>,
|
||||
|
@ -48,27 +60,109 @@ impl Solver {
|
|||
collapse_option: cell::CollapseOption::Random,
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_allowed(&mut self, position: (usize, usize), blocking_cells: &Vec<cell::BlockingCell>) -> 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<usize>) -> 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<Step> = 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<cell::BlockingCell>) -> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<cell::BlockingCell>> = vec![vec![]; self.size];
|
||||
let mut column_used_states: Vec<Vec<cell::BlockingCell>> = vec![vec![]; self.size];
|
||||
let mut square_used_states: Vec<Vec<Vec<cell::BlockingCell>>> = 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<usize>) -> 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(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue