简单生成随机迷宫的算法___Prim和DFS

快要大四了,要赶紧复习下数据结构好找工作,哈哈哈哈哈,所以最近在看图相关的算法,光看书没什么感觉,准备实践一下,写个迷宫算法

主要用Prim算法和DFS算法实现了一遍,没有用BFS是因为Prim的思路跟BFS的其实差不太多,但是看别的博客都说Prim比较自然

思路其实大同小异,主要为:常见迷宫(即任意两点间都能够找到路径,且仅有一条成功路径),迷宫可看做一组连通图(用数组储存),默认所有点间都为墙,我们需要做的就是遍历整个图中所有的点,并且不重复访问,在遍历图的过程中将相邻(指的是上下左右相邻)的两个点间的墙随机打通;满足条件的常见算法有:Prim,Kruskal,DFS,BFS,(另外有个博客中写到了递归分割算法,但是因为本菜很懒没有去具体了解)

定义数组:设我们要做一个row*column的迷宫,那么我们的数组的行列应为2*row+1和2*cloumn+1 (不理解为什么可以看这个表情  |O|O|    O为点,|为墙)

首先我们要造一堆墙围着相邻的格子,用于之后去打破如图:

简单生成随机迷宫的算法___Prim和DFS_第1张图片

代码如下:

 //初始化一个地图  默认所有路不通
 //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)];

            }
        }

    }

 

生成效果如下:

简单生成随机迷宫的算法___Prim和DFS_第2张图片

 

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);
            }
            
        }
        

    }

效果图如下:

简单生成随机迷宫的算法___Prim和DFS_第3张图片

感觉两种效果差不多,但是明显DFS的代码量更少一些,哈哈哈哈哈,不过多生成几次会发现Prim好像确实更自然一些

是不是很简单捏?

可能写的不是很好,但是小菜鸡会茁壮成长的!!!

虽然说不知道你们需不需要,但是贴心的我还是把unity的demo准备好了,基本不用考虑版本匹配的问题,因为没写多少行代码.哈哈哈

gayhub的地址: https://github.com/Bigbao123/Labyrinth

 

许愿:能够顺利成为一名游戏人!

你可能感兴趣的:(C#,Unity,算法)