Skip to content

Commit bba0b0d

Browse files
sozelfistvil02
andauthored
Refactor Sudoku implementation (#715)
* ref: refactor Sudoku implementation - Create `sudoku_solver` function to hide states of `SudokuSolver` class - Rename variables to make code more readable - Rewrite tests using macros - Remove `print_board` method * chore: update tests --------- Co-authored-by: Piotr Idzik <[email protected]>
1 parent c16984f commit bba0b0d

File tree

2 files changed

+85
-90
lines changed

2 files changed

+85
-90
lines changed

src/backtracking/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ pub use knight_tour::find_knight_tour;
1010
pub use n_queens::n_queens_solver;
1111
pub use parentheses_generator::generate_parentheses;
1212
pub use permutations::permute;
13-
pub use sudoku::Sudoku;
13+
pub use sudoku::sudoku_solver;

src/backtracking/sudoku.rs

Lines changed: 84 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,80 @@
1-
/*
2-
A Rust implementation of Sudoku solver using Backtracking.
3-
GeeksForGeeks: https://www.geeksforgeeks.org/sudoku-backtracking-7/
4-
*/
1+
//! A Rust implementation of Sudoku solver using Backtracking.
2+
//!
3+
//! This module provides functionality to solve Sudoku puzzles using the backtracking algorithm.
4+
//!
5+
//! GeeksForGeeks: [Sudoku Backtracking](https://www.geeksforgeeks.org/sudoku-backtracking-7/)
6+
7+
/// Solves a Sudoku puzzle.
8+
///
9+
/// Given a partially filled Sudoku puzzle represented by a 9x9 grid, this function attempts to
10+
/// solve the puzzle using the backtracking algorithm.
11+
///
12+
/// Returns the solved Sudoku board if a solution exists, or `None` if no solution is found.
13+
pub fn sudoku_solver(board: &[[u8; 9]; 9]) -> Option<[[u8; 9]; 9]> {
14+
let mut solver = SudokuSolver::new(*board);
15+
if solver.solve() {
16+
Some(solver.board)
17+
} else {
18+
None
19+
}
20+
}
521

6-
pub struct Sudoku {
22+
/// Represents a Sudoku puzzle solver.
23+
struct SudokuSolver {
24+
/// The Sudoku board represented by a 9x9 grid.
725
board: [[u8; 9]; 9],
826
}
927

10-
impl Sudoku {
11-
pub fn new(board: [[u8; 9]; 9]) -> Sudoku {
12-
Sudoku { board }
28+
impl SudokuSolver {
29+
/// Creates a new Sudoku puzzle solver with the given board.
30+
fn new(board: [[u8; 9]; 9]) -> SudokuSolver {
31+
SudokuSolver { board }
1332
}
1433

34+
/// Finds an empty cell in the Sudoku board.
35+
///
36+
/// Returns the coordinates of an empty cell `(row, column)` if found, or `None` if all cells are filled.
1537
fn find_empty_cell(&self) -> Option<(usize, usize)> {
16-
// Find a empty cell in the board (returns None if all cells are filled)
17-
for i in 0..9 {
18-
for j in 0..9 {
19-
if self.board[i][j] == 0 {
20-
return Some((i, j));
38+
// Find an empty cell in the board (returns None if all cells are filled)
39+
for row in 0..9 {
40+
for column in 0..9 {
41+
if self.board[row][column] == 0 {
42+
return Some((row, column));
2143
}
2244
}
2345
}
2446

2547
None
2648
}
2749

28-
fn check(&self, index_tuple: (usize, usize), value: u8) -> bool {
29-
let (y, x) = index_tuple;
30-
31-
// checks if the value to be added in the board is an acceptable value for the cell
50+
/// Checks whether a given value can be placed in a specific cell according to Sudoku rules.
51+
///
52+
/// Returns `true` if the value can be placed in the cell, otherwise `false`.
53+
fn is_value_valid(&self, coordinates: (usize, usize), value: u8) -> bool {
54+
let (row, column) = coordinates;
3255

33-
// checking through the row
34-
for i in 0..9 {
35-
if self.board[i][x] == value {
56+
// Checks if the value to be added in the board is an acceptable value for the cell
57+
// Checking through the row
58+
for current_column in 0..9 {
59+
if self.board[row][current_column] == value {
3660
return false;
3761
}
3862
}
39-
// checking through the column
40-
for i in 0..9 {
41-
if self.board[y][i] == value {
63+
64+
// Checking through the column
65+
for current_row in 0..9 {
66+
if self.board[current_row][column] == value {
4267
return false;
4368
}
4469
}
4570

46-
// checking through the 3x3 block of the cell
47-
let sec_row = y / 3;
48-
let sec_col = x / 3;
71+
// Checking through the 3x3 block of the cell
72+
let start_row = row / 3 * 3;
73+
let start_column = column / 3 * 3;
4974

50-
for i in (sec_row * 3)..(sec_row * 3 + 3) {
51-
for j in (sec_col * 3)..(sec_col * 3 + 3) {
52-
if self.board[i][j] == value {
75+
for current_row in start_row..start_row + 3 {
76+
for current_column in start_column..start_column + 3 {
77+
if self.board[current_row][current_column] == value {
5378
return false;
5479
}
5580
}
@@ -58,65 +83,51 @@ impl Sudoku {
5883
true
5984
}
6085

61-
pub fn solve(&mut self) -> bool {
86+
/// Solves the Sudoku puzzle recursively using backtracking.
87+
///
88+
/// Returns `true` if a solution is found, otherwise `false`.
89+
fn solve(&mut self) -> bool {
6290
let empty_cell = self.find_empty_cell();
6391

64-
if let Some((y, x)) = empty_cell {
65-
for val in 1..10 {
66-
if self.check((y, x), val) {
67-
self.board[y][x] = val;
92+
if let Some((row, column)) = empty_cell {
93+
for value in 1..=9 {
94+
if self.is_value_valid((row, column), value) {
95+
self.board[row][column] = value;
6896
if self.solve() {
6997
return true;
7098
}
71-
// backtracking if the board cannot be solved using current configuration
72-
self.board[y][x] = 0
99+
// Backtracking if the board cannot be solved using the current configuration
100+
self.board[row][column] = 0;
73101
}
74102
}
75103
} else {
76-
// if the board is complete
104+
// If the board is complete
77105
return true;
78106
}
79107

80-
// returning false the board cannot be solved using current configuration
108+
// Returning false if the board cannot be solved using the current configuration
81109
false
82110
}
83-
84-
pub fn print_board(&self) {
85-
// helper function to display board
86-
87-
let print_3_by_1 = |arr: Vec<u8>, last: bool| {
88-
let str = arr
89-
.iter()
90-
.map(|n| n.to_string())
91-
.collect::<Vec<String>>()
92-
.join(", ");
93-
94-
if last {
95-
println!("{str}",);
96-
} else {
97-
print!("{str} | ",);
98-
}
99-
};
100-
101-
for i in 0..9 {
102-
if i % 3 == 0 && i != 0 {
103-
println!("- - - - - - - - - - - - - -")
104-
}
105-
106-
print_3_by_1(self.board[i][0..3].to_vec(), false);
107-
print_3_by_1(self.board[i][3..6].to_vec(), false);
108-
print_3_by_1(self.board[i][6..9].to_vec(), true);
109-
}
110-
}
111111
}
112112

113113
#[cfg(test)]
114114
mod tests {
115115
use super::*;
116116

117-
#[test]
118-
fn test_sudoku_correct() {
119-
let board: [[u8; 9]; 9] = [
117+
macro_rules! test_sudoku_solver {
118+
($($name:ident: $board:expr, $expected:expr,)*) => {
119+
$(
120+
#[test]
121+
fn $name() {
122+
let result = sudoku_solver(&$board);
123+
assert_eq!(result, $expected);
124+
}
125+
)*
126+
};
127+
}
128+
129+
test_sudoku_solver! {
130+
test_sudoku_correct: [
120131
[3, 0, 6, 5, 0, 8, 4, 0, 0],
121132
[5, 2, 0, 0, 0, 0, 0, 0, 0],
122133
[0, 8, 7, 0, 0, 0, 0, 3, 1],
@@ -126,9 +137,7 @@ mod tests {
126137
[1, 3, 0, 0, 0, 0, 2, 5, 0],
127138
[0, 0, 0, 0, 0, 0, 0, 7, 4],
128139
[0, 0, 5, 2, 0, 6, 3, 0, 0],
129-
];
130-
131-
let board_result = [
140+
], Some([
132141
[3, 1, 6, 5, 7, 8, 4, 9, 2],
133142
[5, 2, 9, 1, 3, 4, 7, 6, 8],
134143
[4, 8, 7, 6, 2, 9, 5, 3, 1],
@@ -138,18 +147,9 @@ mod tests {
138147
[1, 3, 8, 9, 4, 7, 2, 5, 6],
139148
[6, 9, 2, 3, 5, 1, 8, 7, 4],
140149
[7, 4, 5, 2, 8, 6, 3, 1, 9],
141-
];
142-
143-
let mut sudoku = Sudoku::new(board);
144-
let is_solved = sudoku.solve();
145-
146-
assert!(is_solved);
147-
assert_eq!(sudoku.board, board_result);
148-
}
150+
]),
149151

150-
#[test]
151-
fn test_sudoku_incorrect() {
152-
let board: [[u8; 9]; 9] = [
152+
test_sudoku_incorrect: [
153153
[6, 0, 3, 5, 0, 8, 4, 0, 0],
154154
[5, 2, 0, 0, 0, 0, 0, 0, 0],
155155
[0, 8, 7, 0, 0, 0, 0, 3, 1],
@@ -159,11 +159,6 @@ mod tests {
159159
[1, 3, 0, 0, 0, 0, 2, 5, 0],
160160
[0, 0, 0, 0, 0, 0, 0, 7, 4],
161161
[0, 0, 5, 2, 0, 6, 3, 0, 0],
162-
];
163-
164-
let mut sudoku = Sudoku::new(board);
165-
let is_solved = sudoku.solve();
166-
167-
assert!(!is_solved);
162+
], None::<[[u8; 9]; 9]>,
168163
}
169164
}

0 commit comments

Comments
 (0)