java的迷宫游戏代码 java迷宫小游戏随机地图_i++

一、生成迷宫算法简介
1.1 Depth First Search Algorithm(深度优先遍历算法)
算法思路
1、从第一个单元开始,检查当前单元是否堵塞(即周围四个单元都 是已被访问或不可访问)
2、若不堵塞,则随机选择一个相邻单元作为下一单元,检查是否可访问
3、若可访问,则打破当前单元与下一单元之间的墙壁,将当前单元入栈,将下一单元作为当前单元;若不不可访问,则回到步骤2
4、若当前单元堵塞,则回退至上一单元
5、如此遍历,直到所有的单元都被访问
特点
该算法产生的迷宫图片具有一条明显的主路径,比较弯曲。
1.2 Randomized Prim’s Algorithm(随机普利姆算法)
算法思路
1、使迷宫全是墙,即每个单元之间无法连通
2、随机选一个单元格作为迷宫的通路,然后把它的邻墙放入列表
3、当列表里还有墙时:
从列表里随机选一个墙,如果这面墙分隔的两个单元只有一个单元被访问过:那就从列表里移除这面墙,即把墙打通,让未访问的单元成为迷宫的通路把这个单元的墙加入列表;
如果墙两面的单元都已经被访问过,那就从列表里移除这面墙
4、如此遍历,直到列表没有墙
特点
相对于深度优先的算法,Randomized Prim’s Algorithm不是优先选择最近选中的单元格,而是随机的从所有的列表中的单元格进行选择,新加入的单元格和旧加入的单元格同样概率会被选择,新加入的单元格没有优先权。因此其分支更多,生成的迷宫更复杂,岔路更多,难度更大,也更自然。

1.3 Recursive Division Algorithm(回溯算法)

算法思路

1、让迷宫全是通路,即没有墙

2、随机选择一偶数行和一偶数列让其全部变为墙,通过这两堵墙将整个迷宫分为四个子迷宫

3、在3面墙上各挖一个洞(为了确保连通)

4、如果子迷宫仍可分割成四个子迷宫,回到步骤1继续分割子迷宫

5、如此遍历,直到所有子迷宫都不可分割。

特点

Recursive Division Algorithm十分高效,生成的迷宫较为简单。

二、程序思路

2.1、MazeUnit

1、迷宫单元是组成迷宫图的基础,不同算法会对迷宫单元进行不同的属性设置。在这里,单元既包括四面墙,也包括方格通路。迷宫单元有四个属性,坐标(X、Y)、可访问性(isVisited)、是否被访问(accessible)、墙数组(int[])。

2、墙的表示使用整型数组来表示,int[0]、int[1]、int[2]、int[3]的值分别表示该迷宫单元的左、上、下、右墙壁是否存在,值为0表示墙不存在,值为1表示墙存在。至于左、上、下、右是对应0、1、2、3,是为了使相对面的墙数组下标相加为3,便于计算。

3、这样定义迷宫单元有一个缺点,就是相邻单元的墙是会重合的,所以在绘制最终的迷宫图片时,表示墙的线条时绘制两遍重叠的。

java的迷宫游戏代码 java迷宫小游戏随机地图_i++_02


2.2 UI

界面模块主要功能是实现输入想要的迷宫列数和行数,同时对于输入的数字进行一定的过滤,

java的迷宫游戏代码 java迷宫小游戏随机地图_数组_03


2.3 PathStack

堆栈的主要作用是在遍历时记录主路径。在遍历时,将每次走过的迷宫单元放入堆栈中。当遍历时遇到当前迷宫单元堵塞的情况时,就弹出栈顶的迷宫单元,实现返回上一迷宫单元。

堆栈的另一个作用就是当遍历经过出口的坐标时,可以复制当前堆栈储存来保存入口到出口的路径。根据算法来理解一下,其实很容易明白:该路径便是入口到出口的最短路径。

2.4 CreateMaze

主模块便是生成迷宫算法实现的核心了。主要流程如下:

1、初始化迷宫数据:创建二维数组MazeUnit[][];将所有的墙设置为存在;将所有的单元设置为未访问的;设置外围边界不可访问(为防止数组越界,定义MazeUnit[][]时比实际的迷宫向外拓展了一层,这里称为外围边界)。
2、生成入口和出口:将入口的墙打破(即定义该单元的其中某一面墙不存在),同时随机在整个迷宫的下边界和右边界分别选择一个出口并将墙打破。
3、定义用于遍历的辅助方法:randway()用于随机选择下一个相邻单元;breakwall()用于将当前单元与下一个相邻单元之间的墙壁打破;isBlock用于判断当前单元是否堵塞(即四个相邻单元都是已访问或者不可访问);countIsVisited()用于统计当前已经访问的单元格数。
4、对整个迷宫进行遍历:从入口(第一个单元)开始,检查当前单元是否堵塞,若不堵塞,则随机选择一个相邻单元作为下一单元,检查是否可访问。若可访问,则打破当前单元与下一单元之间的墙壁,将当前单元入栈,将下一单元作为当前单元;若不不可访问,则再次随机选择一个相邻单元。若当前单元堵塞,则回退至上一单元。如此遍历,直到所有的单元都被访问。
5、记录两条路径:当遍历时,当前单元为随机选择的出口时,将当前堆栈复制并储存,记录下两个出口的路径。
6、遍历完成之后,调用了drawMaze类,在该类中重写了paint方法,将遍历后的迷宫数组数据,转化为坐标,绘制成图,并将两个出口的路径也表示出来。
三、程序实现
MazeUnit

