1. 程式人生 > >學以致用——Java原始碼——騎士之旅(跳馬)小遊戲_優化演算法版(Knight’s Tour - Heuristic version)

學以致用——Java原始碼——騎士之旅(跳馬)小遊戲_優化演算法版(Knight’s Tour - Heuristic version)

程式功能:

輸入騎士的起始位置,程式模擬輸出其移動軌跡(本程式採用優化演算法(Heuristic)但未使用蠻力解決法)。相比上一版本騎士“隨意”的選擇路勁,這次,武士受到了啟發,優先選擇靠邊的位置行走,所以可以走的更遠。但有時依然改變不了自己尚未完成使命(走完全部方格),卻被困在棋盤上某個位置動彈不得的命運。

參考文章:

學以致用——Java原始碼——騎士之旅(跳馬)小遊戲_宿命版(Knight’s Tour - Non-heuristic version),

https://blog.csdn.net/hpdlzu80100/article/details/85320824

執行示例:

********

武士之旅小遊戲********

Accessibility Table

2         3         4         4         4         4         3         2        

3         4         6         6         6         6         4         3        

4         6         8         8         8         8         6         4        

4         6         8         8         8         8         6         4        

4         6         8         8         8         8         6         4        

4         6         8         8         8         8         6         4        

3         4         6         6         6         6         4         3        

2         3         4         4         4         4         3         2        

 

請輸入武士的起始行號(18,輸入-1退出):3

請輸入武士的起始列號(18):2

起始位置:【32

 

終點位置:【66】,移動步數:63

2         17        36        33        4         19        38        23       

35        32        3         18        37        22        5         20       

16        1         34        47        52        59        24        39       

31        46        51        60        63        54        21        6        

50        15        48        53        58        61        40        25       

45        30        57        62        55        64        7         10       

14        49        28        43        12        9         26        41       

29        44        13        56        27        42        11        8        

 

請輸入武士的起始行號(18,輸入-1退出):1

請輸入武士的起始列號(18):1

起始位置:【11

 

終點位置:【63】,移動步數:59

1         32        3         18        29        34        13        16       

4         19        30        33        14        17        28        35       

31        2         55        58        53        44        15        12       

20        5         52        43        56        27        36        45       

51        42        57        54        59        0         11        26       

6         21        60        0         0         0         46        37       

41        50        23        8         39        48        25        10       

22        7         40        49        24        9         38        47       

 

請輸入武士的起始行號(18,輸入-1退出):3

請輸入武士的起始列號(18):4

起始位置:【34

 

終點位置:【53】,移動步數:61

36        15        34        31        2         17        46        21       

33        30        37        16        47        20        3         18       

14        35        32        1         58        49        22        45       

29        38        51        48        61        56        19        4        

52        13        62        57        50        59        44        23       

39        28        0         60        55        0         5         8        

12        53        26        41        10        7         24        43       

27        40        11        54        25        42        9         6        

 

請輸入武士的起始行號(18,輸入-1退出):5

請輸入武士的起始列號(18):6

起始位置:【56

 

終點位置:【66】,移動步數:61

6         43        8         23        4         45        18        21       

9         24        5         44        19        22        3         46       

42        7         60        57        48        55        20        17       

25        10        49        54        59        32        47        2        

50        41        58        61        56        1         16        31       

11        26        0         0         53        62        33        36       

40        51        28        13        38        35        30        15       

27        12        39        52        29        14        37        34       

 

請輸入武士的起始行號(18,輸入-1退出):8

請輸入武士的起始列號(18):8

起始位置:【88

 

終點位置:【65】,移動步數:55

7         40        9         24        5         42        19        22       

10        25        6         41        20        23        4         43       

39        8         0         34        0         52        21        18       

26        11        38        51        0         33        44        3        

37        50        35        0         53        0         17        32       

12        27        0         0         56        0         2         45       

49        36        29        14        47        54        31        16       

28        13        48        55        30        15        46        1

程式碼如下:

import java.util.Scanner;

//JHTP Exercise 7.22: Knight’s Tour
//by [email protected]

