⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mazegame.java

📁 一个迷宫游戏,最大特色是可以随机生成迷宫,可以遍历迷宫得到答案,可以在迷宫中添加追赶者.随机生成迷宫算法和遍历算法比较经典,可提供参考.
💻 JAVA
📖 第 1 页 / 共 4 页
字号:

/**
 * 本类定义了一个迷宫(面板)以及和迷宫相关的一些操作.直接把迷宫面板对象加入程序主面板即可.
 * 
 * @author 山
 * 
 */
class Maze extends JPanel {
	private final int DELAY=1200;// 追赶者走动延迟时间(注意:追赶者很聪明,因此走动延迟时间长一点)
	private int checkSize, width, height;// (迷宫由单位方格构成)单位方格的边长,迷宫的宽度,迷宫的高度
	private Wall[][] horizontal;// 单位水平墙.例如:若迷宫长度为3*5,则horizontal为4*5数组
	private Wall[][] vertical;// 单位竖直墙.例如:若迷宫长度为3*5,则horizontal为3*6数组
	private Check[][] checks;// 单位方格的状态(在遍历迷宫时需要).例如:若迷宫长度为3*5,则checks为3*5数组
	private int playerRow, playerColumn;// 玩家目前所在行,目前所在列
	private boolean[][] notMarked;// 单位方格是否有标记过(在随机生成迷宫时需要)
	private boolean solve;// 控制是否打印结果
	private Timer timer;// 定时器:追赶者每隔一段时间就会在迷宫中走动,玩家碰到追赶者就输.
	private boolean chase;// 追赶者开关器
	private Chaser[] chasers;// 追赶者们
	private int tearDownAllowed;// 玩家可拆墙次数限制
	private int currentResult;// 当前玩家的输赢情况:-1表示玩家输,0表示仍在进行,1表示玩家赢

	private enum Check {// 遍历迷宫时,单位方格的状态:notFoot--未踏足过;notMain--踏足过但不在主路径上;
		// main--踏足过且在主路径上(主路径即从起点到终点的路径)
		notFoot, notMain, main;
	}

	private enum Wall {// 单位墙的存在状态: notExist--不存在;exist--存在
		notExist, exist;
	}

	/**
	 * 一个新的随机生成的迷宫(面板).墙壁为黑色,玩家为蓝色,追赶者为红色. 建议本迷宫面板背景设置为白色. 玩家初始在左上角起点.右下角为终点.
	 * 
	 * @param checkSize
	 *            (迷宫由单位方格构成)单位方格的边长,以像素输入
	 * @param width
	 *            迷宫的宽度.例如:宽度若为3即宽度为3个单位方格
	 * @param height
	 *            迷宫的高度.例如:高度若为3即高度为3个单位方格
	 * @param numberOfChasers
	 *            追赶者的数量(不要追赶者则为0)
	 * @param tearDownAllowed
	 *            玩家可拆墙次数限制(不可拆墙则为0,可拆无数次则为-1)
	 */
	public Maze(int checkSize, int width, int height, int numberOfChasers,
			int tearDownAllowed) {
		this.checkSize = checkSize;
		this.width = width;
		this.height = height;
		this.tearDownAllowed = tearDownAllowed;
		this.playerRow = 0;
		this.playerColumn = 0;// 玩家初始在左上角的方格:起点
		this.solve = false;// 不打印结果
		this.timer = new Timer(DELAY, new TimerListener());// 追赶者移动定时器

		if (numberOfChasers < 1) {
			this.chase = false;// 玩家设定追赶者为"关"
			chasers = new Chaser[0];
		} else {
			this.chase = true;
			chasers = new Chaser[numberOfChasers];// 初始化追赶者
			for (int i = 0; i < numberOfChasers; i++) {
				Random gen = new Random();
				int ranRow, ranColumn;
				ranRow = gen.nextInt(this.height);
				ranColumn = gen.nextInt(this.width);
				chasers[i] = new Chaser(ranRow, ranColumn);
			}
			chaseOn();// 开启追赶者,使其移动
		}

		this.currentResult = 0;// 游戏进行中
		this.checks = new Check[this.height][this.width];// 迷宫为height*width个方格
		this.horizontal = new Wall[this.height + 1][this.width];
		// 单位水平墙.horizontalWalls有height*width-1个
		this.vertical = new Wall[this.height][this.width + 1];
		// 单位竖直墙.horizontalWalls有(height-1)*width个
		this.notMarked = new boolean[this.height][this.width];
		// 单位方格是否有标记过(在随机生成迷宫时需要)

		// 以下为迷宫具体初始化:
		// 全部方格状态初始化为"未踏足过"
		for (int i = 0; i < this.height; i++)
			for (int j = 0; j < this.width; j++)
				checks[i][j] = Check.notFoot;

		// 全部方格初始化为"未标记"
		for (int i = 0; i < this.height; i++)
			for (int j = 0; j < this.width; j++)
				notMarked[i][j] = true;

		// 全部水平墙状态初始化为"存在"
		for (int i = 0; i < this.height + 1; i++)
			for (int j = 0; j < this.width; j++)
				horizontal[i][j] = Wall.exist;

		// 全部竖直墙状态初始化为"存在"
		for (int i = 0; i < this.height; i++)
			for (int j = 0; j < this.width + 1; j++)
				vertical[i][j] = Wall.exist;

		// 迷宫左上角方格为入口,因此它的左墙为不存在
		vertical[0][0] = Wall.notExist;

		// 迷宫右下角为出口,因此它的右墙不存在
		vertical[this.height - 1][this.width] = Wall.notExist;

		// 随机生成迷宫.其实质是:拆除若干个水平墙和竖直墙以便能形成迷宫
		generate(0, 0);

		// 生成迷宫后立刻遍历,得到答案:checks[i][j]为Check.main的都是在主路径上
		// 分析Java程序设计教程书上的算法,是从首格开始的valid,但是本算法不是从首格开始.因此要从0,1开始,若不行则从1,0开始
		if (!traverse(0, 0, 0, 1))
			traverse(0, 0, 1, 0);

		// 遍历完后可能有些不在主路径上的方格未设置成Check.notMain,最好执行以下语句
		for (int i = 0; i < this.height; i++)
			for (int j = 0; j < this.width; j++)
				if (checks[i][j] != Check.main)
					checks[i][j] = Check.notMain;

	}

