DrewKestell

KnightsTourEnhanced

Oct 19th, 2014
277
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.30 KB | None | 0 0
  1. /**
  2. Author: Drew Kestell
  3. Date: Oct 17, 2014
  4.  
  5. Exercise 7.22d
  6. An implementation of the Knight's Tour problem.  This algorithm relies on heuristics
  7. to analyze the efficiency of of all possible moves and prioritize accordingly.
  8. If two moves are equal, we look one step further to find the next most efficient move,
  9. and make the move that leads us that direction.
  10. The application repeats the simulation 64 times, starting once from each tile.
  11. */
  12.  
  13. package drewKestellChapter72;
  14.  
  15. import java.util.ArrayList;
  16. import java.util.Random;
  17.  
  18. public class KnightsTourEnhanced
  19. {
  20.     // tracks the total tour attempts.
  21.     static int tourTracker = 0;
  22.    
  23.     // tracks the amount of successful tours
  24.     static int successfulTours = 0;
  25.    
  26.     // random object.
  27.     static Random random = new Random();
  28.     static boolean running = true;
  29.    
  30.     // tracks the amount of moves by the knight each simulation.
  31.     // initialized in the performTour method.
  32.     static int moveCounter;
  33.    
  34.     // this two-dimensional array represents the chess board.
  35.     static int[][] chessBoard = new int[8][8];
  36.    
  37.     // two-dimensional heuristic accessibility array.
  38.     static int[][] accessibility = new int[8][8];
  39.    
  40.     // these two arrays define the 8 possible moves a knight can make.
  41.     static int[] horizontal = new int[8];
  42.     static int[] vertical = new int[8];
  43.    
  44.     // these two ints track the knight's current position on the board.
  45.     // initialized in the performTour method.
  46.     static int currentRow;
  47.     static int currentColumn;
  48.    
  49.     // initializes the chessBoard array to contain all zeros
  50.     public static void initializeChessBoard()
  51.     {
  52.         for(int i = 0; i < 8; i++)
  53.         {
  54.             for(int j = 0; j < 8; j++)
  55.             {
  56.                 chessBoard[i][j] = 0;
  57.             }
  58.         }
  59.     }
  60.        
  61.     // initialize the two arrays which define the 8 possible moves a knight can make.
  62.     public static void initializeMoves()
  63.     {
  64.         // positive numbers indicate the knight is moving right (horizontal) or down (vertical).
  65.         // negative numbers indicate the knight is moving left (horizontal) or up (vertical).
  66.         // for example, move 0 is defined as the knight moving right 2 tiles and up 1 tile.
  67.         horizontal[0] = 2;
  68.         horizontal[1] = 1;
  69.         horizontal[2] = -1;
  70.         horizontal[3] = -2;
  71.         horizontal[4] = -2;
  72.         horizontal[5] = -1;
  73.         horizontal[6] = 1;
  74.         horizontal[7] = 2;
  75.         vertical[0] = -1;
  76.         vertical[1] = -2;
  77.         vertical[2] = -2;
  78.         vertical[3] = -1;
  79.         vertical[4] = 1;
  80.         vertical[5] = 2;
  81.         vertical[6] = 2;
  82.         vertical[7] = 1;
  83.     }
  84.        
  85.     // the accessibility array defines how many possible moves a knight
  86.     // has starting from any given tile on the chess board.
  87.     public static void initializeAccessibility()
  88.     {
  89.         accessibility[0][0] = 2;
  90.         accessibility[0][1] = 3;
  91.         accessibility[0][2] = 4;
  92.         accessibility[0][3] = 4;
  93.         accessibility[0][4] = 4;
  94.         accessibility[0][5] = 4;
  95.         accessibility[0][6] = 3;
  96.         accessibility[0][7] = 2;
  97.         accessibility[1][0] = 3;
  98.         accessibility[1][1] = 4;
  99.         accessibility[1][2] = 6;
  100.         accessibility[1][3] = 6;
  101.         accessibility[1][4] = 6;
  102.         accessibility[1][5] = 6;
  103.         accessibility[1][6] = 4;
  104.         accessibility[1][7] = 3;
  105.         accessibility[2][0] = 4;
  106.         accessibility[2][1] = 6;
  107.         accessibility[2][2] = 8;
  108.         accessibility[2][3] = 8;
  109.         accessibility[2][4] = 8;
  110.         accessibility[2][5] = 8;
  111.         accessibility[2][6] = 6;
  112.         accessibility[2][7] = 4;
  113.         accessibility[3][0] = 4;
  114.         accessibility[3][1] = 6;
  115.         accessibility[3][2] = 8;
  116.         accessibility[3][3] = 8;
  117.         accessibility[3][4] = 8;
  118.         accessibility[3][5] = 8;
  119.         accessibility[3][6] = 6;
  120.         accessibility[3][7] = 4;
  121.         accessibility[4][0] = 4;
  122.         accessibility[4][1] = 6;
  123.         accessibility[4][2] = 8;
  124.         accessibility[4][3] = 8;
  125.         accessibility[4][4] = 8;
  126.         accessibility[4][5] = 8;
  127.         accessibility[4][6] = 6;
  128.         accessibility[4][7] = 4;
  129.         accessibility[5][0] = 4;
  130.         accessibility[5][1] = 6;
  131.         accessibility[5][2] = 8;
  132.         accessibility[5][3] = 8;
  133.         accessibility[5][4] = 8;
  134.         accessibility[5][5] = 8;
  135.         accessibility[5][6] = 6;
  136.         accessibility[5][7] = 4;
  137.         accessibility[6][0] = 3;
  138.         accessibility[6][1] = 4;
  139.         accessibility[6][2] = 6;
  140.         accessibility[6][3] = 6;
  141.         accessibility[6][4] = 6;
  142.         accessibility[6][5] = 6;
  143.         accessibility[6][6] = 4;
  144.         accessibility[6][7] = 3;
  145.         accessibility[7][0] = 2;
  146.         accessibility[7][1] = 3;
  147.         accessibility[7][2] = 4;
  148.         accessibility[7][3] = 4;
  149.         accessibility[7][4] = 4;
  150.         accessibility[7][5] = 4;
  151.         accessibility[7][6] = 3;
  152.         accessibility[7][7] = 2;   
  153.     }
  154.    
  155.     // moves the knight and updates its current location.
  156.     public static void moveKnight(int moveNumber)
  157.     {
  158.         System.out.printf("Knight is currently in row %d and column %d.  Performing move type %d...\n", currentRow, currentColumn, moveNumber);
  159.         currentRow += vertical[moveNumber];
  160.         currentColumn += horizontal[moveNumber];
  161.         chessBoard[currentRow][currentColumn] = 1;
  162.         adjustAccessibility();
  163.         System.out.printf("After the move, knight is now in row %d and column %d.\n\n", currentRow, currentColumn);
  164.        
  165.         if(moveCounter == 63)
  166.         {
  167.             System.out.println("Tour complete!  Printing a summary of the tour:\n");
  168.             for(int i = 0; i < 8; i++)
  169.             {
  170.                 for(int j = 0; j < 8; j++)
  171.                 {
  172.                     System.out.print(chessBoard[i][j] + " ");
  173.                 }
  174.                 System.out.println();
  175.             }
  176.             System.out.println();
  177.             successfulTours++;
  178.         }
  179.     }
  180.    
  181.     // finds all possible moves first by checking if a move will put the knight off the
  182.     // board, then by checking if a tile has already been touched.
  183.     public static ArrayList<Integer> findPossibleMoves(int row, int column)
  184.     {
  185.         ArrayList<Integer> moveTracker = new ArrayList<Integer>(0);
  186.        
  187.         for(int j = 0; j < 8; j++)
  188.         {
  189.             if((row + vertical[j] >= 0 && row + vertical[j] <= 7) &&
  190.                     (column + horizontal[j] >= 0 && column + horizontal[j] <= 7))
  191.             {
  192.                 if(chessBoard[row + vertical[j]][column + horizontal[j]] == 0)
  193.                 {
  194.                     // if move is valid, and has not yet been made, add potential move to moveTracker.
  195.                     moveTracker.add(j);                    
  196.                 }
  197.             }
  198.         }      
  199.         return moveTracker;    
  200.     }
  201.    
  202.     // adjusts the heuristic grid after each move.
  203.     public static void adjustAccessibility()
  204.     {
  205.         ArrayList<Integer> possibleMoves = findPossibleMoves(currentRow, currentColumn);
  206.         for(int i = 0; i < possibleMoves.size(); i++)
  207.         {          
  208.             accessibility[(currentRow + vertical[possibleMoves.get(i)])][(currentColumn + horizontal[possibleMoves.get(i)])]--;
  209.         }
  210.     }
  211.    
  212.     // this method checks each possible move (as determined by the findPossibleMoves method) against the accessibility
  213.     // array to find the best move at any given time.  "harder" moves are prioritized based on heuristic analysis.
  214.     // if two moves are considered equally efficient, a random move is chosen.  this random element is important because
  215.     // it allows the knight to take different routes each simulation, and eventually find a solution.
  216.     // i think i can clean up the logic in this section to make the algorithm more efficient, and the code cleaner.
  217.     public static Integer checkMoveEfficiency(ArrayList<Integer> moveList, int row, int column)
  218.     {          
  219.         int tempCurrentRow = row;
  220.         int tempCurrentColumn = column;
  221.        
  222.         int smallest = accessibility[(tempCurrentRow + vertical[moveList.get(0)])][(tempCurrentColumn + horizontal[moveList.get(0)])];
  223.        
  224.         ArrayList<Integer> returnMoveList = new ArrayList<Integer>(0);
  225.         ArrayList<Integer> returnMoveEfficiency = new ArrayList<Integer>(0);
  226.        
  227.         for(int i = 0; i < moveList.size(); i++)
  228.         {
  229.             if(accessibility[(tempCurrentRow + vertical[moveList.get(i)])][(tempCurrentColumn + horizontal[moveList.get(i)])] <= smallest)
  230.             {
  231.                 returnMoveList.add(moveList.get(i));
  232.                 returnMoveEfficiency.add(accessibility[(tempCurrentRow + vertical[moveList.get(0)])][(tempCurrentColumn + horizontal[moveList.get(0)])]);
  233.             }
  234.         }  
  235.        
  236.         if(returnMoveList.size() == 1)
  237.         {
  238.             return returnMoveList.get(0);
  239.         }
  240.        
  241.         if(returnMoveEfficiency.get(0) < returnMoveEfficiency.get(1))
  242.         {
  243.             return returnMoveList.get(0);
  244.         }
  245.        
  246.         if(returnMoveEfficiency.get(1) < returnMoveEfficiency.get(0))
  247.         {
  248.             return returnMoveList.get(1);
  249.         }
  250.        
  251.         else
  252.         {
  253.             int smallestMove = 100;
  254.             int bestMove = 0;
  255.            
  256.             for(int i = 0; i < returnMoveList.size(); i++)
  257.             {
  258.                 int tempRow = tempCurrentRow + vertical[returnMoveList.get(i)];
  259.                 int tempColumn = tempCurrentColumn + horizontal[returnMoveList.get(i)];
  260.                 ArrayList<Integer> possibleMoves = findPossibleMoves(tempRow, tempColumn);
  261.                 int tempSmallestMove = checkMoveEfficiencyDeeper(possibleMoves, tempRow, tempColumn);
  262.                
  263.                 if(tempSmallestMove < smallestMove)
  264.                 {
  265.                     smallestMove = tempSmallestMove;
  266.                     bestMove = i;
  267.                 }  
  268.             }
  269.             return returnMoveList.get(bestMove);
  270.         }
  271.     }
  272.    
  273.     public static Integer checkMoveEfficiencyDeeper(ArrayList<Integer> moveList, int row, int column)
  274.     {
  275.         int tempCurrentRow = row;
  276.         int tempCurrentColumn = column;
  277.         int smallestMove = 10;
  278.        
  279.         for(int i = 0; i < moveList.size(); i++)
  280.         {
  281.             int move = accessibility[(tempCurrentRow + vertical[moveList.get(i)])][(tempCurrentColumn + horizontal[moveList.get(i)])];
  282.             if(move < smallestMove)
  283.             {
  284.                 smallestMove = move;
  285.             }
  286.         }
  287.         return smallestMove;
  288.     }
  289.    
  290.     // primary loop that moves the knight around the board, choosing moves based on their efficiency.
  291.     // (efficiency is determined by the checkMoveEfficiency method).
  292.     public static void performTour(int startingRow, int startingColumn)
  293.     {
  294.         // initialize these two variables to determine the knight's starting position.
  295.         currentRow = startingRow;
  296.         currentColumn = startingColumn;
  297.        
  298.         // knight starts on this tile, so it should not be visited again.
  299.         chessBoard[startingRow][startingColumn] = 1;
  300.        
  301.         // initialized moveCounter to 1.
  302.         moveCounter = 1;
  303.        
  304.         while(moveCounter < 64)
  305.         {  
  306.             if(findPossibleMoves(currentRow, currentColumn).size() == 0)
  307.             {
  308.                 System.out.println("No possible moves remaining.  Printing a summary of the tour:\n");
  309.                 for(int i = 0; i < 8; i++)
  310.                 {
  311.                     for(int j = 0; j < 8; j++)
  312.                     {
  313.                         System.out.print(chessBoard[i][j] + " ");
  314.                     }
  315.                     System.out.println();
  316.                 }
  317.                 System.out.println("\nExiting simulation.\n");
  318.                 break;
  319.             }
  320.            
  321.             else
  322.             {
  323.                 System.out.printf("Performing move number %d\n",moveCounter);
  324.                 Integer bestMove = checkMoveEfficiency(findPossibleMoves(currentRow, currentColumn), currentRow, currentColumn);
  325.                 moveKnight(bestMove);
  326.                 moveCounter++;
  327.             }
  328.         }
  329.     }
  330.    
  331.     // main method.
  332.     public static void main(String[] args)
  333.     {  
  334.         // the move types don't change so they can be initialized once.
  335.         initializeMoves();
  336.  
  337.         for(int i = 0; i < 8; i++)
  338.         {
  339.             for(int j = 0; j < 8; j++)
  340.             {
  341.             // the chessBoard array changes during each to keep track of which tiles have
  342.             // been visited by the knight, so this needs to be reinitialized each simulation.
  343.             initializeChessBoard();
  344.            
  345.             // the accessibility array is adjusted during the simulation after each move in order
  346.             // to help prioritize which subsequent move to make, so this also needs to be
  347.             // reinitialized each simulation.
  348.             initializeAccessibility();
  349.            
  350.             // increment the tourTracker and print an update to the console.
  351.             tourTracker++;
  352.             System.out.printf("Attempting tour number %d.\n\n", tourTracker);
  353.            
  354.             // this method starts the knight's tour simulation.
  355.             performTour(i, j);
  356.             }
  357.         }      
  358.         System.out.printf("Simulation complete after %d tours.", tourTracker);
  359.         System.out.printf("\nThere were %d successful tours.", successfulTours);
  360.     }
  361. }
Advertisement
Add Comment
Please, Sign In to add comment