/*7.22 (Knight’s Tour) An interesting puzzler for chess buffs is the Knight’s Tour problem, 
 * originally proposed by the mathematician Euler. Can the knight piece move around an empty 
 * chessboard and touch each of the 64 squares once and only once? We study this intriguing 
 * problem in depth here.
The knight makes only L-shaped moves (two spaces in one direction and one space in a perpendicular
 direction). Thus, as shown in Fig. 7.30, from a square near the middle of an empty chessboard, 
 the knight (labeled K) can make eight different moves (numbered 0 through 7).
 ) Draw an eight-by-eight chessboard on a sheet of paper, and attempt a Knight’s Tour by hand. 
 Put a 1 in the starting square, a 2 in the second square, a 3 in the third, and so on.
Before starting the tour, estimate how far you think you’ll get, remembering that a full tour 
consists of 64 moves. How far did you get? Was this close to your estimate?
b) Now let’s develop an application that will move the knight around a chessboard. The board is 
represented by an eight-by-eight two-dimensional array board. Each square is initialized to zero. 
We describe each of the eight possible moves in terms of its horizontal and vertical components. 
For example, a move of type 0, as shown in Fig. 7.30, consists of moving two squares horizontally 
to the right and one square vertically upward.
A move of type 2 consists of moving one square horizontally to the left and two squares vertically 
upward. Horizontal moves to the left and vertical moves upward are indicated with negative numbers. 
The eight moves may be described by two one-dimensional arrays,
horizontal and vertical, as follows:
horizontal[0] = 2 vertical[0] = -1
horizontal[1] = 1 vertical[1] = -2
horizontal[2] = -1 vertical[2] = -2
horizontal[3] = -2 vertical[3] = -1
horizontal[4] = -2 vertical[4] = 1
horizontal[5] = -1 vertical[5] = 2
horizontal[6] = 1 vertical[6] = 2
horizontal[7] = 2 vertical[7] = 1
Let the variables currentRow and currentColumn indicate the row and column, respectively, 
of the knight’s current position. To make a move of type moveNumber, where moveNumber is between
 0 and 7, your application should use the statements
currentRow += vertical[moveNumber];
currentColumn += horizontal[moveNumber];
Write an application to move the knight around the chessboard. Keep a counter that varies from
 1 to 64. Record the latest count in each square the knight moves to.
Test each potential move to see if the knight has already visited that square. Test every potential
 move to ensure that the knight does not land off the chessboard. Run the application. How many moves
  did the knight make?
c) After attempting to write and run a Knight’s Tour application, you’ve probably developed some valuable
 insights. We’ll use these insights to develop a heuristic (i.e., a common-sense rule) for moving the
  knight. Heuristics do not guarantee success, but a carefully developed heuristic greatly improves the
   chance of success. You may have observed that the outer squares are more troublesome than the squares
    nearer the center of the board. In fact, the most troublesome or inaccessible squares are the four corners.
Intuition may suggest that you should attempt to move the knight to the most troublesome squares first 
and leave open those that are easiest to get to, so that when the board gets congested near the end of 
the tour, there will be a greater chance of success.
We could develop an “accessibility heuristic” by classifying each of the squares according to how accessible
 it is and always moving the knight (using the knight’s Lshaped moves) to the most inaccessible square. 
 We label a two-dimensional array accessibility with numbers indicating from how many squares each particular
  square is accessible. On a blank chessboard, each of the 16 squares nearest the center is
rated as 8, each corner square is rated as 2, and the other squares have accessibility numbers of 3, 4 or 6 
as follows:
2 3 4 4 4 4 3 2
3 4 6 6 6 6 4 3
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
3 4 6 6 6 6 4 3
2 3 4 4 4 4 3 2
Write a new version of the Knight’s Tour, using the accessibility heuristic. The knight should always move
 to the square with the lowest accessibility number. In case of a tie, the knight may move to any of the 
 tied squares. Therefore, the tour may begin in any of the four corners. [Note: As the knight moves around
  the chessboard, your application should reduce the accessibility numbers as more squares become occupied.
In this way, at any given time during the tour, each available square’s accessibility number will remain 
equal to precisely the number of squares from which that square may be reached.] Run this version of your 
application. Did you get a full tour? Modify the application to run 64 tours, one starting from each square
 of the chessboard. How many full tours did you get?
d) Write a version of the Knight’s Tour application that, when encountering a tie between two or more squares, 
decides what square to choose by looking ahead to those squares reachable from the “tied” squares. Your 
application should move to the tied square for which the next move would arrive at a square with the lowest
 accessibility number.


 */

