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
+ }
5
21
6
- pub struct Sudoku {
22
+ /// Represents a Sudoku puzzle solver.
23
+ struct SudokuSolver {
24
+ /// The Sudoku board represented by a 9x9 grid.
7
25
board : [ [ u8 ; 9 ] ; 9 ] ,
8
26
}
9
27
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 }
13
32
}
14
33
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.
15
37
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 ) ) ;
21
43
}
22
44
}
23
45
}
24
46
25
47
None
26
48
}
27
49
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;
32
55
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 {
36
60
return false ;
37
61
}
38
62
}
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 {
42
67
return false ;
43
68
}
44
69
}
45
70
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 ;
49
74
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 {
53
78
return false ;
54
79
}
55
80
}
@@ -58,65 +83,51 @@ impl Sudoku {
58
83
true
59
84
}
60
85
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 {
62
90
let empty_cell = self . find_empty_cell ( ) ;
63
91
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 ;
68
96
if self . solve ( ) {
69
97
return true ;
70
98
}
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 ;
73
101
}
74
102
}
75
103
} else {
76
- // if the board is complete
104
+ // If the board is complete
77
105
return true ;
78
106
}
79
107
80
- // returning false the board cannot be solved using current configuration
108
+ // Returning false if the board cannot be solved using the current configuration
81
109
false
82
110
}
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
- }
111
111
}
112
112
113
113
#[ cfg( test) ]
114
114
mod tests {
115
115
use super :: * ;
116
116
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: [
120
131
[ 3 , 0 , 6 , 5 , 0 , 8 , 4 , 0 , 0 ] ,
121
132
[ 5 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
122
133
[ 0 , 8 , 7 , 0 , 0 , 0 , 0 , 3 , 1 ] ,
@@ -126,9 +137,7 @@ mod tests {
126
137
[ 1 , 3 , 0 , 0 , 0 , 0 , 2 , 5 , 0 ] ,
127
138
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 7 , 4 ] ,
128
139
[ 0 , 0 , 5 , 2 , 0 , 6 , 3 , 0 , 0 ] ,
129
- ] ;
130
-
131
- let board_result = [
140
+ ] , Some ( [
132
141
[ 3 , 1 , 6 , 5 , 7 , 8 , 4 , 9 , 2 ] ,
133
142
[ 5 , 2 , 9 , 1 , 3 , 4 , 7 , 6 , 8 ] ,
134
143
[ 4 , 8 , 7 , 6 , 2 , 9 , 5 , 3 , 1 ] ,
@@ -138,18 +147,9 @@ mod tests {
138
147
[ 1 , 3 , 8 , 9 , 4 , 7 , 2 , 5 , 6 ] ,
139
148
[ 6 , 9 , 2 , 3 , 5 , 1 , 8 , 7 , 4 ] ,
140
149
[ 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
+ ] ) ,
149
151
150
- #[ test]
151
- fn test_sudoku_incorrect ( ) {
152
- let board: [ [ u8 ; 9 ] ; 9 ] = [
152
+ test_sudoku_incorrect: [
153
153
[ 6 , 0 , 3 , 5 , 0 , 8 , 4 , 0 , 0 ] ,
154
154
[ 5 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
155
155
[ 0 , 8 , 7 , 0 , 0 , 0 , 0 , 3 , 1 ] ,
@@ -159,11 +159,6 @@ mod tests {
159
159
[ 1 , 3 , 0 , 0 , 0 , 0 , 2 , 5 , 0 ] ,
160
160
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 7 , 4 ] ,
161
161
[ 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 ] >,
168
163
}
169
164
}
0 commit comments