基于Java FX,BFS和Prim实现的迷宫小游戏

介绍:
 程序由两个文件组成,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();
		 }
}

在这里插入图片描述

  • 10
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值