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

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

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

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

本程式加入了統計分析功能。

統計結果:

不同出發位置對應的移動步數彙總如下(*號表示完成使命):

59        54        62        55        59        62        60        55       

*63       58        54        55        47        59        59        55       

61        *63       55        61        60        61        56        59       

55        59        59        47        55        57        56        60       

59        60        51        60        57        61        58        53       

56        61        58        *63       60        57        58        59       

57        *63       50        62        59        56        61        60       

60        61        59        59        58        59        56        55       

 

執行結果:

(為避免冗長,部分輸出結果已省略。完整執行結果,請執行程式碼獲取)

由統計分析可看出,騎士完成使命的出發位置共有四次(在彙總表種用*號表示)。

這就是程式的魅力,你可以動動腦筋寫個程式,然後讓程式幫你找到解決方案。

********騎士之旅小遊戲********

 

No.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       

 

No.2

起始位置:【12】,終點位置:【65】,移動步數:54

4         1         6         21        32        39        16        19       

7         22        3         38        17        20        31        40       

2         5         0         33        0         49        18        15       

23        8         37        48        0         30        41        50       

36        47        34        0         0         0         14        29       

9         24        0         0         55        0         51        42       

46        35        26        11        44        53        28        13       

25        10        45        54        27        12        43        52       

 

 

……     

 

No.9

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

4         19        2         35        6         21        38        25       

1         34        5         20        37        24        7         22       

18        3         36        47        52        59        26        39       

33        46        51        60        63        54        23        8        

50        17        48        53        58        61        40        27       

45        32        57        62        55        64        9         12       

16        49        30        43        14        11        28        41       

31        44        15        56        29        42        13        10       

 

……     

 

No.64

起始位置:【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        

 

 

不同出發位置對應的移動步數彙總如下(*號表示完成使命):

59        54        62        55        59        62        60        55       

*63       58        54        55        47        59        59        55       

61        *63       55        61        60        61        56        59       

55        59        59        47        55        57        56        60       

59        60        51        60        57        61        58        53       

56        61        58        *63       60        57        58        59       

57        *63       50        62        59        56        61        60       

60        61        59        59        58        59        56        55       

 

 

程式碼如下:

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 TourOfKnightHeuristicStatistics  {
	 private static int currentRow;		//騎士當前所在行
	 private static int currentColumn;	//騎士當前所在列
	 private static int count;	//單次旅行移動步數
	 private static int[][] counts = new int[8][8]; //分別從棋盤各個位置出發,對應的單次旅行移動步數
	 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種),-1表示無法移動
 
	 
	 public static void main(String[] args)
	{
	     
	     System.out.printf("********騎士之旅小遊戲********%n");
	     
	     //列印accessibility table
			/*
			 * 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(); }
			 */
	 

	     for (int r=0;r<=7;r++)  //r表示row
	     	for (int c=0; c<=7;c++){  //c表示column
	     chessBoard=new int[8][8];	//新旅行開始,重置棋盤
	     count =0;	//新旅行開始,重置移動步數
	     currentRow = r;   //設定起始行
	     currentColumn =c; //設定起始列
	     
		     chessBoard[r][c]=1;  //起點標記為1
		     System.out.printf("%nNo.%d%n起始位置:【%d,%d】,", r*8+c+1,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);
		    counts[r][c]=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();
		        }
			}
	   //列印彙總資訊
	     System.out.printf("%n%n不同出發位置對應的移動步數彙總如下(*號表示完成使命):%n");
	     for (int i=0;i<=7;i++){
		        for (int j=0;j<=7;j++) {
		        	if(counts[i][j]==63)
			        	   System.out.print("*");
		           System.out.printf("%d\t",counts[i][j]);
		           
		        }
		        System.out.println();
		        }
		
	   input.close();  

	 }
	 
	 public static int knightMoveCheck() //檢查騎士是否可以移動,如果可以,返回移動型別,如果不可以,返回-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)
				 return i;
		 }
	   return -1;
	    
	 }
	 
	 public static int knightMoveCheckHeuristic() //檢查騎士是否可以移動,如果可以,返回最佳移動型別,如果不可以,返回-1
	{
		 int 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
	     }
	 

	 }