An alternative solution to isSafe()
and class Queen
- make the chess board a class to track the status
- recurse
- clone the current board status
- set the queen and block all fields reachable by her
- pass the clone downwards for the next row
- remember the column-per-row positions for each queen
The following is a generic solver which gets passed in a placer
closure. By using the approach it is simple to use the same solver for rooks (placeRook()
), knights (placeKnight()
) or bishops (placeBishop()
).
Please note that my solution is written in Groovy, which also runs on JVM, and is very close to Java. So it should be no problem to translate the juicy bits of the algorithm to Java.
class ChessBoard {
int N
int lastIndex
private boolean[][] board
int solutions
ChessBoard(int n) {
board = new boolean[n][n]
N = n
lastIndex = n - 1
solutions = 0
this.each { int row, int column -> board[row][column] = true }
}
ChessBoard(ChessBoard orig) {
N = orig.getN()
board = new boolean[N][N]
lastIndex = N - 1
solutions = 0
this.each { int row, int column -> board[row][column] = orig.getField(row, column) }
}
void each(Closure c) {
(0..lastIndex).each { row ->
(0..lastIndex).each { column -> c(row, column) }
}
}
void print() {
println " ${'-' * N}"
(0..lastIndex).each { row ->
print "|"
(0..lastIndex).each { column -> print "${board[row][column] ? ' ' : 'X'}" }
println "|"
}
println " ${'-' * N}"
}
int getN() { return N }
int getSolutions() { return solutions }
boolean getField(int row, int column) { return board[row][column] }
void blockField(int row, int column) {
if ((row < 0) || (row > lastIndex))
return
if ((column < 0) || (column > lastIndex))
return
board[row][column] = false
}
List<Integer> getFree(int row) {
(0..lastIndex).findResults { int column -> board[row][column] ? column : null }
}
void placeQueen(int row, int column, boolean all = true) {
if (all) {
(0..lastIndex).each { offset ->
blockField(row, offset) // row
blockField(offset, column) // column
blockField(row + offset, column + offset) // diagonals
blockField(row + offset, column - offset)
blockField(row - offset, column + offset)
blockField(row - offset, column - offset)
}
} else {
blockField(row, column)
}
}
// recursive solver
void solve(ChessBoard previous, List<Integer> columns, int row, Closure placer) {
List<Integer> free = previous.getFree(row)
if (row < lastIndex) {
// recurse
free.each { column ->
ChessBoard work = new ChessBoard(previous)
columns[row] = column
placer(work, row, column, true)
solve(work, columns, row + 1, placer)
}
} else {
// solutions
free.each { column ->
ChessBoard solution = new ChessBoard(N)
columns[row] = column
(0..lastIndex).each { placer(solution, it, columns[it], false) }
println "Solution #${++solutions}:"
solution.print()
}
}
}
// start recursion
void solve(Closure placer) {
List<Integer> columns = []
solve(this, columns, 0, placer)
}
}
board = new ChessBoard(8)
board.solve { ChessBoard work, int row, int column, boolean all -> work.placeQueen(row, column, all) }
println "Solutions: ${board.getSolutions()}"
Test run:
Solution #1:
--------
|X |
| X |
| X|
| X |
| X |
| X |
| X |
| X |
--------
...
Solution #92:
--------
| X|
| X |
|X |
| X |
| X |
| X |
| X |
| X |
--------
Solutions: 92
If my memory serves me correctly, 92 does sound correct for the 8-Queen problem. But it has been over 35 years since I solved this in school using an iterative approach in Pascal :-)
UPDATE improved solution
- split original class into
ChessBoard
for tracking state and Solver
for the algorithm
- placers for queens, rooks, bishops & knights
- calculate solutions for sizes 1 to 8
- generate markdown table for the results
class ChessBoard {
private int N
private int lastIndex
private boolean[][] state
ChessBoard(int n) {
N = n
lastIndex = N - 1
state = new boolean[N][N]
(0..lastIndex).each { row ->
(0..lastIndex).each { column ->
setField(row, column, true)
}
}
}
ChessBoard(ChessBoard orig) {
N = orig.getN()
lastIndex = N - 1
state = new boolean[N][N]
(0..lastIndex).each { row ->
(0..lastIndex).each { column ->
setField(row, column, orig.getField(row, column))
}
}
}
int getN() {
return N
}
boolean getField(int row, int column) {
return state[row][column]
}
void setField(int row, int column, boolean free = false) {
if ((row < 0) || (row > lastIndex))
return
if ((column < 0) || (column > lastIndex))
return
state[row][column] = free
}
List<Integer> getFree(int row) {
(0..lastIndex)
.findResults { int column ->
getField(row, column) ? column : null
}
}
// for debugging only
void print() {
println " ${'-' * N}"
(0..lastIndex).each { row ->
print "|"
(0..lastIndex).each { column -> print "${getField(row, column) ? ' ' : 'X'}" }
println "|"
}
println " ${'-' * N}"
}
}
class Solver {
private int N
private int lastIndex
private int solutions
private int[] columns
Solver(int n) {
N = n
lastIndex = N - 1
solutions = 0
columns = new int[N]
}
void printSolution(String label) {
solutions++
if (!label)
return
println "${N}-${label} solution #${solutions}"
println " ${'-' * N}"
(0..lastIndex).each { row ->
int column = columns[row]
println "|${' ' * column}X${' ' * (lastIndex - column)}|"
}
println " ${'-' * N}"
}
int getSolutions() {
return solutions
}
void placeQueen(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
(1..(lastIndex - row)).each { offset ->
board.setField(row + offset, column) // column
board.setField(row + offset, column + offset) // diagonals
board.setField(row + offset, column - offset)
}
}
void placeRook(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
(1..(lastIndex - row)).each { offset ->
board.setField(row + offset, column) // column
}
}
void placeBishop(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
(1..(lastIndex - row)).each { offset ->
board.setField(row + offset, column + offset) // diagonals
board.setField(row + offset, column - offset)
}
}
static void placeKnight(ChessBoard board, int row, int column) {
// only modify fields from (row+1) downwards
board.setField(row + 1, column - 2)
board.setField(row + 1, column + 2)
board.setField(row + 2, column - 1)
board.setField(row + 2, column + 1)
}
// recursive solver
void solve(ChessBoard previous, int row, Closure placer, String label) {
List<Integer> free = previous.getFree(row)
if (row < lastIndex) {
// recurse
free.each { column ->
ChessBoard work = new ChessBoard(previous)
columns[row] = column
placer(this, work, row, column)
solve(work, row + 1, placer, label)
}
} else {
// solutions
free.each { column ->
columns[row] = column
printSolution(label)
}
}
}
// start recursion
int solve(Closure placer, String label = null) {
solve(new ChessBoard(N), 0, placer, label)
return solutions
}
}
Map<String, Closure> placers = [
'Queens': { Solver solver, ChessBoard board, int row, int column -> solver.placeQueen(board, row, column) },
'Rooks': { Solver solver, ChessBoard board, int row, int column -> solver.placeRook(board, row, column) },
'Bishops': { Solver solver, ChessBoard board, int row, int column -> solver.placeBishop(board, row, column) },
'Knights': { Solver solver, ChessBoard board, int row, int column -> solver.placeKnight(board, row, column) },
]
Map<String, List<Integer>> solutions = [:]
// generate solutions up to maxN
int maxN = 8
boolean print = false
placers
.keySet()
.each { String key ->
Closure placer = placers[key]
List<Integer> results = []
(1..maxN).each { N ->
results.push(new Solver(N).solve(placer, print ? key : null))
}
solutions[key] = results
}
// generate markdown table from solutions
List lines = []
(0..maxN).each { lines[it] = [it ?: 'Size'] }
[
'Queens',
'Rooks',
'Bishops',
'Knights',
].each { key ->
List<Integer> results = solutions[key]
lines[0].push(key)
(1..maxN).each { lines[it].push(results[it - 1]) }
}
lines.each { line -> println line.join('|') }
return
Result table:
| Size | Queens | Rooks | Bishops | Knights |
|------|--------|-------|---------|---------|
| 1 | 1 | 1 | 1 | 1 |
| 2 | 0 | 2 | 2 | 4 |
| 3 | 0 | 6 | 5 | 9 |
| 4 | 2 | 24 | 24 | 52 |
| 5 | 10 | 120 | 125 | 451 |
| 6 | 4 | 720 | 796 | 4898 |
| 7 | 40 | 5040 | 5635 | 67381 |
| 8 | 92 | 40320 | 48042 | 1131382 |
|------|--------|-------|---------|---------|