Skip to content

Commit

Permalink
generator; fix bug of hidden single
Browse files Browse the repository at this point in the history
  • Loading branch information
Nervonment committed Jun 1, 2024
1 parent bb1f92b commit 6844643
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 17 deletions.
84 changes: 72 additions & 12 deletions src/bin/example/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ use std::io::{self, stdout};

use crossterm::{terminal::EnterAlternateScreen, ExecutableCommand};
use sudoku::{
game::generator::random_sudoku_puzzle_normal,
// game::{
// generator::{random_sudoku_puzzle_extrahard, random_sudoku_puzzle_normal},
// solver::{SudokuSolver, SudokuSolverH},
// },
neo::{
puzzle::{Grid, SudokuPuzzleFull},
generator::random_sudoku_puzzle,
puzzle::{Grid, SudokuPuzzleFull, SudokuPuzzleSimple},
solver::{Grader, Solver, StochasticSolver, TechniquesSolver},
techniques::{
hidden_pair_blk, hidden_pair_col, hidden_pair_row, hidden_single_blk,
hidden_single_col, hidden_single_row, naked_pair_blk, naked_pair_col, naked_pair_row,
Expand All @@ -15,17 +20,42 @@ use sudoku::{
};

fn main() -> io::Result<()> {
let board = random_sudoku_puzzle_normal();
let board = random_sudoku_puzzle::<
StochasticSolver<SudokuPuzzleSimple>,
TechniquesSolver<SudokuPuzzleFull>,
>(45, 1000, 100000);
// let board = [
// [0, 1, 0, 0, 0, 0, 0, 0, 0],
// [0, 0, 0, 0, 4, 0, 1, 0, 8],
// [0, 0, 0, 0, 1, 0, 7, 5, 4],
// [0, 0, 8, 1, 0, 7, 0, 6, 5],
// [5, 7, 0, 0, 0, 0, 2, 1, 9],
// [0, 6, 1, 5, 0, 2, 8, 0, 0],
// [0, 0, 0, 3, 7, 0, 5, 0, 2],
// [0, 5, 0, 6, 2, 0, 0, 0, 0],
// [7, 0, 2, 0, 5, 0, 0, 0, 0],
// [4, 0, 8, 0, 0, 0, 0, 0, 9],
// [0, 0, 0, 4, 9, 0, 7, 0, 0],
// [0, 0, 0, 6, 8, 0, 0, 0, 0],
// [0, 0, 6, 8, 0, 0, 5, 0, 0],
// [8, 0, 1, 0, 4, 9, 0, 0, 0],
// [0, 0, 0, 0, 1, 6, 8, 0, 0],
// [0, 0, 0, 3, 7, 4, 9, 0, 0],
// [0, 0, 3, 9, 0, 0, 2, 0, 0],
// [9, 0, 0, 0, 6, 0, 0, 3, 7],
// ];
// let board = [
// [4, 0, 8, 0, 0, 0, 0, 0, 9],
// [0, 0, 0, 0, 0, 0, 7, 0, 0],
// [0, 0, 0, 6, 8, 0, 0, 0, 0],
// [0, 0, 6, 8, 0, 0, 5, 0, 0],
// [8, 0, 1, 0, 4, 9, 0, 0, 0],
// [0, 0, 0, 0, 1, 0, 8, 0, 0],
// [0, 0, 0, 0, 7, 4, 0, 0, 0],
// [0, 0, 3, 0, 0, 0, 2, 0, 0],
// [9, 0, 0, 0, 6, 0, 0, 3, 7],
// ];
// let board = [
// [4, 0, 8, 0, 0, 0, 0, 0, 9],
// [0, 0, 0, 4, 0, 0, 7, 0, 0],
// [0, 0, 0, 6, 8, 0, 0, 0, 0],
// [0, 0, 6, 8, 0, 0, 5, 0, 0],
// [8, 0, 1, 0, 4, 9, 0, 0, 0],
// [0, 0, 0, 0, 1, 0, 8, 0, 0],
// [0, 0, 0, 3, 7, 4, 0, 0, 0],
// [0, 0, 3, 0, 0, 0, 2, 0, 0],
// [9, 0, 0, 0, 6, 0, 0, 3, 7],
// ];
let puzzle = SudokuPuzzleFull::new(board);
stdout().execute(EnterAlternateScreen)?;
Expand Down Expand Up @@ -53,5 +83,35 @@ fn main() -> io::Result<()> {
println!("naked pair in row: {:?}", res_naked_pair_row);
println!("naked pair in col: {:?}", res_naked_pair_col);
println!("naked pair in blk: {:?}", res_naked_pair_blk);
let mut solver2 = TechniquesSolver::<SudokuPuzzleFull>::new(board);
solver2.have_unique_solution();
println!("{}", solver2.difficulty());

// let mut solver1_total_invoke = 0;
// let mut solver2_total_invoke = 0;
// let times = 1;
// for _ in 0..times {
// // let puzzle = random_sudoku_puzzle_extrahard();
// let puzzle = [
// [4, 0, 0, 0, 0, 7, 6, 0, 0],
// [0, 7, 0, 6, 1, 0, 0, 2, 3],
// [5, 0, 0, 0, 0, 0, 1, 0, 0],
// [0, 0, 0, 2, 8, 0, 0, 0, 0],
// [0, 0, 5, 0, 3, 0, 0, 0, 0],
// [9, 0, 0, 0, 7, 0, 0, 0, 0],
// [0, 0, 8, 0, 9, 3, 0, 0, 1],
// [0, 0, 0, 0, 0, 0, 8, 0, 0],
// [3, 0, 0, 4, 0, 0, 0, 5, 0],
// ];
// let mut solver1 = SudokuSolverH::new(puzzle);
// let mut solver2 = TechniquesSolver::<SudokuPuzzleFull>::new(puzzle);
// solver1.have_unique_solution();
// solver2.have_unique_solution();
// solver1_total_invoke += solver1.invoke_cnt;
// solver2_total_invoke += solver2.difficulty();
// }
// println!("solver1: {}", solver1_total_invoke / times);
// println!("solver2: {}", solver2_total_invoke / times);

Ok(())
}
3 changes: 2 additions & 1 deletion src/neo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub mod test;
pub mod utils;
pub mod techniques;
pub mod solver;
pub mod judge;
pub mod judge;
pub mod generator;
93 changes: 93 additions & 0 deletions src/neo/generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use rand::random;

use super::solver::{Grader, Solver};

pub fn random_sudoku_puzzle<T1, T2>(
min_blank_cnt: i32, // 需要生成的题目最少空格数
min_difficulty: i32, // 题目最小难度分数
max_difficulty: i32, // 题目最大难度分数
) -> [[i8; 9]; 9]
where
T1: Solver,
T2: Solver + Grader,
{
loop {
// 生成随机终局
let mut puzzle = T1::new([[0; 9]; 9]).any_solution().unwrap();
// println!("生成了新的终局,正在尝试在此基础上挖空生成题目...");

let mut dug = 0; // 已经挖掉的空格数
let mut trace = vec![]; // 挖空历史记录
trace.reserve(64);
let failed_try_threshold = 48; // 挖空失败次数阈值,失败次数超过此值会尝试回退

let trace_back_step = 24; // 回退的步长
let mut trace_back_cnt = 0; // 回退的次数
let trace_back_cnt_threshold = 12; // 回退次数阈值,回退次数超过此值会尝试重新生成终局

let mut difficulty = -1; // 搜索函数在此题目上调用的次数

while trace_back_cnt < trace_back_cnt_threshold
&& !(dug >= min_blank_cnt
&& (difficulty >= min_difficulty && difficulty <= max_difficulty))
{
let mut failed_try = 0;
let step = match dug {
..=40 => 3,
41.. => 1,
};
loop {
// 一次挖 step 个空
for _ in 0..step {
// 随机选取非空格
let (mut r, mut c) = (random::<usize>() % 9, random::<usize>() % 9);
while puzzle[r][c] == 0 {
(r, c) = (random::<usize>() % 9, random::<usize>() % 9);
}
trace.push((r, c, puzzle[r][c]));
puzzle[r][c] = 0;
}

// 挖空后,判断是否有唯一解
let mut solver = T2::new(puzzle);
if solver.have_unique_solution() {
difficulty = solver.difficulty();
break;
}

// 没有唯一解,填回刚刚挖的空
failed_try += 1;
for _ in 0..step {
let last = trace.pop();
if last.is_some() {
let (r, c, num) = last.unwrap();
puzzle[r][c] = num;
}
}

// 尝试失败次数过多时,退回一定步数重新尝试
if failed_try > failed_try_threshold {
for _ in 0..trace_back_step {
let last = trace.pop();
if last.is_some() {
let (r, c, num) = last.unwrap();
puzzle[r][c] = num;
}
}
dug -= trace_back_step;
failed_try = 0;
trace_back_cnt += 1;
// println!(
// "挖空失败超过{}次,退回{}步重新尝试挖空",
// failed_try_threshold, trace_back_step
// );
}
}
dug += step;
}
if dug >= min_blank_cnt && (difficulty >= min_difficulty && difficulty <= max_difficulty) {
return puzzle;
}
// println!("这个终局可能不太行,正在换一个终局重新生成...");
}
}
22 changes: 22 additions & 0 deletions src/neo/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub trait Solver {
fn have_unique_solution(&mut self) -> bool;
}

pub trait Grader {
fn difficulty(&self) -> i32;
}

fn next_blank(mut row: usize, mut col: usize, puzzle: &impl Grid) -> Option<(usize, usize)> {
while row < 9 && !puzzle.is_grid_empty(row, col) {
if col == 8 {
Expand Down Expand Up @@ -125,6 +129,7 @@ where
puzzle_arr: [[i8; 9]; 9],
puzzle: T,
solution_cnt: u32,
invoke_cnt: i32,
}

impl<T> TechniquesSolver<T>
Expand All @@ -138,10 +143,12 @@ where
{
fn init_search(&mut self) {
self.solution_cnt = 0;
self.invoke_cnt = 0;
self.puzzle = T::new(self.puzzle_arr);
}

fn search(&mut self, solution_cnt_needed: u32) -> bool {
self.invoke_cnt += 1;
if self.puzzle.board().iter().flatten().all(|v| *v > 0) {
self.solution_cnt += 1;
return solution_cnt_needed <= self.solution_cnt;
Expand Down Expand Up @@ -258,6 +265,7 @@ where
puzzle_arr: puzzle,
puzzle: T::new(puzzle),
solution_cnt: 0,
invoke_cnt: 0,
}
}

Expand All @@ -281,3 +289,17 @@ where
self.solution_cnt == 1
}
}

impl<T> Grader for TechniquesSolver<T>
where
T: Grid
+ Fillable
+ CandidatesSettable
+ TrackingCandidates
+ TrackingCandidateCountOfGrid
+ TrackingGridCountOfCandidate,
{
fn difficulty(&self) -> i32 {
self.invoke_cnt
}
}
6 changes: 2 additions & 4 deletions src/neo/techniques.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ where
let j = (0..9)
.filter(|j: &usize| {
let (r, c) = coord_transform(i, *j);
puzzle.is_candidate_of(r, c, num)
puzzle.is_grid_empty(r, c) && puzzle.is_candidate_of(r, c, num)
})
.next()
.unwrap();
let (r, c) = coord_transform(i, j);
if puzzle.is_grid_empty(r, c) {
return Some((r, c, num));
}
return Some((r, c, num));
}
}
}
Expand Down

0 comments on commit 6844643

Please sign in to comment.