diff --git a/Cargo.lock b/Cargo.lock index d27500e..ada5efe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,55 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -14,6 +63,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "getrandom" version = "0.2.15" @@ -25,6 +120,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "libc" version = "0.2.166" @@ -88,10 +195,17 @@ dependencies = [ "getrandom", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "sudoku" version = "0.1.0" dependencies = [ + "clap", "rand", "text_io", ] @@ -119,12 +233,91 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index eaf2d7a..4dc8104 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4.5.21", features = ["derive"] } rand = "0.8.5" text_io = "0.1.12" diff --git a/src/cell.rs b/src/cell.rs index d93c51b..ca6cd6e 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -105,14 +105,20 @@ impl Cell { } return Ok(RemoveResult::NumAllowed(self.allowed_states.len())) } + + pub fn add_allowed(&mut self, state: usize) { + if self.allowed_states.contains(&state) || !self.all_states.contains(&state) { + return + } + self.allowed_states.push(state); + } pub fn reset_allowed(&mut self) { self.allowed_states = self.all_states.clone(); } - pub fn reset(&mut self) { + pub fn reset_state(&mut self) { self.state = None; - self.reset_allowed(); } } diff --git a/src/main.rs b/src/main.rs index be498df..4160eca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::collections::HashSet; use std::time::Instant; use std::env; use std::process; +use clap::{Parser}; mod cell; use cell::Cell; @@ -38,6 +39,7 @@ struct Sudoku { size: usize, square_size: usize, debug_display: bool, + grid_display: bool, collapse_option: CollapseOption, } @@ -53,6 +55,7 @@ impl Sudoku { size, square_size: order, debug_display: false, + grid_display: false, collapse_option: CollapseOption::Random, } } @@ -226,23 +229,14 @@ impl Sudoku { } return self.collapse_cell(min_row_index, min_column_index) -// let Err(reason) = self.collapse_cell(min_row_index, min_column_index) else { -// return Ok(()) -// }; -// -// if let WaveError::Contradiction = reason { -// return self.backtrack() -// } else { -// return Err(reason) -// } } -// + fn backtrack(&mut self) -> Result<(), WaveError> { let mut fork: Option = None; - while let Some(step) = self.history.pop() { self.last_history -= 1; - self.grid[step.cell_selected[0]][step.cell_selected[1]].reset(); + self.grid[step.cell_selected[0]][step.cell_selected[1]].reset_state(); + self.propagate_backtrack(step.cell_selected, step.state_selected); if step.num_allowed_states > 1 { fork = Some(step); break; @@ -262,14 +256,42 @@ impl Sudoku { println!("* fork [{}][{}] : {}", step.cell_selected[0], step.cell_selected[1], step.state_selected); } - self.reset_allowed(); + //self.reset_allowed(); let mut state_selected_set = HashSet::new(); state_selected_set.insert(step.state_selected); self.remove_allowed(step.cell_selected[0], step.cell_selected[1], &state_selected_set)?; + if self.debug_display { + println!(" - removed : {}, available : {:?}", step.state_selected, self.grid[step.cell_selected[0]][step.cell_selected[1]].get_allowed()); + } Ok(()) } + + fn propagate_backtrack(&mut self, cell_pos: [usize; 2], removed_state: usize) { + for row_index in 0..self.size { + if row_index == cell_pos[0] { + continue + } + self.grid[row_index][cell_pos[1]].add_allowed(removed_state); + } + for column_index in 0..self.size { + if column_index == cell_pos[1] { + continue + } + self.grid[cell_pos[0]][column_index].add_allowed(removed_state); + } + for row_index in 0..self.square_size { + for column_index in 0..self.square_size { + let row = (cell_pos[0]/self.square_size)*self.square_size + row_index; + let column = (cell_pos[1]/self.square_size)*self.square_size + column_index; + if row == cell_pos[0] && column == cell_pos[1] { + continue + } + self.grid[row][column].add_allowed(removed_state); + } + } + } fn reset_allowed(&mut self) { for row in &mut self.grid { @@ -326,67 +348,8 @@ impl Sudoku { } } } -// -// fn solve(&mut self) -> Result<(), WaveError> { -// let now = Instant::now(); -// -// self.display(DisplayMode::Full); -// if self.debug_display { -// println!("--------"); -// } -// -// -// let initial_grid = self.grid.clone(); -// -// println!("# started"); -// -// self.update_possibilities()?; -// -// let mut step_counter: usize = 0; -// let mut reset_counter: usize = 0; -// -// loop { -// match self.collapse() { -// Ok(()) => {} -// Err(reason) => { -// if let WaveError::NoEmptyCell = reason { -// break; -// } else { -// return Err(reason) -// } -// } -// } -// self.propagate_collapse()?; -// -// -// if step_counter%(2*self.size*self.size) == 0 { -// self.grid = initial_grid.clone(); -// self.reset_allowed(); -// self.history = vec![]; -// self.update_possibilities()?; -// reset_counter += 1; -// } -// -// if !self.debug_display { -// let bar_size = (self.size + self.square_size - 1)*2 + 1; -// let progress = self.history.len()*bar_size/(self.size*self.size - filled_cells_number); -// let to_do = bar_size - progress; -// print!("\r[{}{}]", "#".repeat(progress), "-".repeat(to_do)); -// } -// step_counter += 1; -// } -// -// println!(); -// -// if reset_counter > 0 { -// println!("# {} resets", reset_counter); -// } -// self.display(DisplayMode::Full); -// -// Ok(()) -// } - fn solve(&mut self) -> Result<(), WaveError> { + fn solve(&mut self, solver_limit: Option) -> Result<(), WaveError> { let now = Instant::now(); self.display(DisplayMode::Full); let mut n_start_cells: usize = 0; @@ -407,6 +370,7 @@ impl Sudoku { while n_start_cells + self.history.len() < self.size * self.size { if self.debug_display { + println!(); println!("## while, h={}/{}", self.last_history, self.history.len()); } while self.last_history < self.history.len() && n_start_cells + self.history.len() < self.size * self.size { @@ -424,28 +388,46 @@ impl Sudoku { while backtrack > 0 { backtrack -=1; self.backtrack()?; - match self.update_possibilities() { - Ok(_) => {}, - Err(reason) => { - if let WaveError::Contradiction = reason { - backtrack += 1; - } else { - return Err(reason) - } - } - }; + // match self.update_possibilities() { + // Ok(_) => {}, + // Err(reason) => { + // if let WaveError::Contradiction = reason { + // backtrack += 1; + // } else { + // return Err(reason) + // } + // } + // }; } propagation_counter += 1; } + + if self.grid_display { + self.display(DisplayMode::Full); + } + self.collapse()?; collapse_counter += 1; - if !self.debug_display { + if !self.debug_display && !self.grid_display { let bar_size = (self.size + self.square_size - 1)*2 + 1; let progress = self.history.len()*bar_size/(self.size*self.size - n_start_cells); let to_do = bar_size - progress; print!("\r[{}{}]", "#".repeat(progress), "-".repeat(to_do)); } + + if let Some(limit) = solver_limit { + if collapse_counter >= limit { + for cell in &self.grid[13] { + if let Some(state) = cell.get() { + println!("state: '{}'", state); + continue + } + println!("allowed : '{:?}'", cell.get_allowed()); + } + break; + } + } } println!(); @@ -467,36 +449,51 @@ impl Sudoku { } } -fn main() { - let args: Vec = env::args().collect(); - if args.len() < 2 { - eprintln!("Usage: {} [--debug] [--ask]", args[0]); - process::exit(1); - } +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// The size of the Sudoku grid (order) + #[arg()] + size: usize, - let size: usize = args[1].parse().unwrap_or_else(|_| { - eprintln!("Invalid size argument"); - process::exit(1); - }); + /// Enable debug mode + #[arg(long)] + debug: bool, - let mut sudoku = Sudoku::new(size); + /// Enable ask mode + #[arg(long)] + ask: bool, - if args.contains(&"--ask".to_string()) { - if let Err(reason) = sudoku.ask() { - println!("{}",reason); - } - } + /// Set a limit for the solver + #[arg(long)] + limit: Option, // Optional value - if args.contains(&"--debug".to_string()) { - sudoku.debug_mode(true); - } + /// Disable randomization of the collapse + #[arg(long)] + norand: bool, - if args.contains(&"--norand".to_string()) { - sudoku.random_mode(false); - } - - if let Err(reason) = sudoku.solve() { - println!("{}",reason); - sudoku.display(DisplayMode::Full); - } + /// Display grid when solving + #[arg(long)] + grid: bool, +} + + +fn main() { + let args = Args::parse(); + + let mut sudoku = Sudoku::new(args.size); + + if args.ask { + if let Err(reason) = sudoku.ask() { + println!("{}", reason); + } + } + sudoku.debug_mode(args.debug); + sudoku.random_mode(!args.norand); + sudoku.grid_display_mode(args.grid); + + if let Err(reason) = sudoku.solve(args.limit) { + println!("{}", reason); + sudoku.display(DisplayMode::Full); + } } diff --git a/src/ui.rs b/src/ui.rs index ad6b100..f90f4bc 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -87,4 +87,7 @@ impl Sudoku { pub fn debug_mode(&mut self, debug_display: bool) { self.debug_display = debug_display; } + pub fn grid_display_mode(&mut self, grid_display: bool) { + self.grid_display = grid_display; + } } diff --git a/todo b/todo new file mode 100644 index 0000000..c9a2301 --- /dev/null +++ b/todo @@ -0,0 +1,9 @@ +more check for impossible + + +cargo run 4 --grid --debug --ask --norand --limit 220 +--d-fc-g + +error : +when backtracking, always adding the state back is wrong +(maybe it was not there)