Add alternate style of puzzle generation
This commit is contained in:
parent
031bdf6cd9
commit
e69db652d4
2 changed files with 109 additions and 4 deletions
|
@ -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);
|
||||||
|
|
|
@ -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(¤t_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 {
|
||||||
|
|
Loading…
Reference in a new issue