sudoku_rust/
lib.rs

1use rand::Rng;
2
3pub const N: usize = 9;
4pub const SRN: usize = 3;
5
6#[derive(Clone, Copy)]
7pub struct Cell {
8    pub num: u8,
9    pub locked: bool,
10}
11type Grid = [[Cell; N]; N];
12
13pub struct SudokuBoard {
14    pub cells: Grid,
15}
16
17impl SudokuBoard {
18    /// Create an empty board with all cells "locked"
19    pub fn new() -> Self {
20        Self {
21            cells: [[Cell {
22                num: 0,
23                locked: true,
24            }; N]; N],
25        }
26    }
27
28    /// Fill board completely with solved state
29    pub fn fill(&mut self) {
30        self.fill_diagonal();
31        self.fill_remaining(0, SRN);
32    }
33
34    fn fill_diagonal(&mut self) {
35        for i in 0..SRN {
36            self.fill_box(i * SRN, i * SRN);
37        }
38    }
39
40    /// Returns false if given SRN x SRN block contains num.
41    pub fn unused_in_box(grid: Grid, row_start: usize, col_start: usize, num: u8) -> bool {
42        for i in 0..SRN {
43            for j in 0..SRN {
44                if grid[row_start + i][col_start + j].num == num {
45                    return false;
46                }
47            }
48        }
49        return true;
50    }
51
52    fn fill_box(&mut self, row: usize, col: usize) {
53        let mut rng = rand::thread_rng();
54        let mut num: Option<u8> = None;
55        for i in 0..SRN {
56            for j in 0..SRN {
57                while num == None || !Self::unused_in_box(self.cells, row, col, num.unwrap()) {
58                    num = Some(rng.gen_range(0..=N as u8));
59                }
60                self.cells[row + i][col + j].num = num.unwrap();
61            }
62        }
63    }
64
65    /// Check if safe to put in cell
66    pub fn check_if_safe(grid: Grid, i: usize, j: usize, num: u8) -> bool {
67        num == 0
68            || (Self::unused_in_row(grid, i, num)
69                && Self::unused_in_col(grid, j, num)
70                && Self::unused_in_box(grid, i - i % SRN, j - j % SRN, num))
71    }
72
73    pub fn unused_in_row(grid: Grid, i: usize, num: u8) -> bool {
74        for j in 0..N {
75            if grid[i][j].num == num {
76                return false;
77            }
78        }
79        return true;
80    }
81
82    pub fn unused_in_col(grid: Grid, j: usize, num: u8) -> bool {
83        for i in 0..N {
84            if grid[i][j].num == num {
85                return false;
86            }
87        }
88        return true;
89    }
90
91    /// A recursive function to fill remaining
92    /// matrix
93    pub fn fill_remaining(&mut self, mut i: usize, mut j: usize) -> bool {
94        if j >= N && i < N - 1 {
95            i += 1;
96            j = 0;
97        }
98        if i >= N && j >= N {
99            return true;
100        }
101        if i < SRN {
102            if j < SRN {
103                j = SRN;
104            }
105        } else if i < N - SRN {
106            if j == i / SRN * SRN {
107                j += SRN;
108            }
109        } else {
110            if j == N - SRN {
111                i += 1;
112                j = 0;
113                if i >= N {
114                    return true;
115                }
116            }
117        }
118
119        for num in 1..=N as u8 {
120            if Self::check_if_safe(self.cells, i, j, num) {
121                self.cells[i][j].num = num;
122                self.cells[i][j].locked = true;
123                if self.fill_remaining(i, j + 1) {
124                    return true;
125                }
126                self.cells[i][j].num = 0;
127                self.cells[i][j].locked = false;
128            }
129        }
130
131        false
132    }
133
134    /// Get the amount of unique solutions a board has
135    pub fn solve(mut grid: Grid, mut counter: u32) -> u32 {
136        let mut row = 0;
137        let mut col = 0;
138        for i in 0..81 {
139            row = i / N;
140            col = i % N;
141            if grid[row][col].num == 0 {
142                for num in 1..10 {
143                    // Check if it is safe to place
144                    // the num (1-N)  in the
145                    // given row ,col ->we move to next column
146                    if Self::check_if_safe(grid, row, col, num) {
147                        /*  assigning the num in the current
148                        (row,col)  position of the grid and
149                        assuming our assigned num in the position
150                        is correct */
151                        grid[row][col].num = num;
152                        grid[row][col].locked = true;
153                        if Self::check_filled(grid) {
154                            counter += 1;
155                            break;
156                        } else {
157                            // Checking for next
158                            // possibility with next column
159                            return Self::solve(grid, counter);
160                        }
161                    }
162                    /* removing the assigned num , since our
163                    assumption was wrong , and we go for next
164                    assumption with diff num value   */
165                }
166                break;
167            }
168        }
169        grid[row][col].num = 0;
170        grid[row][col].locked = false;
171        return counter;
172    }
173
174    /// Check if the board has no mistakes
175    pub fn check_valid(mut grid: Grid) -> bool {
176        for i in 0..N {
177            for j in 0..N {
178                let original = grid[i][j].num;
179                grid[i][j].num = 0;
180                if !Self::check_if_safe(grid, i, j, original) {
181                    return false;
182                }
183                grid[i][j].num = original;
184            }
185        }
186        true
187    }
188
189    /// Check if the board is solved
190    pub fn check_solved(grid: Grid) -> bool {
191        Self::check_filled(grid) && Self::check_valid(grid)
192    }
193
194    /// Check if the board is completely filled
195    pub fn check_filled(grid: Grid) -> bool {
196        for row in 0..N {
197            for col in 0..N {
198                if grid[row][col].num == 0 {
199                    return false;
200                }
201            }
202        }
203
204        true
205    }
206
207    /// Remove cells from the filled solved board to make a puzzle
208    pub fn remove_cells(&mut self, mut attempts: u32) {
209        let mut rng = rand::thread_rng();
210
211        while attempts > 0 {
212            // Select a random cell that is not already empty
213            let mut row = rng.gen_range(0..N);
214            let mut col = rng.gen_range(0..N);
215            while self.cells[row][col].num == 0 {
216                row = rng.gen_range(0..N);
217                col = rng.gen_range(0..N);
218            }
219
220            //Remember its cell value in case we need to put it back
221            let backup = self.cells[row][col].clone();
222            self.cells[row][col].num = 0;
223            self.cells[row][col].locked = false;
224
225            // Take a full copy of the grid
226            let copy_grid = self.cells.clone();
227
228            let counter = Self::solve(copy_grid, 0);
229            // Count the number of solutions that this grid has (using a backtracking approach implemented in the solveGrid() function)
230            //If the number of solution is different from 1 then we need to cancel the change by putting the value we took away back in the grid
231            if counter != 1 {
232                self.cells[row][col] = backup;
233                //We could stop here, but we can also have another attempt with a different cell just to try to remove more numbers
234                attempts -= 1;
235            }
236        }
237    }
238
239    /// Display the board in stdout
240    pub fn display(&self) {
241        Self::display_grid(&self.cells);
242    }
243
244    /// Display grid in stdout
245    pub fn display_grid(grid: &Grid) {
246        println!(
247            "{}",
248            grid.map(|a| {
249                a.map(|a| match a.num {
250                    0 => " ".to_string(),
251                    _ => a.num.to_string(),
252                })
253                .join(" ")
254            })
255            .join("\n")
256        );
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use crate::{SudokuBoard, N};
263
264    #[test]
265    fn check_solved() {
266        let mut board = SudokuBoard::new();
267        board.fill();
268        assert!(SudokuBoard::check_solved(board.cells));
269
270        board.cells[0][0].num = N as u8 - board.cells[0][0].num;
271        assert!(!SudokuBoard::check_solved(board.cells));
272
273        board.cells[0][0].num = 0;
274        assert!(!SudokuBoard::check_solved(board.cells));
275        assert!(SudokuBoard::check_valid(board.cells));
276    }
277}