快要大四了,要赶紧复习下数据结构好找工作,哈哈哈哈哈,所以最近在看图相关的算法,光看书没什么感觉,准备实践一下,写个迷宫算法
主要用Prim算法和DFS算法实现了一遍,没有用BFS是因为Prim的思路跟BFS的其实差不太多,但是看别的博客都说Prim比较自然
思路其实大同小异,主要为:常见迷宫(即任意两点间都能够找到路径,且仅有一条成功路径),迷宫可看做一组连通图(用数组储存),默认所有点间都为墙,我们需要做的就是遍历整个图中所有的点,并且不重复访问,在遍历图的过程中将相邻(指的是上下左右相邻)的两个点间的墙随机打通;满足条件的常见算法有:Prim,Kruskal,DFS,BFS,(另外有个博客中写到了递归分割算法,但是因为本菜很懒没有去具体了解)
定义数组:设我们要做一个row*column的迷宫,那么我们的数组的行列应为2*row+1和2*cloumn+1 (不理解为什么可以看这个表情 |O|O| O为点,|为墙)
首先我们要造一堆墙围着相邻的格子,用于之后去打破如图:
代码如下:
//初始化一个地图 默认所有路不通
//row行表示的是刚开始空白格子的行数,而格子之间还有墙壁,所以最终产生的二维数组大小实际为(2row+1) * (2colum+1)
//看到其他博客有其他的思路也可以,可以凭自己爱好:例如 空间:O(row*col),时间:O(墙数=(row-1)*col+row*(col - 1))
//默认为7行6列
private int row = 7;
private int column = 6;
private int[,] LabId;//存放迷宫的数组
private Vector2[,] Pos;
public void Init()
{
int r = 2 * row + 1, c = 2 * column + 1;
LabId = new int[r, c];
for (int i = 0; i < r; i++) //将所有格子都设为墙
for (int j = 0; j < c; j++)
LabId[i, j] = 0;//0 为墙 1为路
//中间格子放为1
for (int i = 0; i < row; i++)
for (int j = 0; j < column; j++)
LabId[2 * i + 1, 2 * j + 1] = 1;//0 为墙 1为路
//普里姆算法
//accLabPrime();
//深度优先搜索算法
//accLabDFS();
}
1.Prim算法实现的方式:随机打通相邻(上下左右)的墙壁
///
/// 通过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上 4下
int[] offR = { -1, 1, 0, 0 };
int[] offC = { 0, 0, 1, -1 };
//四个方向的偏移 左右上下
int[] offS = { -1, 1, row, -row };
for (int i = 0; i < count; i++)
{
acc[i] = 0;
noacc[i] = 0;
}
//起点
acc[0] = Random.Range(0, 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 = Random.Range(0, 4); //包含min但不包含max。
int repos;
int move_x, move_y;
//计算出移动方位
repos = pos + offS[point];
move_x = x + offR[point];
move_y = y + offC[point];
//判断移动是否合法
if (move_y > -1 && move_x > -1 && move_x < row && move_y < column && repos >= 0 && repos < count && noacc[repos] != 1)
{
noacc[repos] = 1;
acc[++accsize] = repos;
pos = repos;
offpos = point;
//相邻的格子中间的位置放1
LabId[2 * x + 1 + offR[point], 2 * y + 1 + offC[point]] = 1;
break;
}
else
{
if (accsize == count - 1)
return;
continue;
}
}
if (offpos < 0)
{//周边没有找到能走的路了 从走过的路里重新找个起点
pos = acc[Random.Range(0, accsize + 1)];
}
}
}
生成效果如下:
2.深度优先搜素算法实现方式:思路同样也是随机打通相邻(上下左右)的墙壁
///
/// 考虑到bfs和prim在思路上有些相似 在这里选择实现DFS会更有趣一些
///
public void accLabDFS()
{
int[] lab;//访问队列
int count = row * column;
//初始化数组
lab = new int[count];
for (int i = 0; i < count; i++)lab[i] = 0;
for (int v=0;v
/// 使用DFS算法,借助递归思想访问某一顶点v,找v点附近且未被访问的点w,在找w附近未被访问的点....(循环...),直到没有继续能找下去的点,依次退回最近被访问的点,如果还有该顶点的其他邻居没有被访问,就从邻居点开始继续搜索,把相邻的部分格子打通
///
///
///
public void DFS(int[] LabG, int v)
{
//访问顶点
LabG[v] = 1;
//四个邻居 上下左右
int[] neighbor = { v + row, v - row, v - 1, v + 1 };
//Row上个方向的偏移 Column上各方向的偏移 上下左右
int[] offR = { 0, 0, -1, 1 }, offC = { 1, -1, 0, 0 };
int[] tag = { -1, -1, -1,-1 };//记录打通位置
int n = 0;//打通的次数
while (n<4) {
//随机打通一个方向
int i = Random.Range(0, 4);
if (tag[i] == 1) continue;
tag[i] = 1;
n++;
int w = neighbor[i];
//该点不存在
if (w > LabG.Length - 1 || w < 0)
continue;
//取出现在的v点的位置
int x = v % row;
int y = v / row;
//该点没有该方向上的邻居
if (i == 0 && y == column - 1) continue;
if (i == 1 && y == 0) continue;
if (i == 2 && x == 0) continue;
if (i == 3 && x == row - 1) continue;
//未访问的邻居
if (LabG[w] != 1)
{
//相邻的格子中间的位置放1
LabId[2 * x + 1 + offR[i], 2 * y + 1 + offC[i]] = 1;
DFS(LabG, w);
}
}
}
效果图如下:
感觉两种效果差不多,但是明显DFS的代码量更少一些,哈哈哈哈哈,不过多生成几次会发现Prim好像确实更自然一些
是不是很简单捏?
可能写的不是很好,但是小菜鸡会茁壮成长的!!!
虽然说不知道你们需不需要,但是贴心的我还是把unity的demo准备好了,基本不用考虑版本匹配的问题,因为没写多少行代码.哈哈哈
gayhub的地址: https://github.com/Bigbao123/Labyrinth
许愿:能够顺利成为一名游戏人!