Skip to content

Commit f8096d2

Browse files
sozelfistvil02
andauthored
Refactor NQueens implementation (TheAlgorithms#732)
* ref: refactor NQueens implementation - Rewrite `NQueensSolver` in OOP style - Use `n_queens_solver` method to hide internal state of `NQueensSolver` object - Add Rust docstrings - Write parametrized tests, add 6x6 case * ref: refactor implementation - Simplified the `NQueensSolver` struct initialization - Streamlined the `is_safe` method - Minor formatting changes * ref: update implementation - Use `std::mem::take()` instead of `clone` - Rewrite tests --------- Co-authored-by: Piotr Idzik <[email protected]>
1 parent c502484 commit f8096d2

File tree

1 file changed

+198
-77
lines changed

1 file changed

+198
-77
lines changed

src/backtracking/n_queens.rs

Lines changed: 198 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,108 @@
1-
/*
2-
The N-Queens problem is a classic chessboard puzzle where the goal is to
3-
place N queens on an N×N chessboard so that no two queens threaten each
4-
other. Queens can attack each other if they share the same row, column, or
5-
diagonal.
6-
7-
We solve this problem using a backtracking algorithm. We start with an empty
8-
chessboard and iteratively try to place queens in different rows, ensuring
9-
they do not conflict with each other. If a valid solution is found, it's added
10-
to the list of solutions.
11-
12-
Time Complexity: O(N!), where N is the size of the chessboard.
13-
14-
nqueens_solver(4) => Returns two solutions:
15-
Solution 1:
16-
[
17-
".Q..",
18-
"...Q",
19-
"Q...",
20-
"..Q."
21-
]
22-
23-
Solution 2:
24-
[
25-
"..Q.",
26-
"Q...",
27-
"...Q",
28-
".Q.."
29-
]
30-
*/
31-
1+
//! This module provides functionality to solve the N-Queens problem.
2+
//!
3+
//! The N-Queens problem is a classic chessboard puzzle where the goal is to
4+
//! place N queens on an NxN chessboard so that no two queens threaten each
5+
//! other. Queens can attack each other if they share the same row, column, or
6+
//! diagonal.
7+
//!
8+
//! This implementation solves the N-Queens problem using a backtracking algorithm.
9+
//! It starts with an empty chessboard and iteratively tries to place queens in
10+
//! different rows, ensuring they do not conflict with each other. If a valid
11+
//! solution is found, it's added to the list of solutions.
12+
13+
/// Solves the N-Queens problem for a given size and returns a vector of solutions.
14+
///
15+
/// # Arguments
16+
///
17+
/// * `n` - The size of the chessboard (NxN).
18+
///
19+
/// # Returns
20+
///
21+
/// A vector containing all solutions to the N-Queens problem.
3222
pub fn n_queens_solver(n: usize) -> Vec<Vec<String>> {
33-
let mut board = vec![vec!['.'; n]; n];
34-
let mut solutions = Vec::new();
35-
solve(&mut board, 0, &mut solutions);
36-
solutions
23+
let mut solver = NQueensSolver::new(n);
24+
solver.solve()
25+
}
26+
27+
/// Represents a solver for the N-Queens problem.
28+
struct NQueensSolver {
29+
// The size of the chessboard
30+
size: usize,
31+
// A 2D vector representing the chessboard where '.' denotes an empty space and 'Q' denotes a queen
32+
board: Vec<Vec<char>>,
33+
// A vector to store all valid solutions
34+
solutions: Vec<Vec<String>>,
3735
}
3836

39-
fn is_safe(board: &[Vec<char>], row: usize, col: usize) -> bool {
40-
// Check if there is a queen in the same column
41-
for (i, _) in board.iter().take(row).enumerate() {
42-
if board[i][col] == 'Q' {
43-
return false;
37+
impl NQueensSolver {
38+
/// Creates a new `NQueensSolver` instance with the given size.
39+
///
40+
/// # Arguments
41+
///
42+
/// * `size` - The size of the chessboard (N×N).
43+
///
44+
/// # Returns
45+
///
46+
/// A new `NQueensSolver` instance.
47+
fn new(size: usize) -> Self {
48+
NQueensSolver {
49+
size,
50+
board: vec![vec!['.'; size]; size],
51+
solutions: Vec::new(),
4452
}
4553
}
4654

47-
// Check if there is a queen in the left upper diagonal
48-
for i in (0..row).rev() {
49-
let j = col as isize - (row as isize - i as isize);
50-
if j >= 0 && board[i][j as usize] == 'Q' {
51-
return false;
52-
}
55+
/// Solves the N-Queens problem and returns a vector of solutions.
56+
///
57+
/// # Returns
58+
///
59+
/// A vector containing all solutions to the N-Queens problem.
60+
fn solve(&mut self) -> Vec<Vec<String>> {
61+
self.solve_helper(0);
62+
std::mem::take(&mut self.solutions)
5363
}
5464

55-
// Check if there is a queen in the right upper diagonal
56-
for i in (0..row).rev() {
57-
let j = col + row - i;
58-
if j < board.len() && board[i][j] == 'Q' {
59-
return false;
65+
/// Checks if it's safe to place a queen at the specified position (row, col).
66+
///
67+
/// # Arguments
68+
///
69+
/// * `row` - The row index of the position to check.
70+
/// * `col` - The column index of the position to check.
71+
///
72+
/// # Returns
73+
///
74+
/// `true` if it's safe to place a queen at the specified position, `false` otherwise.
75+
fn is_safe(&self, row: usize, col: usize) -> bool {
76+
// Check column and diagonals
77+
for i in 0..row {
78+
if self.board[i][col] == 'Q'
79+
|| (col >= row - i && self.board[i][col - (row - i)] == 'Q')
80+
|| (col + row - i < self.size && self.board[i][col + (row - i)] == 'Q')
81+
{
82+
return false;
83+
}
6084
}
85+
true
6186
}
6287

63-
true
64-
}
65-
66-
fn solve(board: &mut Vec<Vec<char>>, row: usize, solutions: &mut Vec<Vec<String>>) {
67-
let n = board.len();
68-
if row == n {
69-
let solution: Vec<String> = board.iter().map(|row| row.iter().collect()).collect();
70-
solutions.push(solution);
71-
return;
72-
}
88+
/// Recursive helper function to solve the N-Queens problem.
89+
///
90+
/// # Arguments
91+
///
92+
/// * `row` - The current row being processed.
93+
fn solve_helper(&mut self, row: usize) {
94+
if row == self.size {
95+
self.solutions
96+
.push(self.board.iter().map(|row| row.iter().collect()).collect());
97+
return;
98+
}
7399

74-
for col in 0..n {
75-
if is_safe(board, row, col) {
76-
board[row][col] = 'Q';
77-
solve(board, row + 1, solutions);
78-
board[row][col] = '.';
100+
for col in 0..self.size {
101+
if self.is_safe(row, col) {
102+
self.board[row][col] = 'Q';
103+
self.solve_helper(row + 1);
104+
self.board[row][col] = '.';
105+
}
79106
}
80107
}
81108
}
@@ -84,17 +111,111 @@ fn solve(board: &mut Vec<Vec<char>>, row: usize, solutions: &mut Vec<Vec<String>
84111
mod tests {
85112
use super::*;
86113

87-
#[test]
88-
fn test_n_queens_solver() {
89-
// Test case: Solve the 4-Queens problem
90-
let solutions = n_queens_solver(4);
91-
92-
assert_eq!(solutions.len(), 2); // There are two solutions for the 4-Queens problem
93-
94-
// Verify the first solution
95-
assert_eq!(solutions[0], vec![".Q..", "...Q", "Q...", "..Q."]);
114+
macro_rules! test_n_queens_solver {
115+
($($name:ident: $tc:expr,)*) => {
116+
$(
117+
#[test]
118+
fn $name() {
119+
let (n, expected_solutions) = $tc;
120+
let solutions = n_queens_solver(n);
121+
assert_eq!(solutions, expected_solutions);
122+
}
123+
)*
124+
};
125+
}
96126

97-
// Verify the second solution
98-
assert_eq!(solutions[1], vec!["..Q.", "Q...", "...Q", ".Q.."]);
127+
test_n_queens_solver! {
128+
test_0_queens: (0, vec![Vec::<String>::new()]),
129+
test_1_queen: (1, vec![vec!["Q"]]),
130+
test_2_queens:(2, Vec::<Vec<String>>::new()),
131+
test_3_queens:(3, Vec::<Vec<String>>::new()),
132+
test_4_queens: (4, vec![
133+
vec![".Q..",
134+
"...Q",
135+
"Q...",
136+
"..Q."],
137+
vec!["..Q.",
138+
"Q...",
139+
"...Q",
140+
".Q.."],
141+
]),
142+
test_5_queens:(5, vec![
143+
vec!["Q....",
144+
"..Q..",
145+
"....Q",
146+
".Q...",
147+
"...Q."],
148+
vec!["Q....",
149+
"...Q.",
150+
".Q...",
151+
"....Q",
152+
"..Q.."],
153+
vec![".Q...",
154+
"...Q.",
155+
"Q....",
156+
"..Q..",
157+
"....Q"],
158+
vec![".Q...",
159+
"....Q",
160+
"..Q..",
161+
"Q....",
162+
"...Q."],
163+
vec!["..Q..",
164+
"Q....",
165+
"...Q.",
166+
".Q...",
167+
"....Q"],
168+
vec!["..Q..",
169+
"....Q",
170+
".Q...",
171+
"...Q.",
172+
"Q...."],
173+
vec!["...Q.",
174+
"Q....",
175+
"..Q..",
176+
"....Q",
177+
".Q..."],
178+
vec!["...Q.",
179+
".Q...",
180+
"....Q",
181+
"..Q..",
182+
"Q...."],
183+
vec!["....Q",
184+
".Q...",
185+
"...Q.",
186+
"Q....",
187+
"..Q.."],
188+
vec!["....Q",
189+
"..Q..",
190+
"Q....",
191+
"...Q.",
192+
".Q..."],
193+
]),
194+
test_6_queens: (6, vec![
195+
vec![".Q....",
196+
"...Q..",
197+
".....Q",
198+
"Q.....",
199+
"..Q...",
200+
"....Q."],
201+
vec!["..Q...",
202+
".....Q",
203+
".Q....",
204+
"....Q.",
205+
"Q.....",
206+
"...Q.."],
207+
vec!["...Q..",
208+
"Q.....",
209+
"....Q.",
210+
".Q....",
211+
".....Q",
212+
"..Q..."],
213+
vec!["....Q.",
214+
"..Q...",
215+
"Q.....",
216+
".....Q",
217+
"...Q..",
218+
".Q...."],
219+
]),
99220
}
100221
}

0 commit comments

Comments
 (0)