学以致用——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 pandenghuang@163.com

	/*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
	     }
	 

	 }

 

    原文作者:骑士周游问题
    原文地址: https://blog.csdn.net/hpdlzu80100/article/details/85334413
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