目录
题目:***18.32 (游戏:骑士的旅途)
习题思路
代码示例
输出结果
题目:***18.32 (游戏:骑士的旅途)
骑士的旅途是一个古老的谜题,它的目的是使骑从棋盘上的任意一个正方 形开始移动,经过其他的每个正方形一次,如图18-15a 所示。注意,骑士只能做L形的移动(两个空格在一个方向上而一个空格在垂直的方向上)。如图18-15b 所示,骑士可以移动到八个正方形的位置。编写一个程序,显示骑士的移动,如图 18-15C 所示。当单击一个单元格的时候,骑士被放置在该单元格中。该单元格作为骑士的起始点。单击Solve按钮显示作为解答的路径。
-
习题思路
- 界面设置:
- 使用
BorderPane
作为主布局,中心放置一个Board
(自定义的Pane
),用于绘制棋盘和骑士的移动路径。 - 在底部添加一个按钮
btSolve
,用于触发求解过程。 - 舞台(
Stage
)和场景(Scene
)的设置,包括尺寸和标题。
- 使用
- 棋盘和骑士的初始化:
- 在
Board
类中,通过draw
方法绘制棋盘(网格线)和初始位置的骑士(红色圆圈)。 - 骑士的初始位置(
startX
和startY
)默认为(0, 0),但可通过鼠标点击棋盘上的任意格子来设置新的起始位置。
- 在
- 求解逻辑:
- 使用一个二维布尔数组
moves
来记录棋盘上的格子是否已被访问过。 solvePuzzle
方法是一个递归函数,用于尝试不同的移动路径,直到找到一种能够遍历所有格子并返回起点的解决方案。- 在每一步尝试中,通过
lookAheadCount
方法评估每个可能的下一步移动,并选择看起来最有前景的(即下一步后能够访问最多新格子的)移动。 - 使用回溯法,如果当前路径无法找到解决方案,则撤销上一步的选择,尝试其他可能的移动。
- 使用一个二维布尔数组
- 用户交互:
- 点击棋盘上的格子设置新的起始位置,并清空之前的移动历史。
- 点击“Solve”按钮触发求解过程,并在找到解决方案后重新绘制棋盘,显示骑士的移动路径。
- 移动历史记录:
- 使用
ArrayList<Point2D>
来记录骑士的移动历史,以便在求解过程中回溯和重新绘制路径。 - 提供
resetMoves
、addMove
和removeLastMoveHistory
方法来管理移动历史记录。
- 使用
- 棋盘绘制:
- 在
Board
类的draw
方法中,除了绘制棋盘网格和骑士外,还根据移动历史记录绘制骑士的移动路径。
- 在
-
代码示例
编程练习题18_32TheKnightJourney.java
package chapter_18; import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage; public class 编程练习题18_32TheKnightJourney extends Application { private static final int SIZE = 8;private int startX = 0;private int startY = 0;private ArrayList<Point2D> moves = null;public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) throws Exception {BorderPane pane = new BorderPane();Board board = new Board();pane.setCenter(board);Button btSolve = new Button("Solve");pane.setBottom(btSolve);BorderPane.setAlignment(btSolve, Pos.CENTER);Scene scene = new Scene(pane,400,400);primaryStage.setTitle("");primaryStage.setScene(scene);primaryStage.show();board.draw();btSolve.setOnAction(e -> {boolean[][] moves = new boolean[SIZE][SIZE];moves[startX][startY] = true;resetMoves();addMove(startX,startY);solvePuzzle(moves,1,startX,startY);board.draw();});}private boolean solvePuzzle(boolean[][] moves,int numberMoves,int x,int y) {int nextX = 0;int nextY = 0;int bestMoveX = 0;int bestMoveY = 0;int bestMoveX2 = 0;int bestMoveY2 = 0;int minMoveCount = SIZE;int moveCount = 0;for(int i = 2;i >= -2;i += -4) {for(int j = 1;j >= -1;j += -2) {nextX = x + i;nextY = y + j;if(nextX >= 0&&nextX <= SIZE-1&&nextY >= 0&&nextY <= SIZE-1&&!moves[nextX][nextY]) {moveCount = lookAheadCount(moves,nextX,nextY);if(moveCount <= minMoveCount) {minMoveCount = moveCount;bestMoveX2 = bestMoveX;bestMoveY2 = bestMoveY;bestMoveX = nextX;bestMoveY = nextY;}}nextX = x + j;nextY = y + i;if(nextX >= 0&&nextX <= SIZE-1&&nextY >= 0&&nextY <= SIZE-1&&!moves[nextX][nextY]) {moveCount = lookAheadCount(moves,nextX,nextY);if(moveCount <= minMoveCount) {minMoveCount = moveCount;bestMoveX2 = bestMoveX;bestMoveY2 = bestMoveY;bestMoveX = nextX;bestMoveY = nextY;}}}}moves[bestMoveX][bestMoveY] = true;addMove(bestMoveX,bestMoveY);numberMoves++;if(numberMoves == (SIZE * SIZE))return true;if(moveCount > 0&&solvePuzzle(moves, numberMoves, bestMoveX, bestMoveY))return true;moves[bestMoveX][bestMoveY] = false;moves[bestMoveX2][bestMoveY2] = true;removeLastMoveHistory();addMove(bestMoveX2,bestMoveY2);if(moveCount > 1&&solvePuzzle(moves, numberMoves, bestMoveX2, bestMoveY2)) {return true;}moves[bestMoveX2][bestMoveY2] = false;removeLastMoveHistory();numberMoves--;return false;}private int lookAheadCount(boolean[][] moves,int x,int y) {int maxCount = 0;for(int i = -2;i<=2;i+=4) {for(int j = -1;j <= 1;j += 2) {int nextX = x + i;int nextY = y + j;if(nextX >= 0&&nextX <= SIZE-1&&nextY >=0 && nextY <= SIZE-1&&!moves[nextX][nextY]) {maxCount++;}nextX = x + j;nextY = y + i;if(nextX >= 0&&nextX <= SIZE-1&&nextY >= 0&&nextY <= SIZE-1&&!moves[nextX][nextY])maxCount++;}}return maxCount;}public void resetMoves() {moves = new ArrayList(63);}public void addMove(int x,int y) {moves.add(new Point2D(x, y));}public void removeLastMoveHistory() {moves.remove(moves.size()-1);}private class Board extends Pane{Circle theKnight = new Circle();Board(){this.setOnMouseClicked(e ->{startX = (int)(e.getX()/(getWidth()/SIZE));startY = (int)(e.getY()/(getHeight()/SIZE));resetMoves();draw();});}protected void draw() {this.getChildren().clear();this.getChildren().add(theKnight);theKnight.setCenterX(startX * getWidth()/SIZE +15);theKnight.setCenterY(startY * getHeight()/SIZE + 15);theKnight.setRadius(5);theKnight.setFill(Color.RED);for(int i = 1;i <= SIZE;i++) {this.getChildren().add(new Line(0,i*getHeight()/SIZE,getWidth(),i*getHeight()/SIZE)); this.getChildren().add(new Line(i*getWidth()/SIZE,0,i*getWidth()/SIZE,getHeight()));}if(moves != null) {for(int i = 1;i < moves.size();i++) {Point2D p1 = moves.get(i - 1);Point2D p2 = moves.get(i);this.getChildren().add(new Line(p1.getX()*(getWidth()/SIZE)+(getWidth()/SIZE/2),p1.getY()*(getHeight()/SIZE)+(getHeight()/SIZE/2),p2.getX()*(getWidth()/SIZE)+(getWidth()/SIZE/2),p2.getY()*(getHeight()/SIZE)+(getHeight()/SIZE/2)));}}}}
}
-
输出结果