介绍:
程序由两个文件组成,app和CreateMap.
CreateMap利用Prim算法自动生成迷宫
app内包含利用JavaFX生成的图形界面和BFS自动走迷宫算法
用法:
程序需要先安装Java FX,具体安装步骤请移步Java FX安装教程.
程序默认左上角为起点,右下角为终点
可以使用键盘上的上下左右按键操作目标方块进行移动.
当方块移动到右下角时游戏结束,弹出提示框"You WIN!!!",并重新开始(包含重新生成地图,目标块重置到起始位置)
当用户按下键盘上的空格键时,目标块以当前位置为起点自动检索到终点的最短路径并开始移动.因为目标块移动是用连续动画实现,所以移动过程不可暂停,移动到终点后无提示框,无自动重置,若需要继续进行游戏,需要重启.
CeateMap
import java.util.Random;
public class CreateMap {
// 初始化一个地图 默认所有路不通
// row行表示的是刚开始空白格子的行数,而格子之间还有墙壁,所以最终产生的二维数组大小实际为(2row+1) * (2colum+1)
private int row;
private int column;
public int[][] map;// 存放迷宫的数组
// private Vector[] Pos;
private int r;
private int c;
CreateMap(int r0, int c0) {
row = r0;
column = c0;
r = 2 * row + 1;
c = 2 * column + 1;
map = new int[r][c];
}
public void Init() {
for (int i = 0; i < r; i++) // 将所有格子都设为墙
for (int j = 0; j < c; j++)
map[i][j] = 0;// 0 为墙 1为路
// 中间格子放为1
for (int i = 0; i < row; i++)
for (int j = 0; j < column; j++)
map[2 * i + 1][2 * j + 1] = 1;// 0 为墙 1为路
// 普里姆算法
accLabPrime();
}
// 通过Prim算法处理数组 生成最后的迷宫
// 思路:
// 随机找最近的点访问(每个点只访问一次,直到访问完所有的路),
//会生成一条访问所有点的路(无序),在随机找下一个点的时,把之前相邻的两个格子之间的墙壁打通
public void accLabPrime() {
// acc存放已访问队列,noacc存放没有访问队列
int[] acc, noacc;
int count = row * column;
int accsize = 0;// 记录访问过点的数量
acc = new int[count];
noacc = new int[count];
// row上各方向的偏移 column各方向的偏移 0左 1右 3上 2下
int[] offR = { -1, 1, 0, 0 };
int[] offC = { 0, 0, 1, -1 };
// 四个方向的偏移 左右上下
int[] offS = { -1, 1, row, -row }; // 向上向下移动都是变化一行
// 初始化 acc中0代表未访问,noacc中0代表未访问
for (int i = 0; i < count; i++) {
acc[i] = 0;
noacc[i] = 0;
}
// 起点
Random rd = new Random();
acc[0] = rd.nextInt(count);// 起始点
int pos = acc[0];
// 第一个点存入
noacc[pos] = 1;
while (accsize < count) {
// 取出现在的点
int x = pos % row;
int y = pos / row;// 该点的坐标
int offpos = -1;// 用于记录偏移量
int w = 0;
// 四个方向都尝试一遍 直到挖通为止
while (++w < 5) {
// 随机访问最近的点
int point = rd.nextInt(4); // 0-3
int repos;
int move_x, move_y;
// 计算出移动方位
repos = pos + offS[point];// 移动后的下标
move_x = x + offR[point];// 移动后的方位
move_y = y + offC[point];
// 判断移动是否合法
if (move_y >= 0 && move_x >= 0 && move_x < row && move_y < column && repos >= 0 && repos < count
&& noacc[repos] != 1) {
noacc[repos] = 1;// 把该点标记为已访问
acc[++accsize] = repos;// ++accsize代表第几个已经访问的点,repos代表该点的下标
pos = repos;// 把该点作为起点
offpos = point;
// 相邻的格子中间的位置放1
map[2 * x + 1 + offR[point]][2 * y + 1 + offC[point]] = 1;
break;
} else {
if (accsize == count - 1)
return;
continue;
}
}
if (offpos < 0) {// 周边没有找到能走的路了 从走过的路里重新找个起点
pos = acc[rd.nextInt(accsize + 1)];}
}
}
}
app
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
class node {
public int x, y;
node() {
}
node(int a, int b) {
x = a;
y = b;
}
void set(int a, int b) {
x = a;
y = b;
}
}
public class app extends Application {
public int Size = 15;// 有效地图大小,用于Prim算法生成地图
public static final int Range = 30;// 单元格边长
public int VSize = (Size * 2 + 1) * Range;// 实际地图大小
public int maze[][] = new int[VSize][VSize];// 地图
public int vis[][] = new int[VSize][VSize];// 访问过的路径
public node f[][] = new node[VSize][VSize];
public int[][] dir = { { -Range, 0 }, { Range, 0 }, { 0, -Range }, { 0, Range } };// 移动方向
public CreateMap c = new CreateMap(Size, Size);
Rectangle rec = new Rectangle(Range, Range, Range, Range);
private int recX = 30, recY = 30;
private boolean autoPath = false;// 是否开启自动解图
public void start(Stage stage) throws Exception {
CreateMap();
Pane pane = Init();// 生成迷宫平台
Scene scene = new Scene(pane, VSize, VSize);
scene.setOnKeyPressed(k -> {
KeyCode code = k.getCode();
int tx = recX, ty = recY;
if (code.equals(KeyCode.LEFT) && autoPath == false) { // 按下了左键
tx -= Range;
} else if (code.equals(KeyCode.RIGHT) && autoPath == false) {// 按下了右键
tx += Range;
} else if (code.equals(KeyCode.UP) && autoPath == false) {// 按下了上方向键
ty -= Range;
} else if (code.equals(KeyCode.DOWN) && autoPath == false) {// 按下了下方向键
ty += Range;
} else if (code.equals(KeyCode.SPACE)) {
if (autoPath == false) {
autoPath = true;
node e = new node();
e.set(recX, recY);
autoMove(e);
}
}
if (inside(tx, ty) && maze[tx][ty] == 1 && autoPath == false) {
// System.out.println(recX+" "+recY+" "+tx + " " + ty);
move(tx, ty);
recX = tx;
recY = ty;
} else if (recX == VSize - Range * 2 && recY == VSize - Range * 2) {// 判断是否出界和撞墙
Alert alert = new Alert(AlertType.INFORMATION);
alert.titleProperty().set("信息");
alert.headerTextProperty().set("You WIN!!!!");
alert.showAndWait();
try {
start(stage);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
move(Range, Range);
recX = Range;
recY = Range;
}
});
stage.setScene(scene);
stage.centerOnScreen();
stage.setTitle("迷宫");
stage.show();
}
public void move(int tx, int ty) {
SequentialTransition link = new SequentialTransition();// 动画列表
link.setNode(rec);
TranslateTransition tt = new TranslateTransition();
tt.setFromX(recX - 30);
tt.setToX(tx - 30);
tt.setFromY(recY - 30);
tt.setToY(ty - 30);
// System.out.println(recX+" "+recY+" "+tx+" "+ty);
link.getChildren().add(tt);
link.play();
}
public void CreateMap() {
c.Init();// 生成迷宫
for (int i = 0; i < VSize; i += Range) {
for (int j = 0; j < VSize; j += Range) {
maze[i][j] = c.map[i / Range][j / Range];
}
} // 迷宫映射
}
public Pane Init() {
Pane pane = new Pane();
for (int i = 0; i < VSize; i += Range) {
for (int j = 0; j < VSize; j += Range) {
Rectangle r = new Rectangle(i, j, Range, Range);
if (maze[i][j] == 0) {
r.setFill(Color.PINK);
} else if (maze[i][j] == 1) {
r.setFill(Color.YELLOW);
}
if (i == VSize - Range && j == VSize - Range * 2) {
r.setFill(Color.RED);
}
pane.getChildren().add(r);
}
}
rec.setFill(Color.BLACK);
pane.getChildren().add(rec);// 显示目标块
return pane;
}
public void autoMove(node e) {
SequentialTransition link = new SequentialTransition();// 动画列表
link.setNode(rec);
Queue<node> queue = new ArrayBlockingQueue<node>(1000);
int flag = 0;
System.out.println(e.x + " " + e.y);
queue.add(e);
vis[e.x][e.y] = 1;// 已访问
while (flag == 0) {// 广度优先遍历,寻找最短路径
node now = queue.remove();
for (int i = 0; i < 4; i++) {
int fx = now.x + dir[i][0];
int fy = now.y + dir[i][1];
if ((inside(fx, fy) && (vis[fx][fy] == 0) && maze[fx][fy] == 1)) {
vis[fx][fy] = 1;
f[fx][fy] = new node(now.x, now.y);
queue.add(new node(fx, fy));
}
if (fx == VSize - Range * 2 && fy == VSize - Range * 2) {// 当有一路到达终点时开始回溯
node ans[] = new node[1000];
int cnt = 0;
int t1, t2;
ans[cnt] = new node(fx, fy);
while (f[fx][fy].x != e.x || f[fx][fy].y != e.y) {// 按照点记录的前一点坐标进行回溯即可得到到达点的最短路径。
t1 = fx;
t2 = fy;
cnt++;
ans[cnt] = new node(f[fx][fy].x, f[fx][fy].y);
fx = f[t1][t2].x;
fy = f[t1][t2].y;
}
ans[++cnt] = new node(0, 0);
for (int l = cnt - 1; l > 0; l--) {
// move(ans[l].x, ans[l].y);
// System.out.println(recX + " " + recY + " " + ans[l].x + " " + ans[l].y);
// recX = ans[l].x;
// recY = ans[l].y;
TranslateTransition tt = new TranslateTransition();
tt.setFromX(ans[l].x - 30);
tt.setToX(ans[l - 1].x - 30);
tt.setFromY(ans[l].y - 30);
tt.setToY(ans[l - 1].y - 30);
link.getChildren().add(tt);
}
flag = 1;
break;
}
}
}
link.play();
}
boolean inside(int fx, int fy) {
return (fx >= Range && fx <= VSize - Range && fy >= Range && fy <= VSize - Range);
}
public static void main(String[] args){
Application.launch();
}
}