From 7b881ff1379be605648d5074b7db0b061ff8b605 Mon Sep 17 00:00:00 2001 From: Joel Therrien Date: Tue, 22 Sep 2020 19:30:38 -0700 Subject: [PATCH] Add ability to specify puzzle difficulty and save result to CSV file --- src/bin/generator.rs | 77 ++++++++++++++++++++++++++++++++++++++++++-- src/generator.rs | 11 +++---- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/bin/generator.rs b/src/bin/generator.rs index 7a92715..fb36309 100644 --- a/src/bin/generator.rs +++ b/src/bin/generator.rs @@ -1,3 +1,8 @@ +use rand_chacha::ChaCha8Rng; +use rand::prelude::*; +use sudoku_solver::grid::{Grid, CellValue}; +use std::error::Error; +use std::io::Write; fn main() { @@ -5,6 +10,10 @@ fn main() { // Starting default seed will just be based on time let mut seed = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).expect("Time went backwards").as_secs(); + let mut max_hints = 81; + let mut max_attempts = 100; + let mut filename : Option = None; + { // this block limits scope of borrows by ap.refer() method let mut ap = argparse::ArgumentParser::new(); ap.set_description("Generate Sudoku puzzles"); @@ -14,6 +23,15 @@ fn main() { ap.refer(&mut seed) .add_option(&["--seed"], argparse::Store, "Provide seed for puzzle generation"); + ap.refer(&mut max_hints) + .add_option(&["--hints"], argparse::Store, "Only return a puzzle with less than or equal to this number of hints"); + + ap.refer(&mut max_attempts) + .add_option(&["--attempts"], argparse::Store, "Number of attempts that will be tried to generate such a puzzle; default is 100"); + + ap.refer(&mut filename) + .add_argument("filename", argparse::StoreOption, "Optional filename to store puzzle in as a CSV"); + ap.parse_args_or_exit(); } @@ -28,9 +46,62 @@ fn main() { if debug { println!("Using seed {}", seed); } + let mut rng = ChaCha8Rng::seed_from_u64(seed); - let (grid, num_hints) = sudoku_solver::generator::generate_grid(seed); + let mut num_attempts = 0; - println!("{}", grid); - println!("Puzzle has {} hints", num_hints); + let grid = loop { + if num_attempts >= max_attempts{ + println!("Unable to find a puzzle with only {} hints in {} attempts", max_hints, max_attempts); + return; + } + + let (grid, num_hints) = sudoku_solver::generator::generate_grid(&mut rng); + num_attempts = num_attempts + 1; + + if num_hints <= max_hints { + println!("{}", grid); + println!("Puzzle has {} hints", num_hints); + if num_attempts > 1 { + println!("It took {} attempts to find this puzzle.", num_attempts); + } + break grid; + } + }; + + match filename { + Some(filename) => { + save_grid(&grid, &filename).unwrap(); + println!("Grid saved to {}", filename); + }, + None => {} + } + +} + +fn save_grid(grid: &Grid, filename: &str) -> Result<(), Box>{ + // Not using the csv crate for writing because it's being difficult and won't accept raw integers + let mut file = std::fs::File::create(filename)?; + + for x in 0..9 { + for y in 0..9 { + let cell = grid.get(x, y).unwrap(); + let value = &*cell.value.borrow(); + let digit = + match value { + CellValue::Fixed(digit) => {*digit} + CellValue::Unknown(_) => {0} + }; + + let mut text = digit.to_string(); + if y < 8 { + text.push(','); + } + file.write(text.as_bytes())?; + + } + file.write(b"\n")?; + } + + Ok(()) } \ No newline at end of file diff --git a/src/generator.rs b/src/generator.rs index bbe307f..e35d0d5 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -152,8 +152,7 @@ impl Line { } } -pub fn generate_grid(seed: u64) -> (Grid, i32) { - let mut rng = ChaCha8Rng::seed_from_u64(seed); +pub fn generate_grid(rng: &mut ChaCha8Rng) -> (Grid, i32) { let mut num_hints; let mut grid : Grid = loop { @@ -167,7 +166,7 @@ pub fn generate_grid(seed: u64) -> (Grid, i32) { for digit in 1..10 { if digit != digit_excluded { - let cell = grid.get_random_empty_cell(&mut rng); + let cell = grid.get_random_empty_cell(rng); cell.unwrap().set(digit); num_hints = num_hints + 1; } @@ -189,12 +188,12 @@ pub fn generate_grid(seed: u64) -> (Grid, i32) { grid = 'outer: loop { num_hints = num_hints + 1; - let cell = grid.get_random_empty_cell(&mut rng).unwrap(); // We unwrap because if somehow we're filled each cell without finding a solution, that's reason for a panic + let cell = grid.get_random_empty_cell(rng).unwrap(); // We unwrap because if somehow we're filled each cell without finding a solution, that's reason for a panic let cell = &*cell; let mut cell_possibilities = cell.get_value_possibilities().expect("An empty cell has no possibilities"); // Let's scramble the order - cell_possibilities.shuffle(&mut rng); + cell_possibilities.shuffle(rng); for (_index, digit) in cell_possibilities.iter().enumerate() { @@ -244,7 +243,7 @@ pub fn generate_grid(seed: u64) -> (Grid, i32) { } } // Need to randomly reorder non_empty_cells - non_empty_cells.shuffle(&mut rng); + non_empty_cells.shuffle(rng); for (_index, cell) in non_empty_cells.iter().enumerate() { let grid_clone = grid.clone();