Search code examples
javaarraysalgorithmknights-tour

Knights Tour Algorithm


I'm trying to write a Knights Tour Algorithm which has two arrays, ACCESS and board. ACCESS is the array I use to figure out what the next move is and board is the array that the user will see as the end result. My algorithm checks to find the square with the smallest number of available moves and goes there. If there happen to be 2 possible moves with the same number of available moves, I find which one is furthest from the center (closest to the boundaries) and move to that spot. This algorithm SHOULD give a perfect 64 move knights tour program all the time but I usually only get about 60 moves, can anyone tell me why it doesn't give 64?

import java.util.*;
import java.io.*;
import java.text.DecimalFormat;

class KnightsTour
{
    public static void main(String args[]) throws IOException
    {
        boolean hasnextmove = true;
        Knight knight = new Knight();
        knight.getStart();
        do
        {
            knight.move();
            knight.newposition();
            hasnextmove = knight.hasnextmove();
        }while(hasnextmove == true);
        knight.displayBoard();
    }
}

class Knight
{
    DecimalFormat twoDigits = new DecimalFormat("00");
    private int board[][];
    private int startRow, startCol, rowPos, colPos, smallest;
    private int k = 2;
    private boolean move = true;
    final private int ACCESS[][] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                                    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                                    {0, 0, 2, 3, 4, 4, 4, 4, 3, 2, 0, 0},
                                    {0, 0, 3, 4, 6, 6, 6, 6, 4, 3, 0, 0},
                                    {0, 0, 4, 6, 8, 8, 8, 8, 6, 4, 0, 0},
                                    {0, 0, 4, 6, 8, 8, 8, 8, 6, 4, 0, 0},
                                    {0, 0, 4, 6, 8, 8, 8, 8, 6, 4, 0, 0},
                                    {0, 0, 4, 6, 8, 8, 8, 8, 6, 4, 0, 0},
                                    {0, 0, 3, 4, 6, 6, 6, 6, 4, 3, 0, 0},
                                    {0, 0, 2, 3, 4, 4, 4, 4, 3, 2, 0, 0},
                                    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                                    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
//                                      constructor, initializes values and the board
    public Knight()
    {
        board = new int[8][8];
        for(int i = 0; i < 8; i++)
            for(int k = 0; k < 8; k++)
                board[i][k] = 0;
        startRow = 0;
        startCol = 0;
        rowPos = 0;
        colPos = 0;
    }
//                                      tests to see if there is another move available
    public boolean hasnextmove()
    {
        if(ACCESS[rowPos + 1][colPos + 2] != 0 || ACCESS[rowPos + 1][colPos - 2] != 0 || ACCESS[rowPos - 1][colPos + 2] != 0 || ACCESS[rowPos - 1][colPos - 2] != 0 || ACCESS[rowPos - 2][colPos + 1] != 0 || ACCESS[rowPos - 2][colPos - 1] != 0 || ACCESS[rowPos + 2][colPos - 1] != 0 || ACCESS[rowPos + 2][colPos + 1] != 0)
            return true;
        else
            return false;
    }
//                                      gets user input for starting square of the knight
    public void getStart() throws IOException
    {
        Scanner input = new Scanner(System.in);
        System.out.println("Please input the starting row number of the knight: ");
        startRow = input.nextInt() + 1;
        System.out.println("Please input the starting column number of the knight: ");
        startCol = input.nextInt() + 1;
        rowPos = startRow;
        colPos = startCol;
        board[startRow - 2][startCol - 2] = 1;
        ACCESS[startRow][startCol] = 0;
    }
//                                      displays the board
    public void displayBoard()
    {
        System.out.println("This is the game board");
        for(int i = 0; i < 8; i++)
        {
            for(int k = 0; k < 8; k++)
            {
                System.out.print(twoDigits.format(board[i][k]) + " ");
            }
            System.out.println();
        }
    }
//                                      sees if there is a possible move and if so, what is the smallest number space that the knight can move
    public void move()
    {
        smallest = 50;

        if(ACCESS[rowPos + 1][colPos + 2] != 0 || ACCESS[rowPos + 1][colPos - 2] != 0 || ACCESS[rowPos - 1][colPos + 2] != 0 || ACCESS[rowPos - 1][colPos - 2] != 0 || ACCESS[rowPos - 2][colPos + 1] != 0 || ACCESS[rowPos - 2][colPos - 1] != 0 || ACCESS[rowPos + 2][colPos - 1] != 0 || ACCESS[rowPos + 2][colPos + 1] != 0)
            move = true;
        else
            move = false;

        if(move == true)
        {
            if(ACCESS[rowPos + 1][colPos + 2] < smallest && ACCESS[rowPos + 1][colPos + 2] != 0)
                smallest = ACCESS[rowPos + 1][colPos + 2];

            if(ACCESS[rowPos + 1][colPos - 2] < smallest && ACCESS[rowPos + 1][colPos - 2] != 0)
                smallest = ACCESS[rowPos + 1][colPos - 2];

            if(ACCESS[rowPos - 1][colPos + 2] < smallest && ACCESS[rowPos - 1][colPos + 2] != 0)
                smallest = ACCESS[rowPos - 1][colPos + 2];

            if(ACCESS[rowPos - 1][colPos - 2] < smallest && ACCESS[rowPos - 1][colPos - 2] != 0)
                smallest = ACCESS[rowPos - 1][colPos - 2];

            if(ACCESS[rowPos + 2][colPos + 1] < smallest && ACCESS[rowPos + 2][colPos + 1] != 0)
                smallest = ACCESS[rowPos + 2][colPos + 1];

            if(ACCESS[rowPos + 2][colPos - 1] < smallest && ACCESS[rowPos + 2][colPos - 1] != 0)
                smallest = ACCESS[rowPos + 2][colPos - 1];

            if(ACCESS[rowPos - 2][colPos + 1] < smallest && ACCESS[rowPos - 2][colPos + 1] != 0)
                smallest = ACCESS[rowPos - 2][colPos + 1];

            if(ACCESS[rowPos - 2][colPos - 1] < smallest && ACCESS[rowPos - 2][colPos - 1] != 0)
                smallest = ACCESS[rowPos - 2][colPos - 1];
        }
    }
//                                          moves the knight to the smallest numbered square it can
    public void newposition()
    {
        int temprow = rowPos;
        int tempcol = colPos;
        int possiblemoves = 0;
        boolean moved = false;
        boolean specialcasemoved = false;
//                                          moves pieces to new spot
        if(ACCESS[rowPos - 2][colPos + 1] == smallest && moved == false)
        {
            temprow = rowPos - 2;
            tempcol = colPos + 1;
            possiblemoves++;
        }
        if(ACCESS[rowPos - 1][colPos + 2] == smallest && moved == false)
        {
            temprow = rowPos - 1;
            tempcol = colPos + 2;
            possiblemoves++;
        }
        if(ACCESS[rowPos + 1][colPos + 2] == smallest && moved == false)
        {
            temprow = rowPos + 1;
            tempcol = colPos + 2;
            possiblemoves++;
        }
        if(ACCESS[rowPos + 2][colPos + 1] == smallest && moved == false)
        {
            temprow = rowPos + 2;
            tempcol = colPos + 1;
            possiblemoves++;
        }
        if(ACCESS[rowPos + 2][colPos - 1] == smallest && moved == false)
        {
            temprow = rowPos + 2;
            tempcol = colPos - 1;
            possiblemoves++;
        }
        if(ACCESS[rowPos + 1][colPos - 2] == smallest && moved == false)
        {
            temprow = rowPos + 1;
            tempcol = colPos - 2;
            possiblemoves++;
        }
        if(ACCESS[rowPos - 1][colPos - 2] == smallest && moved == false)
        {
            temprow = rowPos - 1;
            tempcol = colPos - 2;
            possiblemoves++;
        }
        if(ACCESS[rowPos - 2][colPos - 1] == smallest && moved == false)
        {
            temprow = rowPos - 2;
            tempcol = colPos - 1;
            possiblemoves++;
        }
        if(possiblemoves > 1)
        {
            double distance = 0;
            double tempdistance;
            if(ACCESS[rowPos - 2][colPos + 1] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos - 2 - 1)), 2) + Math.pow((6.5 - (colPos + 1 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos - 2;
                    tempcol = colPos + 1;
                }
            }
            if(ACCESS[rowPos - 1][colPos + 2] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos - 1 - 1)), 2) + Math.pow((6.5 - (colPos + 2 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos - 1;
                    tempcol = colPos + 2;
                }
            }
            if(ACCESS[rowPos + 1][colPos + 2] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos + 1 - 1)), 2) + Math.pow((6.5 - (colPos + 2 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos + 1;
                    tempcol = colPos + 2;
                }
            }
            if(ACCESS[rowPos +2][colPos + 1] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos + 2 - 1)), 2) + Math.pow((6.5 - (colPos + 1 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos + 2;
                    tempcol = colPos + 1;
                }
            }
            if(ACCESS[rowPos + 2][colPos - 1] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos + 2 - 1)), 2) + Math.pow((6.5 - (colPos - 1 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos + 2;
                    tempcol = colPos - 1;
                }
            }
            if(ACCESS[rowPos + 1][colPos - 2] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos + 1 - 1)), 2) + Math.pow((6.5 - (colPos - 2 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos + 1;
                    tempcol = colPos - 2;
                }
            }
            if(ACCESS[rowPos - 1][colPos - 2] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos - 1 - 1)), 2) + Math.pow((6.5 - (colPos - 2 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos - 1;
                    tempcol = colPos - 2;
                }
            }
            if(ACCESS[rowPos - 2][colPos - 1] == smallest)
            {
                tempdistance = Math.sqrt(Math.pow((6.5 - (rowPos - 2 - 1)), 2) + Math.pow((6.5 - (colPos - 1 - 1)), 2));
                if(tempdistance > distance)
                {
                    distance = tempdistance;
                    temprow = rowPos - 2;
                    tempcol = colPos - 1;
                }
            }
/*          boolean m1, m2, m3, m4, m5, m6, m7, m8;
            m1 = m2 = m3 = m4 = m5 = m6 = m7 = m8 = false;
            int randomnumber;
            if(ACCESS[rowPos - 2][colPos + 1] == smallest)
            {
                m1 = true;
            }
            if(ACCESS[rowPos - 1][colPos + 2] == smallest)
            {
                m2 = true;
            }
            if(ACCESS[rowPos + 1][colPos + 2] == smallest)
            {
                m3 = true;
            }
            if(ACCESS[rowPos + 2][colPos + 1] == smallest)
            {
                m4 = true;
            }
            if(ACCESS[rowPos + 2][colPos - 1] == smallest)
            {
                m5 = true;
            }
            if(ACCESS[rowPos + 1][colPos - 2] == smallest)
            {
                m6 = true;
            }
            if(ACCESS[rowPos - 1][colPos - 2] == smallest)
            {
                m7 = true;
            }
            if(ACCESS[rowPos - 2][colPos - 1] == smallest)
            {
                m8 = true;
            }
            do
            {
                Random rand = new Random();
                int randomNum = (int) (rand.nextInt(6)+1) + 1;

                switch(randomNum) 
                {
                    case 1:
                        if(m1 == true)
                        {
                            temprow = rowPos - 2;
                            tempcol = colPos + 1;
                            specialcasemoved = true;
                        }
                    case 2:
                        if(m2 == true)
                        {
                            temprow = rowPos - 1;
                            tempcol = colPos + 2;
                            specialcasemoved = true;
                        }
                    case 3:
                        if(m3 == true)
                        {
                            temprow = rowPos + 1;
                            tempcol = colPos + 2;
                            specialcasemoved = true;
                        }
                    case 4:
                        if(m4 == true)
                        {
                            temprow = rowPos + 2;
                            tempcol = colPos + 1;
                            specialcasemoved = true;
                        }
                    case 5:
                        if(m5 == true)
                        {
                            temprow = rowPos + 2;
                            tempcol = colPos - 1;
                            specialcasemoved = true;
                        }
                    case 6:
                        if(m6 == true)
                        {
                            temprow = rowPos + 1;
                            tempcol = colPos - 2;
                            specialcasemoved = true;
                        }
                    case 7:
                        if(m7 == true)
                        {
                            temprow = rowPos - 1;
                            tempcol = colPos - 2;
                            specialcasemoved = true;
                        }
                    case 8:
                        if(m8 == true)
                        {
                            temprow = rowPos - 2;
                            tempcol = colPos - 1;
                            specialcasemoved = true;
                        }
                }
            }while(specialcasemoved == false);*/
        }
        rowPos = temprow;
        colPos = tempcol;
        System.out.println(possiblemoves);
        possiblemoves = 0;
        ACCESS[rowPos][colPos] = 0;
        board[rowPos - 2][colPos - 2] = k;
        k++;
//      System.out.println(rowPos + " " + colPos);
    }
}

Solution

  • There is no Knight's Tour solution with 60 moves. There are 64 squares on a chess board so a Knight's Tour must have exactly 64 moves (or possibly 63 if it's not a closed loop solution). If you get a solution with 60 moves then your algorithm is broken.

    It is possible that you have misunderstood Warnsdorff's rule if I interpret your description literally. The 'rule' is intended to solve the problem of an exhaustive Knight's Tour algorithm being inefficient due to the number of possibilities. It suggests that when using an exhaustive, depth first, backtracking search algorithm, always explore first the option which itself has the fewest options. This still requires backtracking as even using the rule will sometimes lead to dead ends that need to be backed out of.

    I realise this might not have solved your problem but you have a lot of code posted which makes it complicated to understand exactly what might be going wrong. I believe it can be substantially simplified through better encapsulation. I'd be happy to post some suggestions if it would be helpful - just leave a comment.