Skip to content

Commit

Permalink
implement SudokuPuzzle fill_grid
Browse files Browse the repository at this point in the history
  • Loading branch information
Nervonment committed May 31, 2024
1 parent 59a7678 commit e907ef0
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod game;
#[cfg(test)]
pub mod test;
pub mod ui;
pub mod neo;

use std::{
io::{self, Write},
Expand Down
4 changes: 4 additions & 0 deletions src/neo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod puzzle;
#[cfg(test)]
pub mod test;
pub mod utils;
168 changes: 168 additions & 0 deletions src/neo/puzzle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use super::utils::{block_idx_2_coord, coord_2_block};

pub trait TrackingCandidates {
fn is_candidate_of(&self, r: usize, c: usize, num: i8) -> bool;
fn get_candidates_of(&mut self, r: usize, c: usize) -> &mut [bool; 10];

fn candidate_cnt_for_grid_in_row(&self, r: usize, c: usize) -> i8;
fn candidate_cnt_for_grid_in_col(&self, c: usize, r: usize) -> i8;
fn candidate_cnt_for_grid_in_blk(&self, b: usize, bidx: usize) -> i8;

fn grid_cnt_for_candidate_in_row(&self, r: usize, num: i8) -> i8;
fn grid_cnt_for_candidate_in_col(&self, c: usize, num: i8) -> i8;
fn grid_cnt_for_candidate_in_blk(&self, b: usize, num: i8) -> i8;
}

pub trait Fillable {
fn fill_grid(&mut self, r: usize, c: usize, num: i8);
fn unfill_grid(&mut self, r: usize, c: usize);
}

pub struct SudokuPuzzle {
pub board: [[i8; 9]; 9],
candidates: [[[bool; 10]; 9]; 9],
candidate_cnt: [[i8; 9]; 9],
pub grid_cnt_for_candidate_in_row: [[i8; 10]; 9],
grid_cnt_for_candidate_in_col: [[i8; 10]; 9],
grid_cnt_for_candidate_in_blk: [[i8; 10]; 9],
}

impl TrackingCandidates for SudokuPuzzle {
fn is_candidate_of(&self, r: usize, c: usize, num: i8) -> bool {
self.candidates[r][c][num as usize]
}
fn get_candidates_of(&mut self, r: usize, c: usize) -> &mut [bool; 10] {
&mut self.candidates[r][c]
}

fn candidate_cnt_for_grid_in_row(&self, r: usize, c: usize) -> i8 {
self.candidate_cnt[r][c]
}
fn candidate_cnt_for_grid_in_col(&self, c: usize, r: usize) -> i8 {
self.candidate_cnt[r][c]
}
fn candidate_cnt_for_grid_in_blk(&self, b: usize, bidx: usize) -> i8 {
let (r, c) = block_idx_2_coord(b, bidx);
self.candidate_cnt[r][c]
}

fn grid_cnt_for_candidate_in_row(&self, r: usize, num: i8) -> i8 {
self.grid_cnt_for_candidate_in_row[r][num as usize]
}
fn grid_cnt_for_candidate_in_col(&self, c: usize, num: i8) -> i8 {
self.grid_cnt_for_candidate_in_col[c][num as usize]
}
fn grid_cnt_for_candidate_in_blk(&self, b: usize, num: i8) -> i8 {
self.grid_cnt_for_candidate_in_blk[b][num as usize]
}
}

impl Fillable for SudokuPuzzle {
fn fill_grid(&mut self, r: usize, c: usize, num: i8) {
self.board[r][c] = num;
let num = num as usize;
let b = coord_2_block(r, c);

for c1 in 0..9 {
self.candidate_cnt[r][c1] -= self.candidates[r][c1][num] as i8;
self.candidates[r][c1][num] = false;
}
for r1 in 0..9 {
self.candidate_cnt[r1][c] -= self.candidates[r1][c][num] as i8;
self.candidates[r1][c][num] = false;
}
for bidx in 0..9 {
let (r1, c1) = block_idx_2_coord(b, bidx);
self.candidate_cnt[r1][c1] -= self.candidates[r1][c1][num] as i8;
self.candidates[r1][c1][num] = false;
}

// 一旦填了一个新的数, grid_cnt 就需要全局更新
for num1 in 1..=9 {
if self.candidates[r][c][num1] {
self.grid_cnt_for_candidate_in_row[r][num1] -= 1;
self.grid_cnt_for_candidate_in_col[c][num1] -= 1;
self.grid_cnt_for_candidate_in_blk[b][num1] -= 1;
}
}
for r in 0..9 {
let mut grid_cnt = 0;
for c in 0..9 {
grid_cnt += (self.candidates[r][c][num] && self.board[r][c] == 0) as i8;
}
self.grid_cnt_for_candidate_in_row[r][num] = grid_cnt;
}
for c in 0..9 {
let mut grid_cnt = 0;
for r in 0..9 {
grid_cnt += (self.candidates[r][c][num] && self.board[r][c] == 0) as i8;
}
self.grid_cnt_for_candidate_in_col[c][num] = grid_cnt;
}
for b in 0..9 {
let mut grid_cnt = 0;
for bidx in 0..9 {
let (r, c) = block_idx_2_coord(b, bidx);
grid_cnt += (self.candidates[r][c][num] && self.board[r][c] == 0) as i8;
}
self.grid_cnt_for_candidate_in_blk[b][num] = grid_cnt;
}
}

fn unfill_grid(&mut self, r: usize, c: usize) {
let num = self.board[r][c] as usize;
self.board[r][c] = 0;
self.candidates[r][c] = [true; 10];
for c1 in 0..9 {
self.candidate_cnt[r][c1] += !self.candidates[r][c1][num] as i8;
self.grid_cnt_for_candidate_in_row[r][num] += !self.candidates[r][c1][num] as i8;
self.candidates[r][c1][num] = true;
// let num1 = self.board[r][c1] as usize;
// self.candidate_cnt[r][c] -= self.candidates[r][c][num1] as i8;
// self.candidates[r][c][num1] = false;
}
for r1 in 0..9 {
self.candidate_cnt[r1][c] += !self.candidates[r1][c][num] as i8;
self.grid_cnt_for_candidate_in_col[c][num] += !self.candidates[r1][c][num] as i8;
self.candidates[r1][c][num] = true;
// let num1 = self.board[r1][c] as usize;
// self.candidate_cnt[r][c] -= self.candidates[r][c][num1] as i8;
// self.candidates[r][c][num1] = false;
}
let b = coord_2_block(r, c);
for bidx in 0..9 {
let (r1, c1) = block_idx_2_coord(b, bidx);
self.candidate_cnt[r1][c1] += !self.candidates[r1][c1][num] as i8;
self.grid_cnt_for_candidate_in_col[b][num] += !self.candidates[r1][c1][num] as i8;
self.candidates[r1][c1][num] = true;
// let num1 = self.board[r1][c1] as usize;
// self.candidate_cnt[r][c] -= self.candidates[r][c][num1] as i8;
// self.candidates[r][c][num1] = false;
}
}
}

impl SudokuPuzzle {
pub fn new(puzzle: [[i8; 9]; 9]) -> Self {
let mut res = Self {
board: [[0; 9]; 9],
candidates: [[[true; 10]; 9]; 9],
candidate_cnt: [[9; 9]; 9],
grid_cnt_for_candidate_in_row: [[9; 10]; 9],
grid_cnt_for_candidate_in_col: [[9; 10]; 9],
grid_cnt_for_candidate_in_blk: [[9; 10]; 9],
};
for r in 0..9 {
for c in 0..9 {
if puzzle[r][c] > 0 {
res.fill_grid(r, c, puzzle[r][c]);
}
}
}
res
}

pub fn grid_val(&self, r: usize, c: usize) -> i8 {
self.board[r][c]
}
}
80 changes: 80 additions & 0 deletions src/neo/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{
game::generator::{random_sudoku_puzzle_easy},
neo::{
puzzle::{SudokuPuzzle, TrackingCandidates},
utils::{block_idx_2_coord, coord_2_block_idx},
},
};

#[test]
fn sudoku_puzzle() {
for _ in 0..100 {
let puzzle = random_sudoku_puzzle_easy();
let puzzle = SudokuPuzzle::new(puzzle);
for r in 0..9 {
for c in 0..9 {
let (b, bidx) = coord_2_block_idx(r, c);

// 如果格 (r,c) 已经填上数 num,那么同行、同列和同宫的空格子的候选数都不包含 num
let num = puzzle.grid_val(r, c);
if num > 0 {
for i in 0..9 {
if i != c && puzzle.grid_val(r, i) == 0 {
assert!(!puzzle.is_candidate_of(r, i, num));
}
if i != r && puzzle.grid_val(i, c) == 0 {
assert!(!puzzle.is_candidate_of(i, c, num));
}
if i != bidx {
let (r1, c1) = block_idx_2_coord(b, bidx);
if puzzle.grid_val(r1, c1) == 0 {
assert!(!puzzle.is_candidate_of(r1, c1, num));
}
}
}
}
// 如果格 (r,c) 未填数,那么他的候选数数量等于候选数数量
else {
let mut candidate_cnt = 0;
for num in 1..=9 {
candidate_cnt += puzzle.is_candidate_of(r, c, num) as i8;
}
assert_eq!(candidate_cnt, puzzle.candidate_cnt_for_grid_in_row(r, c));
assert_eq!(candidate_cnt, puzzle.candidate_cnt_for_grid_in_col(c, r));
assert_eq!(candidate_cnt, puzzle.candidate_cnt_for_grid_in_blk(b, bidx));
}
}
}

for num in 1..=9 {
for r in 0..9 {
// grid_cnt 是第 r 行中候选数列表包含 num 的空格子数
let mut grid_cnt = 0;
for c in 0..9 {
grid_cnt +=
(puzzle.is_candidate_of(r, c, num) && puzzle.grid_val(r, c) == 0) as i8;
}
assert_eq!(grid_cnt, puzzle.grid_cnt_for_candidate_in_row(r, num));
}
for c in 0..9 {
// grid_cnt 是第 c 列中候选数列表包含 num 的空格子数
let mut grid_cnt = 0;
for r in 0..9 {
grid_cnt +=
(puzzle.is_candidate_of(r, c, num) && puzzle.grid_val(r, c) == 0) as i8;
}
assert_eq!(grid_cnt, puzzle.grid_cnt_for_candidate_in_col(c, num));
}
for b in 0..9 {
// grid_cnt 是第 b 宫中候选数列表包含 num 的空格子数
let mut grid_cnt = 0;
for bidx in 0..9 {
let (r, c) = block_idx_2_coord(b, bidx);
grid_cnt +=
(puzzle.is_candidate_of(r, c, num) && puzzle.grid_val(r, c) == 0) as i8;
}
assert_eq!(grid_cnt, puzzle.grid_cnt_for_candidate_in_blk(b, num));
}
}
}
}
11 changes: 11 additions & 0 deletions src/neo/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub fn coord_2_block(r: usize, c: usize) -> usize {
r / 3 * 3 + c / 3
}

pub fn coord_2_block_idx(r: usize, c: usize) -> (usize, usize) {
(r / 3 * 3 + c / 3, r % 3 * 3 + c % 3)
}

pub fn block_idx_2_coord(b: usize, bidx: usize) -> (usize, usize) {
(b / 3 * 3 + bidx / 3, b % 3 * 3 + bidx % 3)
}

0 comments on commit e907ef0

Please sign in to comment.