学以致用——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 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 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
     }
 

 }

 

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