	/**
	 * 生成一个随机迷宫,本方法提供给构造方法及newMaze方法调用. 实质是:拆除若干个水平墙和竖直墙.算法来源:java2游戏设计,清华大学出版社
	 * 
	 * @param i
	 *            可以是0至height-1中任一数值,建议输入0
	 * @param j
	 *            可以是0至width-1中任一数值,建议输入0
	 */
	private void generate(int i, int j) {
		notMarked[i][j] = false;// 标记这个方格,以便不再访问

		Random gen = new Random();// 用于随机一个方向

		if (i == 0 && j == 0)// 左上角方格
			while (notMarked[i + 1][j] || notMarked[i][j + 1])// 只要下面的方格和右边方格有一个未标记就进入循环
			{
				switch (gen.nextInt(2)) {
				case 0:
					if (notMarked[i + 1][j])// 下面方格未标记
					{
						horizontal[i + 1][j] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i + 1, j);
					}// 继续从此方格开始生成
					break;
				case 1:
					if (notMarked[i][j + 1])// 右边方格未标记
					{
						vertical[i][j + 1] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i, j + 1);
					}// 继续从此方格开始生成
					break;
				}
			}

		if (i == this.height - 1 && j == 0)// 左下角方格
			while (notMarked[i - 1][j] || notMarked[i][j + 1])// 只要上面的方格和右边方格有一个未标记就进入循环
			{
				switch (gen.nextInt(2)) {
				case 0:
					if (notMarked[i - 1][j])// 上面方格未标记
					{
						horizontal[i][j] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i - 1, j);
					}// 继续从此方格开始生成
					break;
				case 1:
					if (notMarked[i][j + 1])// 右边方格未标记
					{
						vertical[i][j + 1] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i, j + 1);
					}// 继续从此方格开始生成
					break;
				}
			}

		// 以下算法同上,不详述.总之是分类:四个角,四条边,其余.

		if (i == 0 && j == this.width - 1)// 右上角方格
			while (notMarked[i + 1][j] || notMarked[i][j - 1]) {
				switch (gen.nextInt(2)) {
				case 0:
					if (notMarked[i + 1][j]) {
						horizontal[i + 1][j] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i + 1, j);
					}// 继续从此方格开始生成
					break;
				case 1:
					if (notMarked[i][j - 1]) {
						vertical[i][j] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i, j - 1);
					}// 继续从此方格开始生成
					break;
				}
			}

		if (i == this.height - 1 && j == this.width - 1)// 右下角方格
			while (notMarked[i - 1][j] || notMarked[i][j - 1]) {
				switch (gen.nextInt(2)) {
				case 0:
					if (notMarked[i - 1][j]) {
						horizontal[i][j] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i - 1, j);
					}// 继续从此方格开始生成
					break;
				case 1:
					if (notMarked[i][j - 1]) {
						vertical[i][j] = Wall.notExist;// 拆除它们之间的墙,使它们连通
						generate(i, j - 1);
					}// 继续从此方格开始生成
					break;
				}
			}

		if (i == 0 && j != 0 && j != this.width - 1)// 第一行方格除了左上角和右上角
			while (notMarked[i][j + 1] || notMarked[i][j - 1]
					|| notMarked[i + 1][j]) {
				switch (gen.nextInt(3)) {
				case 0:
					if (notMarked[i][j + 1]) {
						vertical[i][j + 1] = Wall.notExist;
						generate(i, j + 1);
					}
					break;
				case 1:
					if (notMarked[i][j - 1]) {
						vertical[i][j] = Wall.notExist;
						generate(i, j - 1);
					}
					break;
				case 2:
					if (notMarked[i + 1][j]) {
						horizontal[i + 1][j] = Wall.notExist;
						generate(i + 1, j);
					}
					break;
				}
			}

		if (i == this.height - 1 && j != 0 && j != this.width - 1)// 最后一行方格除了左下角和右下角
			while (notMarked[i][j - 1] || notMarked[i][j + 1]
					|| notMarked[i - 1][j]) {
				switch (gen.nextInt(3)) {
				case 0:
					if (notMarked[i][j - 1]) {
						vertical[i][j] = Wall.notExist;
						generate(i, j - 1);
					}
					break;
				case 1:
					if (notMarked[i][j + 1]) {
						vertical[i][j + 1] = Wall.notExist;
						generate(i, j + 1);
					}
					break;
				case 2:
					if (notMarked[i - 1][j]) {
						horizontal[i][j] = Wall.notExist;
						generate(i - 1, j);
					}
					break;
				}
			}

		if (j == 0 && i != 0 && i != this.height - 1)// 第一列方格除了左上角和左下角
			while (notMarked[i - 1][j] || notMarked[i + 1][j]
					|| notMarked[i][j + 1]) {
				switch (gen.nextInt(3)) {
				case 0:
					if (notMarked[i - 1][j]) {
						horizontal[i][j] = Wall.notExist;
						generate(i - 1, j);
					}
					break;
				case 1:
					if (notMarked[i + 1][j]) {
						horizontal[i + 1][j] = Wall.notExist;
						generate(i + 1, j);
					}
					break;
				case 2:
					if (notMarked[i][j + 1]) {
						vertical[i][j + 1] = Wall.notExist;
						generate(i, j + 1);
					}
					break;
				}
			}

		if (j == this.width - 1 && i != 0 && i != this.height - 1)// 最后一列方格除了右上角和右下角
			while (notMarked[i - 1][j] || notMarked[i + 1][j]
					|| notMarked[i][j - 1]) {
				switch (gen.nextInt(3)) {
				case 0:
					if (notMarked[i - 1][j]) {
						horizontal[i][j] = Wall.notExist;
						generate(i - 1, j);
					}
					break;
				case 1:
					if (notMarked[i + 1][j]) {
						horizontal[i + 1][j] = Wall.notExist;
						generate(i + 1, j);
					}
					break;
				case 2:
					if (notMarked[i][j - 1]) {
						vertical[i][j] = Wall.notExist;
						generate(i, j - 1);
					}
					break;
				}
			}

		if (i > 0 && i < this.height - 1 && j > 0 && j < this.width - 1)// 除了最外面一层方格外的其余方格(即有(height-2)*(width-2)个)
			while (notMarked[i - 1][j] || notMarked[i + 1][j]
					|| notMarked[i][j - 1] || notMarked[i][j + 1]) {
				switch (gen.nextInt(4)) {
				case 0:
					if (notMarked[i - 1][j]) {
						horizontal[i][j] = Wall.notExist;
						generate(i - 1, j);
					}
					break;
				case 1:
					if (notMarked[i + 1][j]) {
						horizontal[i + 1][j] = Wall.notExist;
						generate(i + 1, j);
					}
					break;
				case 2:
					if (notMarked[i][j - 1]) {
						vertical[i][j] = Wall.notExist;
						generate(i, j - 1);
					}
					break;
				case 3:
					if (notMarked[i][j + 1]) {
						vertical[i][j + 1] = Wall.notExist;
						generate(i, j + 1);
					}
					break;
				}
			}
	}

	/**
	 * 新建一个迷宫,玩家移动,显示(取消)主路径,追赶者移动,拆墙,这些方法都会自动调用本方法来重画迷宫.
	 * 
	 * @param page
	 *            图形上下文

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -