forked from TheAlgorithms/Rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nqueens.rs
148 lines (124 loc) · 4.21 KB
/
nqueens.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#[allow(unused_imports)]
use std::env::args;
#[allow(dead_code)]
fn main() {
let mut board_width = 0;
for arg in args() {
board_width = match arg.parse() {
Ok(x) => x,
_ => 0,
};
if board_width != 0 {
break;
}
}
if board_width < 4 {
println!(
"Running algorithm with 8 as a default. Specify an alternative Chess board size for \
N-Queens as a command line argument.\n"
);
board_width = 8;
}
let board = match nqueens(board_width) {
Ok(success) => success,
Err(err) => panic!("{}", err),
};
println!("N-Queens {} by {} board result:", board_width, board_width);
print_board(&board);
}
/*
The n-Queens search is a backtracking algorithm. Each row of the Chess board where a Queen is
placed is dependent on all earlier rows. As only one Queen can fit per row, a one-dimensional
integer array is used to represent the Queen's offset on each row.
*/
pub fn nqueens(board_width: i64) -> Result<Vec<i64>, &'static str> {
let mut board_rows = vec![0; board_width as usize];
let mut conflict;
let mut current_row = 0;
//Process by row up to the current active row
loop {
conflict = false;
//Column review of previous rows
for review_index in 0..current_row {
//Calculate the diagonals of earlier rows where a Queen would be a conflict
let left = board_rows[review_index] - (current_row as i64 - review_index as i64);
let right = board_rows[review_index] + (current_row as i64 - review_index as i64);
if board_rows[current_row] == board_rows[review_index]
|| (left >= 0 && left == board_rows[current_row])
|| (right < board_width as i64 && right == board_rows[current_row])
{
conflict = true;
break;
}
}
match conflict {
true => {
board_rows[current_row] += 1;
if current_row == 0 && board_rows[current_row] == board_width {
return Err("No solution exists for specificed board size.");
}
while board_rows[current_row] == board_width {
board_rows[current_row] = 0;
if current_row == 0 {
return Err("No solution exists for specificed board size.");
}
current_row -= 1;
board_rows[current_row] += 1;
}
}
_ => {
current_row += 1;
if current_row as i64 == board_width {
break;
}
}
}
}
Ok(board_rows)
}
fn print_board(board: &[i64]) {
for row in 0..board.len() {
print!("{}\t", board[row as usize]);
for column in 0..board.len() as i64 {
if board[row as usize] == column {
print!("Q");
} else {
print!(".");
}
}
println!();
}
}
#[cfg(test)]
mod test {
use super::*;
fn check_board(board: &Vec<i64>) -> bool {
for current_row in 0..board.len() {
//Column review
for review_index in 0..current_row {
//Look for any conflict.
let left = board[review_index] - (current_row as i64 - review_index as i64);
let right = board[review_index] + (current_row as i64 - review_index as i64);
if board[current_row] == board[review_index]
|| (left >= 0 && left == board[current_row])
|| (right < board.len() as i64 && right == board[current_row])
{
return false;
}
}
}
true
}
#[test]
fn test_board_size_4() {
let board = nqueens(4).expect("Error propagated.");
assert_eq!(board, vec![1, 3, 0, 2]);
assert!(check_board(&board));
}
#[test]
fn test_board_size_7() {
let board = nqueens(7).expect("Error propagated.");
assert_eq!(board, vec![0, 2, 4, 6, 1, 3, 5]);
assert!(check_board(&board));
}
}