目录
***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();
}
}
}
-
动画演示