public class TourOfKnightHeuristic  {
 private static int currentRow;		//武士當前所在行
 private static int currentColumn;	//武士當前所在列
 private static int count;
 private static int[] vertical={-1,-2,-2,-1,1,2,2,1};	//對應武士的8種L形移動中的縱向移動步數(正數表示向下移動)
 private static int[] horizontal={2,1,-1,-2,-2,-1,1,2}; //移動步數(正數表示向右移動)
 private static int[][] chessBoard;		//棋盤,初始狀態所有位置全為0(空白,武士可到達)
 private static int moveNumber;	//移動型別,如果武士無路可走,移動型別為-1,遊戲結束
 private static Scanner input = new Scanner(System.in);
 private static int[][] accessibility = {{2,3,4,4,4,4,3,2},{3,4,6,6,6,6,4,3},{4,6,8,8,8,8,6,4},
		 {4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{3,4,6,6,6,6,4,3},{2,3,4,4,4,4,3,2}};
 private static int[] possibleMoveNumber = {-1,-1,-1,-1,-1,-1,-1,-1}; //可能的移動型別陣列(最多為8種)
 private static int possibleMoves; //可能的移動型別(最多為8種)

 
 public static void main(String[] args)
{
     
     System.out.printf("********武士之旅小遊戲********%n");
     
     System.out.println("Accessibility Table");
     for (int i=0;i<accessibility.length;i++) {
    	for (int j=0;j<accessibility[i].length;j++) 
    		System.out.printf("%d\t",accessibility[i][j]);
    	
       	System.out.println();
    	}
 

     do {
     chessBoard=new int[8][8];	//新旅行開始,重置棋盤
     count =0;	//新旅行開始,重置移動步數
     
     //設定起始位置
     System.out.printf("%n請輸入武士的起始行號(1至8,輸入-1退出):");
     currentRow = input.nextInt()-1;
     if(currentRow==-1) {
			System.out.print("錄入已結束。\n");
			break;
     }
		else {
	     System.out.print("請輸入武士的起始列號(1至8):");
	     currentColumn = input.nextInt()-1;
	     chessBoard[currentRow][currentColumn]=1;  //起點標記為1
	     System.out.printf("起始位置:【%d,%d】%n%n", currentRow+1, currentColumn+1);
	     
	    do
	     {
	    	moveNumber = knightMoveCheckHeuristic();	//起始位置
	        if (moveNumber >=0)
	        knightMove(moveNumber);
	     } while (moveNumber>=0);
	    
	    System.out.printf("終點位置:【%d,%d】,移動步數:%d%n", currentRow+1, currentColumn+1,count);
	   
	   
	    //旅途結束,列印棋盤
	    for (int i=0;i<=7;i++){
	        for (int j=0;j<=7;j++)
	           System.out.printf("%d\t",chessBoard[i][j]);
	        System.out.println();
	        }
		}
    
     }while (currentRow != -1);
            
    input.close(); 

 }
 
 
 public static int knightMoveCheckHeuristic() //檢查武士是否可以移動,如果可以,返回最佳移動型別,如果不可以,返回-1
{
	 possibleMoves = 0; //每次檢查前,重置計數器
	 for (int i=0;i<=7;i++) 
		 possibleMoveNumber[i]=-1; //每次檢查前,重置此陣列
	 
	 for (int i=0;i<=7;i++) {
		 if ((currentRow + vertical[i])>=0 
				 && (currentRow + vertical[i])<=7
				 && (currentColumn + horizontal[i])>=0
				 && (currentColumn + horizontal[i])<=7
				 && chessBoard[currentRow + vertical[i]][currentColumn + horizontal[i]]==0) {
			 possibleMoveNumber[possibleMoves]= i;  //獲取所有可能的移動型別
			 possibleMoves++;
		 }
	 }
	 if (possibleMoveNumber[0] == -1)
		 return -1;  //如果不能移動,返回-1
   
	 else return getMinimumAccessibility(possibleMoveNumber); //獲取最佳移動型別
 }
 
 public static int getMinimumAccessibility(int[] possibleMoveNumber) //返回移動後accessibility最低的移動型別
 {
 	 int possibleRowNum = currentRow + vertical[possibleMoveNumber[0]];  	   //第一個可以移動到的位置對應的行號
 	 int possibleColumnNum = currentColumn + horizontal[possibleMoveNumber[0]];   //第一個可以移動到的位置對應的列號
 	 int possibleAccessibility = accessibility[possibleRowNum][possibleColumnNum]; //第一個可以移動到的位置對應的accessibility
 	 int minAccessiblity = possibleAccessibility;
 	 int minMoveNumber = possibleMoveNumber[0]; //accessibility最低的移動型別,預設為第一個可能移動型別

 	 for (int i=1;i<=7;i++) {
 		 if (possibleMoveNumber[i] != -1) {
	  		possibleRowNum =currentRow + vertical[possibleMoveNumber[i]];
	  		possibleColumnNum =currentColumn + horizontal[possibleMoveNumber[i]];
	  		possibleAccessibility =accessibility[possibleRowNum][possibleColumnNum];
 		 }
		 if( possibleAccessibility < minAccessiblity) {
			minAccessiblity = possibleAccessibility;  //獲取最小accessibility值
			minMoveNumber  = possibleMoveNumber[i];   //獲取最小accessibility值對應的移動型別
		}
 	 }

 	 return minMoveNumber; //返回移動型別
  }
 

 
 public static void knightMove(int moveNumber) //武士移動(位置更新)
 {
	 
	 currentRow += vertical[moveNumber];
	 currentColumn += horizontal[moveNumber];
     chessBoard[currentRow][currentColumn]= ++count+1; //第一次移動後的位置標記為2
     }
 

 }