Add ability to specify puzzle difficulty and save result to CSV file
This commit is contained in:
parent
3c845aec3f
commit
7b881ff137
2 changed files with 79 additions and 9 deletions
|
@ -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() {
|
fn main() {
|
||||||
|
|
||||||
|
@ -5,6 +10,10 @@ fn main() {
|
||||||
// Starting default seed will just be based on time
|
// 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 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<String> = None;
|
||||||
|
|
||||||
{ // this block limits scope of borrows by ap.refer() method
|
{ // this block limits scope of borrows by ap.refer() method
|
||||||
let mut ap = argparse::ArgumentParser::new();
|
let mut ap = argparse::ArgumentParser::new();
|
||||||
ap.set_description("Generate Sudoku puzzles");
|
ap.set_description("Generate Sudoku puzzles");
|
||||||
|
@ -14,6 +23,15 @@ fn main() {
|
||||||
ap.refer(&mut seed)
|
ap.refer(&mut seed)
|
||||||
.add_option(&["--seed"], argparse::Store, "Provide seed for puzzle generation");
|
.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();
|
ap.parse_args_or_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +46,62 @@ fn main() {
|
||||||
if debug {
|
if debug {
|
||||||
println!("Using seed {}", seed);
|
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;
|
||||||
|
|
||||||
|
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!("{}", grid);
|
||||||
println!("Puzzle has {} hints", num_hints);
|
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<dyn Error>>{
|
||||||
|
// 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(())
|
||||||
}
|
}
|
|
@ -152,8 +152,7 @@ impl Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_grid(seed: u64) -> (Grid, i32) {
|
pub fn generate_grid(rng: &mut ChaCha8Rng) -> (Grid, i32) {
|
||||||
let mut rng = ChaCha8Rng::seed_from_u64(seed);
|
|
||||||
|
|
||||||
let mut num_hints;
|
let mut num_hints;
|
||||||
let mut grid : Grid = loop {
|
let mut grid : Grid = loop {
|
||||||
|
@ -167,7 +166,7 @@ pub fn generate_grid(seed: u64) -> (Grid, i32) {
|
||||||
|
|
||||||
for digit in 1..10 {
|
for digit in 1..10 {
|
||||||
if digit != digit_excluded {
|
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);
|
cell.unwrap().set(digit);
|
||||||
num_hints = num_hints + 1;
|
num_hints = num_hints + 1;
|
||||||
}
|
}
|
||||||
|
@ -189,12 +188,12 @@ pub fn generate_grid(seed: u64) -> (Grid, i32) {
|
||||||
grid =
|
grid =
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
num_hints = num_hints + 1;
|
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 cell = &*cell;
|
||||||
let mut cell_possibilities = cell.get_value_possibilities().expect("An empty cell has no possibilities");
|
let mut cell_possibilities = cell.get_value_possibilities().expect("An empty cell has no possibilities");
|
||||||
|
|
||||||
// Let's scramble the order
|
// Let's scramble the order
|
||||||
cell_possibilities.shuffle(&mut rng);
|
cell_possibilities.shuffle(rng);
|
||||||
|
|
||||||
for (_index, digit) in cell_possibilities.iter().enumerate() {
|
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
|
// 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() {
|
for (_index, cell) in non_empty_cells.iter().enumerate() {
|
||||||
let grid_clone = grid.clone();
|
let grid_clone = grid.clone();
|
||||||
|
|
Loading…
Reference in a new issue