1. Knight Tour (2D arrays)
Knight Tour
The Knight Tour puzzle asks if it is possible to move a knight on an empty
chess board such that the knight visits each square on the board exactly once.
The challenge is to select the moves carefully, so that the knight does not
"get stuck" on a square from which no legal move is possible either because all
the other possible squares have been visited or because a move would force the
knight off the board.
For more information on the puzzle and an interactive demo visit:
https://www.maths-resources.com/knights/
The Strategy
The naive strategy is quite simple: the knight always examines the possible moves
in some fixed order around its current position and picks the first empty square.
This strategy does not work since it sometimes gets stuck and does not find the
complete tour, but it does ok: depending on where the knight starts it can find
a tour of length between 44--54 moves.
Even though this strategy does not work it is still instructive to design and
implement a Java program for it. It provides a nice example of using 2D arrays
and designing several helper methods that complete the overall task.
The design
We started with two methods:
1. void playKnightTour(int numRows, int numCols, int startRow, int startCol)
This is the method that drives the whole process. It takes the dimensions of
a board and initial starting location for the knight. It creates a board and
fills it with numbers indicating which cell has been visited on each move.
(The board is initially empty, i.e. all cells are 0.)
2. int[] findNextMove(int[][] board, int curRow, int curCol)
This method takes as input the current configuration of the chess board
(i.e. with some cells visited) and the current position of the knight.
It systematically explores all the possible moves the knight can make
from its current position and returns the first legal move, i.e. a move
that has the following proerties:
- the move location is on the board
- the cell at that location is an empty square
The move is returned as a pair of numbers stored in a 1D array {row, col}.
If no valid move exists the special Java value null is returned.
The outline
Here is the outline of the playKnightTour method:
1. Keep track of current location of the knight.
(initialized to the given startRow and startCol)
2. Mark as visited the cell that corresponds to the
current position of the knight.
3. Get the next move.
4. As long as the next move is valid:
5. Update the knight location.
6. Mark as visited the new cell that
corresponds to current knight location.
7. Get the next move.
The implementation
Here is the code for playKnightTour. It assumes that method findNextMove will
eventually be written -- what is important at this stage is to know:
- what findNextMove returns
- how to call findNextMove, i.e. what information to give it.
void playKnightTour( int numRows, int numCols, int startRow, int startCol )
{
// create empty board
int[][] board = new int[numRows][numCols];
// keep track of knight position
int curRow = startRow; // Step 1.
int curCol = startCol; // Step 1.
// mark the cell as visited
int steps = 1;
board[curRow][curCol] = steps; // Step 2.
int[] move = findNextMove( board, curRow, curCol ); // Step 3.
while ( move != null ) // Step 4.
{
// update current position
curRow = move[0]; // Step 5.
curCol = move[1]; // Step 5.
// mark the board
steps = steps + 1;
board[curRow][curCol] = steps;
// see if another move possible
move = findNextMove( board, curRow, curCol );
}
System.out.println( "Knight made " + steps + " moves!" );
printTable( board );
}
Here is a version that uses do-while loop. (The version with while
loop can be made just as short.)
void playKnightTour( int numRows, int numCols, int startRow, int startCol )
{
// create empty board
int[][] board = new int[numRows][numCols];
int steps = 1; // index of move to make
int[] move = { startRow, startCol }; // the move we are about to make
do
{
// unpack the move
int curRow = move[0];
int curCol = move[1];
// mark the board
board[curRow][curCol] = steps;
steps = steps + 1;
// see if another move possible
move = findNextMove( board, curRow, curCol );
}
while ( move != null )
System.out.println( "Knight made " + steps + " moves!" );
printTable( board );
}
The method findNextMove
This method must find a systematic way to explore all possible moves relative to
the current location.
There are 8 possibilities where the knight could land. We could write a sequence
of if--else if statements that check each possibility in turn. However, we can
design the solution by making effective use of arrays and for loops. The arrays
will serve as a look up table that will help derive the next location to explore.
Here is a table of the possible knight moves relative to a current location. The
numbers in each square indicate what needs to be added to or subtracted from the
current (row, column) to get the coordinates of the new location:
0 1 2 3 4 5
+-------+-------+-------+-------+-------+-------+
0 | | | | | | |
| | | | | | |
+-------+-------+-------+-------+-------+-------+
1 | | move 7| | move 0| | |
| | -2,-1 | | -2,+1 | | |
+-------+-------+-------+-------+-------+-------+
2 | move 6| | | | move 1| |
| -1,-2 | | | | -1,+2 | |
+-------+-------+-------+-------+-------+-------+
3 | | |row,col| | | |
| | |knight | | | |
+-------+-------+-------+-------+-------+-------+
4 | move 5| | | | move 2| |
| +1,-2 | | | | +1,+2 | |
+-------+-------+-------+-------+-------+-------+
5 | | move 4| | move 3| | |
| | +2,-1 | | +2,+1 | | |
+-------+-------+-------+-------+-------+-------+
6 | | | | | | |
| | | | | | |
+-------+-------+-------+-------+-------+-------+
The idea is to keep two arrays:
- one array for the vertical (row) displacements
- one array for the horizontal (column) displacements
Then traverse the two arrays and calculate the new location. The new location is
good if:
- it is within the board
- it is empty (has not been marked)
Here is the Java code:
int[] findNextMove( int[][] board, int curRow, int curCol )
{
// jumps from current position to new reachable squares
int[] rowJumps = { -2, -1, +1, +2, +2, +1 -1, -2 };
int[] colJumps = { +1, +2, +2, +1, -1, -2 -2, -1 };
// try all possible knight moves
for ( int i = 0 ; i < rowJumps.length ; i = i + 1 )
{
// coordinates of potential move
int nextRow = curRow + rowJumps[i];
int nextCol = curCol + colJumps[i];
// if move is within bounds and unmarked
if ( IS-WITHIN-BOARD && (board[nextRow][nextCol] == 0) )
{
int[] move = { nextRow, nextCol };
return move;
}
}
// no move found
return null;
}
Putting it all together
With the exception of a method that checks if the move is legal everything
else is in place. Here is the overall structure:
public class KnightTourApp
{
int[] findNextMove( int[][] board, int curRow, int curCol )
{
// jumps from current position to new reachable squares
int[] rowJumps = { -2, -1, +1, +2, +2, +1 -1, -2 };
int[] colJumps = { +1, +2, +2, +1, -1, -2 -2, -1 };
// try all possible knight moves
for ( int i = 0 ; i < rowJumps.length ; i = i + 1 )
{
// coordinates of potential move
int nextRow = curRow + rowJumps[i];
int nextCol = curCol + colJumps[i];
// if move is within bounds and unmarked
if ( IS-WITHIN-BOARD && (board[nextRow][nextCol] == 0) )
{
int[] move = { nextRow, nextCol };
return move;
}
}
// no move found
return null;
}
void playKnightTour( int numRows, int numCols, int startRow, int startCol )
{
// create empty board
int[][] board = new int[numRows][numCols];
// keep track of knight position
int curRow = startRow;
int curCol = startCol;
// mark the cell as visited
int steps = 1;
board[curRow][curCol] = steps;
int[] move = findNextMove( board, curRow, curCol );
while ( move != null )
{
// update current position
curRow = move[0]; // Step 5.
curCol = move[1]; // Step 5.
// mark the board
steps = steps + 1;
board[curRow][curCol] = steps;
// see if another move possible
move = findNextMove( board, curRow, curCol );
}
System.out.println( "Knight made " + steps + " moves!" );
printTable( board );
}
public void run()
{
// 8x8 board, start at (4,4)
playKnightTour( 8, 8, 4, 4 );
}
}