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};
|
use clap::{Parser};
|
||||||
|
|
||||||
mod solver;
|
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)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::time::Instant;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::Step;
|
|
||||||
|
|
||||||
pub mod sudoku;
|
pub mod sudoku;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
mod cell;
|
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 {
|
pub struct Solver {
|
||||||
grid: Vec<Vec<cell::Cell>>,
|
grid: Vec<Vec<cell::Cell>>,
|
||||||
history: Vec<Step>,
|
history: Vec<Step>,
|
||||||
|
@ -49,26 +61,108 @@ impl Solver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_allowed(&mut self, position: (usize, usize), blocking_cells: &Vec<cell::BlockingCell>) -> Result<(), WaveError> {
|
pub fn random_mode(&mut self, collapse_random: bool) {
|
||||||
match self.grid[position.0][position.1].remove_allowed(blocking_cells) {
|
if collapse_random {
|
||||||
Ok(result) => {
|
self.collapse_option = cell::CollapseOption::Random;
|
||||||
let cell::RemoveResult::Collapsed(state_set) = result else {
|
} else {
|
||||||
return Ok(())
|
self.collapse_option = cell::CollapseOption::First
|
||||||
};
|
|
||||||
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 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> {
|
fn collapse_cell(&mut self, position: (usize, usize)) -> Result<(), WaveError> {
|
||||||
match self.grid[position.0][position.1].collapse(&self.collapse_option) {
|
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> {
|
fn backtrack(&mut self) -> Result<(), WaveError> {
|
||||||
let mut fork: Option<Step> = None;
|
let mut fork: Option<Step> = None;
|
||||||
while let Some(step) = self.history.pop() {
|
while let Some(step) = self.history.pop() {
|
||||||
|
@ -130,31 +216,24 @@ impl Solver {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collapse(&mut self) -> Result<(), WaveError> {
|
fn remove_allowed(&mut self, position: (usize, usize), blocking_cells: &Vec<cell::BlockingCell>) -> Result<(), WaveError> {
|
||||||
let mut min_allowed_position: (usize, usize) = (0, 0);
|
match self.grid[position.0][position.1].remove_allowed(blocking_cells) {
|
||||||
let mut min_allowed_number: usize = self.size;
|
Ok(result) => {
|
||||||
|
let cell::RemoveResult::Collapsed(state_set) = result else {
|
||||||
let mut grid_has_empty_cell: bool = false;
|
return Ok(())
|
||||||
|
};
|
||||||
for row_index in 0..self.size {
|
self.debug(&format!("* collapsed by removal ({},{}) to {}", position.0, position.1, state_set));
|
||||||
for column_index in 0..self.size {
|
self.history.push(Step {
|
||||||
if !self.grid[row_index][column_index].is_none() {
|
position,
|
||||||
continue;
|
state_set,
|
||||||
}
|
num_allowed_states: 1,
|
||||||
grid_has_empty_cell = true;
|
});
|
||||||
let possibilities_len = self.grid[row_index][column_index].get_num_allowed();
|
return Ok(())
|
||||||
if possibilities_len < min_allowed_number {
|
}
|
||||||
min_allowed_position = (row_index, column_index);
|
Err(reason) => {
|
||||||
min_allowed_number = possibilities_len;
|
self.debug(&format!("x failed to update states allowed of ({},{}) : {}", position.0, position.1, reason));
|
||||||
}
|
return Err(WaveError::Contradiction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if !grid_has_empty_cell {
|
|
||||||
self.debug("x no empty cells");
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.collapse_cell(min_allowed_position)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use std::time::Instant;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::Solver;
|
use super::Solver;
|
||||||
use super::ui::DisplayMode;
|
|
||||||
use super::WaveError;
|
use super::WaveError;
|
||||||
use super::cell;
|
use super::cell;
|
||||||
|
|
||||||
impl Solver {
|
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 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 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];
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_collapse(&mut self) -> Result<(), WaveError> {
|
pub fn propagate_collapse(&mut self) -> Result<(), WaveError> {
|
||||||
if self.last_move_index >= self.history.len() {
|
if self.last_move_index >= self.history.len() {
|
||||||
self.debug(&format!("x nothing to propagate"));
|
self.debug(&format!("x nothing to propagate"));
|
||||||
return Ok(())
|
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