public class MazeUnit {
	public boolean isVisited;//是否已经被访问
	public int wall[];//0、1、2、3分别代表左、上、下、右
	public boolean accessible;//可访问性
	int x,y;//坐标
	
	//构造方法
	MazeUnit(boolean gisVisited, int[] gwall, boolean gaccessible){
		isVisited = gisVisited;
		wall = gwall;
		accessible = gaccessible;
	}
}

CreateMaze

public static int x,y,nx,ny,dir;//x、y为当前单元坐标,nx、ny为相邻单元的坐标,dir为当前单元的墙的方向
	
	public static void initialize(int mx,int my) {
		MazeUnit[][] pos = new MazeUnit[mx][my];//mx、my为行数、列数加2
		//初始化迷宫单元数组
		int i,j;
		for(i=0;i<mx;i++) {
			for(j=0;j<my;j++) {
				int[] wall = new int[4];
				for(int w=0;w<4;w++) {
					wall[w] = 1;//将所有迷宫单元用墙隔开
				}
				pos[i][j] = new MazeUnit(false,wall,true);
				pos[i][j].x = i;
				pos[i][j].y = j;
				}
		}
		
		//设置可访问性数组的值,创建外围边界
		for(i=1;i<mx-1;i++) {
			for(j=1;j<my-1;j++) {
				pos[i][j].accessible = true;
			}
		}
		i=0;
		for(j=0;j<my;j++) {
			pos[i][j].accessible = false;
		}
		i=mx-1;
		for(j=0;j<my;j++) {
			pos[i][j].accessible = false;
		}
		j=0;
		for(i=0;i<mx;i++) {
			pos[i][j].accessible = false;
		}
		j=my-1;
		for(i=0;i<mx;i++) {
			pos[i][j].accessible = false;
		}
		
		//生成入口和两个随机出口
		Random randx = new Random();
		int endx = randx.nextInt(mx-2)+1;
		Random randy = new Random();
		int endy = randy.nextInt(my-2)+1;
		pos[1][1].wall[0] = 0;
		pos[endx][my-2].wall[2] = 0;
		pos[mx-2][endy].wall[3] = 0;
		
		IArrayStack path = new StackImpl(mx-2,my-2);
		path.push(pos[1][1]);
		
		x=y=1;
		nx=ny=0;
		dir=0;
		Object[] pathx = new Object[(mx-2)*(my-2)];
		Object[] pathy = new Object[(mx-2)*(my-2)];
		while(countIsVisited(pos,mx,my) < (mx-2)*(my-2)) {
			randway();
			//记录两个出口的路径
			if((x == endx)&&(y == my-2)) {
				pathx = path.copyArray().clone();
			}
			if((x == mx-2)&&(y == endy)) {
				pathy = path.copyArray().clone();
			}
			
			if(isBlock(pos,x,y) == false) {
				breakwall(pos,path);
			}
			else if(isBlock(pos,x,y) == true) {
				pos[x][y].isVisited = true;
				path.pop();//将当前单元出栈
				int[] tempwall = new int[4];
				MazeUnit temp = new MazeUnit(false,tempwall,true);
				temp = (MazeUnit) path.top();//将前一单元转换为当前单元
				x = temp.x;
				y = temp.y;//将坐标转换为前一单元的坐标
				
			}
		}
		
		JFrame win2 = new JFrame();
		win2.setTitle("随机生成迷宫");
		win2.setSize(mx*30+80, my*30+80);
		win2.setLocationRelativeTo(null);
		win2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//绘制迷宫的图片
		drawMaze pic = new drawMaze(mx,my);
		pic.getData(pos,pathx,pathy,mx,my);
		win2.add(pic, BorderLayout.CENTER);

		win2.setResizable(false);
		win2.setVisible(true);
	}

四、总结
1、 整个算法还算是比较容易理解的,实际上就是把整个迷宫的单元随机访问一遍,访问迷宫单元实际上就是把该单元接入通路之中,也就是说被访问过的任意两个单元之间是一定有一条通路的。按照这个想法,其实不管迷宫是要多少个出口和入口,都是可以实现的。