目录
***18.33 (游戏:骑士旅途的动画)
习题思路
代码示例
动画演示
***18.33 (游戏:骑士旅途的动画)
为骑士旅途的问题编写一个程序,该程序应该允许用户将骑士放到任何一个起始正方形,并单击Solve按钮,用动画展示骑士沿着路径的移动,如图18-16所示
-
习题思路
-
初始化棋盘和骑士位置
- Board类:这个内部类继承自
Pane
,用于绘制棋盘和骑士。棋盘通过绘制水平和垂直线来创建,而骑士则通过红色圆圈表示。 - draw方法:此方法用于绘制棋盘和骑士。首先清除所有子节点,然后绘制棋盘网格和初始骑士位置(红色圆圈)。骑士有两个圆圈表示:
theKnight
(静态位置)和movingKnight
(用于动画的移动位置)。 - startX和startY:这两个变量用于记录骑士的起始位置。初始时,它们被设置为(0, 0),即棋盘的左上角。但用户可以通过点击棋盘上的任意格子来改变这些值。
- Board类:这个内部类继承自
-
点击事件处理
- 当用户在棋盘上点击时,
Board
类中的setOnMouseClicked
事件处理器会被触发。 - 事件处理器计算点击位置对应的格子索引(
startX
和startY
),这是通过将鼠标点击的坐标除以每个格子的大小(棋盘宽度/SIZE或棋盘高度/SIZE)并取整来实现的。 - 随后,重置
moves
列表,即骑士的移动历史,为新的求解过程做准备。
- 当用户在棋盘上点击时,
-
求解按钮点击事件
- 当用户点击“Solve”按钮时,会触发一个事件处理器,该处理器执行以下步骤:
- 初始化棋盘状态:创建一个
boolean[][] moves
数组来跟踪哪些格子已被访问。将起始位置标记为已访问。 - 重置和添加起始移动:调用
resetMoves()
和addMove(startX, startY)
来重置移动历史并添加起始位置到历史中。 - 递归求解:调用
solvePuzzle(moves, 1, startX, startY)
开始递归求解过程。该方法尝试从当前位置出发,找到所有可能的下一步,并评估哪一步能导致最少的未访问格子数(通过lookAheadCount
方法)。然后,它选择最优的下一步,并递归地尝试解决剩余的问题。 - 动画展示:如果找到解决方案,
draw
方法会被再次调用以更新棋盘上的骑士位置(尽管在这个实现中,movingKnight
并未在动画中实际移动),然后调用playAnimation
方法来展示骑士的移动路径。
-
动画展示
- playAnimation方法:此方法使用
SequentialTransition
和PathTransition
来创建动画,展示骑士沿着求解路径移动的过程。对于moves
列表中的每一对相邻点,它创建一个PathTransition
,该过渡使一个红色圆圈沿着两点之间的直线移动。所有这些过渡被添加到一个SequentialTransition
中,以便按顺序播放。
- playAnimation方法:此方法使用
-
代码示例
编程练习题18_33TheKnightJourney.java
package chapter_18; import java.util.ArrayList;
import javafx.animation.PathTransition;
import javafx.animation.SequentialTransition;
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.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration; public class 编程练习题18_33TheKnightJourney extends Application {private static final int SIZE = 8;private int startX = 0;private int startY = 0;private ArrayList<Point2D> moves = null;private ArrayList<Line> pathList;public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) throws Exception {pathList = new ArrayList<Line>();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("编程练习题18_33TheKnightJourney");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();board.playAnimation(pathList);});}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();Circle movingKnight = 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);movingKnight.setCenterX(startX * getWidth()/SIZE +15);movingKnight.setCenterY(startY * getHeight()/SIZE + 15);movingKnight.setRadius(5);movingKnight.setFill(Color.RED);this.getChildren().add(movingKnight);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);Line line = 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));pathList.add(line);}}}private void playAnimation(ArrayList<Line> lines) {int i = 0;SequentialTransition sequentialTransition = new SequentialTransition();Circle c;c = new Circle(5);c.setFill(Color.RED);getChildren().add(c);for(Line l: lines) {PathTransition transition = new PathTransition(Duration.seconds(1), l, c);transition.setOnFinished(e->{if(i<lines.size()-2) {getChildren().add(l);}});sequentialTransition.getChildren().add(transition);}sequentialTransition.play();}}
}
-
动画演示