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

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

遊戲規則:

一個8*8的棋盤上站著一個騎士(Knight),騎士在棋盤上只能走L形(橫走1步後豎走2步,或橫走2步後豎走1步),要求棋盤上每個方格只能停留一次,請問騎士應該怎麼走才能走完64個方格?

如,當騎士位於下圖中K位置時,其移動方式共有以下0-7共8種。

程式功能:

輸入武士的起始位置,程式模擬輸出其移動軌跡(本程式未採用優化演算法以及蠻力解決法)。武士只是“隨意”的選擇路勁,直到被困在棋盤上動彈不得。

執行示例:

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

 

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

請輸入騎士的起始列號(1

8):6

起始位置:【56

 

終點位置:【12】,移動步數:40

6         41        26        11        4         9         16        21       

27        12        5         8         15        20        3         18       

40        7         14        25        10        17        22        35       

13        28        0         38        23        34        19        2        

0         39        24        0         0         1         36        33       

29        0         0         0         37        32        0         0        

0         0         0         31        0         0         0         0        

0         30        0         0         0         0         0         0        

 

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

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

起始位置:【18

 

終點位置:【11】,移動步數:31

32        13        18        3         16        11        8         1        

19        4         15        12        7         2         27        10       

14        31        6         17        28        9         24        0        

5         20        29        0         23        0         0         26       

30        0         22        0         0         25        0         0        

21        0         0         0         0         0         0         0        

0         0         0         0         0         0         0         0        

0         0         0         0         0         0         0         0        

 

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

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

起始位置:【16

 

終點位置:【11】,移動步數:31

32        3         30        23        6         1         12        9        

29        24        5         2         11        8         17        14       

4         31        22        7         16        13        10        19       

25        28        0         0         21        18        15        0        

0         0         0         27        0         0         20        0        

0         26        0         0         0         0         0         0        

0         0         0         0         0         0         0         0        

0         0         0         0         0         0         0         0        

 

程式碼如下:

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 TourOfKnight  {
 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);
 
 public static void main(String[] args)
{
     
     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 = knightMoveCheck();	//起始位置
	        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 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 void knightMove(int moveNumber) //騎士移動(位置更新)
 {
	 
	 currentRow += vertical[moveNumber];
	 currentColumn += horizontal[moveNumber];
     chessBoard[currentRow][currentColumn]= ++count+1; //第一次移動後的位置標記為2
     }
 

 }