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 );
    }
}