Skip to content

Commit e06bdc1

Browse files
Add sudoku solver (TheAlgorithms#409)
1 parent cfa131b commit e06bdc1

File tree

5 files changed

+178
-1
lines changed

5 files changed

+178
-1
lines changed

DIRECTORY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# List of all files
22

33
## Src
4-
4+
* Backtracking
5+
* [Sudoku](https://github.com/TheAlgorithms/Rust/blob/master/src/backtracking/sudoku.rs)
56
* Ciphers
67
* [Another Rot13](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/another_rot13.rs)
78
* [Caesar](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/caesar.rs)

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ These are for demonstration purposes only.
167167
- [x] [Another Rot13](./src/ciphers/another_rot13.rs)
168168
- [x] [Rot13](./src/ciphers/rot13.rs)
169169

170+
## [Backtracking](./src/backtracking)
171+
172+
- [x] [Sudoku](./src/backtracking/sudoku.rs)
170173
---
171174

172175
### All implemented Algos

src/backtracking/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod sudoku;
2+
3+
pub use sudoku::Sudoku;

src/backtracking/sudoku.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
A Rust implementation of Sudoku solver using Backtracking.
3+
GeeksForGeeks: https://www.geeksforgeeks.org/sudoku-backtracking-7/
4+
*/
5+
6+
pub struct Sudoku {
7+
board: [[u8; 9]; 9],
8+
}
9+
10+
impl Sudoku {
11+
pub fn new(board: [[u8; 9]; 9]) -> Sudoku {
12+
Sudoku { board }
13+
}
14+
15+
fn find_empty_cell(&self) -> Option<(usize, usize)> {
16+
// Find a empty cell in the board (returns (-1, -1) 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));
21+
}
22+
}
23+
}
24+
25+
None
26+
}
27+
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
32+
33+
// checking through the row
34+
for i in 0..9 {
35+
if self.board[i][x] == value {
36+
return false;
37+
}
38+
}
39+
// checking through the column
40+
for i in 0..9 {
41+
if self.board[y][i] == value {
42+
return false;
43+
}
44+
}
45+
46+
// checking through the 3x3 block of the cell
47+
let sec_row = y / 3;
48+
let sec_col = x / 3;
49+
50+
for i in (sec_row * 3)..(sec_row * 3 + 3) {
51+
for j in (sec_col * 3)..(sec_col * 3 + 3) {
52+
if y != i && x != j && self.board[i][j] == value {
53+
return false;
54+
}
55+
}
56+
}
57+
58+
true
59+
}
60+
61+
pub fn solve(&mut self) -> bool {
62+
let empty_cell = self.find_empty_cell();
63+
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;
68+
if self.solve() {
69+
return true;
70+
}
71+
// backtracking if the board cannot be solved using current configuration
72+
self.board[y][x] = 0
73+
}
74+
}
75+
} else {
76+
// if the board is complete
77+
return true;
78+
}
79+
80+
// returning false the board cannot be solved using current configuration
81+
false
82+
}
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+
}
112+
113+
#[cfg(test)]
114+
mod tests {
115+
use super::*;
116+
117+
#[test]
118+
fn test_sudoku_correct() {
119+
let board: [[u8; 9]; 9] = [
120+
[3, 0, 6, 5, 0, 8, 4, 0, 0],
121+
[5, 2, 0, 0, 0, 0, 0, 0, 0],
122+
[0, 8, 7, 0, 0, 0, 0, 3, 1],
123+
[0, 0, 3, 0, 1, 0, 0, 8, 0],
124+
[9, 0, 0, 8, 6, 3, 0, 0, 5],
125+
[0, 5, 0, 0, 9, 0, 6, 0, 0],
126+
[1, 3, 0, 0, 0, 0, 2, 5, 0],
127+
[0, 0, 0, 0, 0, 0, 0, 7, 4],
128+
[0, 0, 5, 2, 0, 6, 3, 0, 0],
129+
];
130+
131+
let board_result = [
132+
[3, 1, 6, 5, 7, 8, 4, 9, 2],
133+
[5, 2, 9, 1, 3, 4, 7, 6, 8],
134+
[4, 8, 7, 6, 2, 9, 5, 3, 1],
135+
[2, 6, 3, 4, 1, 5, 9, 8, 7],
136+
[9, 7, 4, 8, 6, 3, 1, 2, 5],
137+
[8, 5, 1, 7, 9, 2, 6, 4, 3],
138+
[1, 3, 8, 9, 4, 7, 2, 5, 6],
139+
[6, 9, 2, 3, 5, 1, 8, 7, 4],
140+
[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+
}
149+
150+
#[test]
151+
fn test_sudoku_incorrect() {
152+
let board: [[u8; 9]; 9] = [
153+
[6, 0, 3, 5, 0, 8, 4, 0, 0],
154+
[5, 2, 0, 0, 0, 0, 0, 0, 0],
155+
[0, 8, 7, 0, 0, 0, 0, 3, 1],
156+
[0, 0, 3, 0, 1, 0, 0, 8, 0],
157+
[9, 0, 0, 8, 6, 3, 0, 0, 5],
158+
[0, 5, 0, 0, 9, 0, 6, 0, 0],
159+
[1, 3, 0, 0, 0, 0, 2, 5, 0],
160+
[0, 0, 0, 0, 0, 0, 0, 7, 4],
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);
168+
}
169+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod backtracking;
12
pub mod big_integer;
23
pub mod ciphers;
34
pub mod data_structures;

0 commit comments

Comments
 (0)