Add alternate style of puzzle generation

This commit is contained in:
Joel Therrien 2023-01-22 18:56:42 -08:00
parent 031bdf6cd9
commit e69db652d4
2 changed files with 109 additions and 4 deletions

View file

@ -98,6 +98,7 @@ fn main() {
let mut difficulty = Difficulty::Hard; let mut difficulty = Difficulty::Hard;
let mut threads = 1; let mut threads = 1;
let mut print_possibilities = false; let mut print_possibilities = false;
let mut alternative_generation = false;
{ {
// this block limits scope of borrows by ap.refer() method // this block limits scope of borrows by ap.refer() method
@ -141,14 +142,25 @@ fn main() {
); );
ap.refer(&mut print_possibilities).add_option( ap.refer(&mut print_possibilities).add_option(
&["-p", "--possibilities"], &["-p", "--pencils"],
argparse::StoreTrue, argparse::StoreTrue,
"Include each cell's possibilities in the output; applies only to PDF output", "Include each cell's pencil marks in the output; applies only to PDF output",
);
ap.refer(&mut alternative_generation).add_option(
&["--alt"],
argparse::StoreTrue,
"Generate using an alternative style of generation",
); );
ap.parse_args_or_exit(); ap.parse_args_or_exit();
} }
print_possibilities = print_possibilities | alternative_generation;
if alternative_generation {
max_hints = 81*9;
}
let max_attempts = max_attempts.unwrap_or(100 * number_puzzles); let max_attempts = max_attempts.unwrap_or(100 * number_puzzles);
let solve_controller = difficulty.map_to_solve_controller(); let solve_controller = difficulty.map_to_solve_controller();
@ -168,6 +180,7 @@ fn main() {
&AtomicI64::new(number_puzzles as i64), &AtomicI64::new(number_puzzles as i64),
&AtomicI64::new(max_attempts as i64), &AtomicI64::new(max_attempts as i64),
debug, debug,
alternative_generation,
) )
} }
@ -179,6 +192,7 @@ fn main() {
debug, debug,
solve_controller, solve_controller,
difficulty, difficulty,
alternative_generation,
), ),
}; };
@ -230,6 +244,7 @@ fn run_multi_threaded(
debug: bool, debug: bool,
solve_controller: SolveController, solve_controller: SolveController,
difficulty: Difficulty, difficulty: Difficulty,
alternative_generation: bool,
) -> Vec<GeneratedGrid> { ) -> Vec<GeneratedGrid> {
let mut thread_rng = thread_rng(); let mut thread_rng = thread_rng();
let (transmitter, receiver) = mpsc::channel(); let (transmitter, receiver) = mpsc::channel();
@ -259,6 +274,7 @@ fn run_multi_threaded(
&*puzzles_left, &*puzzles_left,
&*attempts_left, &*attempts_left,
debug, debug,
alternative_generation,
); );
let num_puzzles_found = found_puzzles.len(); let num_puzzles_found = found_puzzles.len();
@ -300,13 +316,16 @@ fn get_puzzle_matching_conditions<R: Rng>(
puzzles_left: &AtomicI64, puzzles_left: &AtomicI64,
attempts_left: &AtomicI64, attempts_left: &AtomicI64,
debug: bool, debug: bool,
alternative_generation: bool,
) -> Vec<GeneratedGrid> { ) -> Vec<GeneratedGrid> {
let mut generated_grids: Vec<GeneratedGrid> = Vec::new(); let mut generated_grids: Vec<GeneratedGrid> = Vec::new();
while attempts_left.fetch_sub(1, Ordering::SeqCst) > 0 && puzzles_left.load(Ordering::SeqCst) > 0 { while attempts_left.fetch_sub(1, Ordering::SeqCst) > 0 && puzzles_left.load(Ordering::SeqCst) > 0 {
let (grid, num_hints, statistics) = let (grid, num_hints, statistics) = match alternative_generation {
sudoku_solver::generator::generate_grid(rng, &solve_controller); true => {sudoku_solver::generator::generate_alternate_grid(rng, &solve_controller)}
false => {sudoku_solver::generator::generate_grid(rng, &solve_controller)}
};
if debug { if debug {
println!("Found puzzle with {:#?}", statistics); println!("Found puzzle with {:#?}", statistics);

View file

@ -178,6 +178,92 @@ pub fn generate_grid<R: Rng>(
(grid, num_hints, statistics_option.unwrap()) (grid, num_hints, statistics_option.unwrap())
} }
pub fn generate_alternate_grid<R: Rng>(
rng: &mut R,
solve_controller: &SolveController,
) -> (Grid, u64, SolveStatistics) {
let mut grid = generate_completed_grid(rng);
let mut cell_order_vec = Vec::new();
// go through and replace every number with a pencil marking
for x in 0..9 {
for y in 0..9 {
let mut cell = grid.get(x, y).unwrap();
cell_order_vec.push(Rc::clone(&cell));
if let CellValue::Fixed(digit) = cell.get_value_copy() {
cell.set_value_exact(CellValue::Unknown(vec![digit]));
} else {
panic!("Unexpectedly found an incomplete cell in a completed puzzle");
}
}
}
// Need to randomly reorder non_empty_cells
cell_order_vec.shuffle(rng);
fn invert_digits(digits: &[u8]) -> Vec<u8>{
let mut inverted = Vec::with_capacity(9);
for i in 1..=9 {
if !digits.contains(&i) {
inverted.push(i);
}
}
inverted
}
let mut num_pencils = 81;
for cell in cell_order_vec {
let mut current_digits = match cell.get_value_copy() {
CellValue::Fixed(digit) => {vec![digit]}
CellValue::Unknown(digits) => {digits}
};
let to_try_add = invert_digits(&current_digits);
for digit in to_try_add {
current_digits.push(digit);
cell.set_value_exact(CellValue::Unknown(current_digits.clone()));
let (status, _) =
evaluate_grid_with_solve_controller(&grid, solve_controller);
match status {
SolveStatus::Complete(uniqueness) => {
let uniqueness = uniqueness.unwrap();
match uniqueness {
Uniqueness::Unique => {
num_pencils += 1;
}
Uniqueness::NotUnique => {
// Too relaxed; can't add this pencil mark
current_digits.pop();
}
}
}
SolveStatus::Unfinished => {
panic!("evaluate_grid_with_solve_controller should never return UNFINISHED")
}
SolveStatus::Invalid => {
println!("{}", grid);
panic!("Removing constraints should not have set the # of solutions to zero")
}
}
}
cell.set_value_exact(CellValue::Unknown(current_digits));
}
let (_, statistics) =
evaluate_grid_with_solve_controller(&grid, solve_controller);
(grid, num_pencils, statistics)
}
// We generate a completed grid with no mind for difficulty; afterward generate_puzzle will take out as many fields as it can with regards to the difficulty // We generate a completed grid with no mind for difficulty; afterward generate_puzzle will take out as many fields as it can with regards to the difficulty
fn generate_completed_grid<R: Rng>(rng: &mut R) -> Grid { fn generate_completed_grid<R: Rng>(rng: &mut R) -> Grid {
let solve_controller = SolveController { let solve_controller = SolveController {