迷宫生成算法

最近做课设时,有一个部分需要用到迷宫的生成算法. 在这里介绍一种使用深度优先搜索生成迷宫的算法.

个人博客地址

最终的效果

先上几张效果图,图中绿色的表示障碍,灰色表示道路(我的世界既视感).




如何描述迷宫

迷宫其实就是一个复杂的地形图,在这个地形中有基本的障碍和通道,当然也可以有其他元素。

我们这里用最简单的方式描述迷宫——矩阵。迷宫中的地形也只有障碍和通道两种元素。可以用0和1表示这两种元素。

因此我们用一个存储着0和1,M*N大小的矩阵就可以描述迷宫啦!

迷宫的特点

  1. 从设定的起点到终点必须是连通的(否则,还能不能好好玩耍了)
  2. 从起点到终点只有一条通路.(其实也可以有多条,根据实际需要设定)

思路

从迷宫特点的描述有没有想到什么?

迷宫就是一个图,要求任意设定的起点和终点之间是连通的,就是一个 全连通图.但是如果这个图的连通度太高,迷宫就没有难度了,所以我们要求图中任意顶点之间只有一条路.

什么样的图只有一条路, 无环图.

所以我们需要的是无环的连通图,这是什么?

我们的迷宫就是一个树,因此迷宫的生成算法就是树的生成算法,树的生成算法有深度优先遍历和广度优先遍历, 在这里使用深度优先.

迷宫的生成过程

1 初始状态

图中绿色的表示障碍,灰色表示道路(空白)

由于迷宫四周都是障碍, 图的宽和高都必须是奇数.

2 迷宫的生成

1 任意选择一个空白块, 将该空白块作为树的根结点.

2 从根节点出发隔一个元素块查找四周(上,下,左,右,四个方向,不包括对角线方向)其他的空白块.

从该结点出发,四周只有两个空白块

从该结点出发,四周有四个空白块

3 随机选取其中一个空白块, 将道路沿该方向拓展, 即把夹在这两个空白块之间的障碍块去掉, 改成空白块.

把夹在这两个空白块之间的障碍块去掉

4 更新当前结点, 然后从当前结点出发,重复步骤2,3.

5 当遇到一个结点周围没有空白块时, 即没有可拓展道路的方向时, 回退并更新当前结点, 直至当前结点四周有空白块, 重复步骤2,3.

该结点周围没有空白块

6 当回退到根节点没有任何可以拓展的道路时, 算法结束, 迷宫也就生成了.

3 设定起点和终点

选取迷宫中的两个空白块作为迷宫的起点和终点,一个完整的迷宫就诞生了.

核心算法–深度优先

由于这部分算法是程序的一部分,不能完整运行,仅供参考.

在程序中用到了Qt中的容器QVector,可以用STL中的std::vector代替; 用到的qsrand()和qrand()生成随机数,可以使用C标准库中的srand()和rand()函数代替.

/*
@ 生成迷宫
*/
void GenerateMaze::Maze(int width, int height)
{
    //初始化矩阵, 申请内存
    maze_matrix_ = new int*[height];
    for(int i=0; i<height; i++)
    {
        maze_matrix_[i] = new int[width];
    }

    for(int i=0; i<height; i++)
    {
        for(int j=0; j<width;j++)
        {
            if(i % 2 == 0 || j % 2==0)
            {
                maze_matrix_[i][j] = 1;  //障碍
            }
            else
            {
                maze_matrix_[i][j] = 0;  //道路(空白)
            }
        }
    }

    qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); //设置随机数种子

    maze_matrix_[1][1] = 2; //选取(1,1)作为根节点, 并将根节点的状态设置成2

    this->generateMaze(1, 1);   //深度优先遍历

    for(int i=0; i<height; i++)
    {
        for(int j=0; j<width;j++)
        {
            if(maze_matrix_[i][j] == 2)
            {
                maze_matrix_[i][j] = 0; //将状态为2的结点重新设置为0, 表示可通行道路
            }
        }
    }
}


/*
@ brief:深度优先生成迷宫(递归实现)
*/
void Maze::generateMaze(int pos_i, int pos_j)
{
    //到达边界, 返回
    if(pos_j < 0 || pos_j >= width || pos_i < 0 || pos_i >= height)
    {
        return;
    }

    QVector<int> vec = existedRoad((const int**)maze_matrix_, pos_i, pos_j); //查找当前结点四周空白块

    //四周没有空白块, 返回
    if(vec.size() == 0)
    {
        return;
    }

    for(int i=0; i < vec.size();)
    {
        int index = qrand()%vec.size(); //随机选择其中一个空白块

        switch(vec[index])
        {
        case D_LEFT:    //左
            if(maze_matrix_[pos_i][pos_j-2] != 2)
            {
                maze_matrix_[pos_i][pos_j-1] = 2;    //将走过的路径设为2, 防止重复经过
                maze_matrix_[pos_i][pos_j-2] = 2;
                this->generateMaze(pos_i, pos_j-2); //更新结点, 递归
            }
            break;
        case D_RIGHT:   //右
            if(maze_matrix_[pos_i][pos_j+2] != 2)
            {
                maze_matrix_[pos_i][pos_j+1] = 2;
                maze_matrix_[pos_i][pos_j+2] = 2;
                this->generateMaze(pos_i, pos_j+2); //更新结点, 递归
            }
            break;  
        case D_UP:  //上
            if(maze_matrix_[pos_i-2][pos_j] != 2)
            {
                maze_matrix_[pos_i-1][pos_j] = 2;
                maze_matrix_[pos_i-2][pos_j] = 2;
                this->generateMaze(pos_i-2, pos_j); //更新结点, 递归
            }
            break;
        case D_DOWN:    //下
            if(maze_matrix_[pos_i+2][pos_j] != 2)
            {
                maze_matrix_[pos_i+1][pos_j] = 2;
                maze_matrix_[pos_i+2][pos_j] = 2;
                this->generateMaze(pos_i+2, pos_j); //更新结点, 递归
            }
            break;
        }

        vec.remove(index);  //清空vec
    }
}

/*
@brief: 查找结点周围的空白块
*/

const QVector<int> Maze::existedRoad(const int **mat, int i, int j)
{
    QVector<int> vec;

    if(j-2 >= 0 && mat[i][j-2] == 0)
    {
        vec.push_back(D_LEFT);  //左边有空白块
    }

    if(j+2 < width && mat[i][j+2] == 0)
    {
        vec.push_back(D_RIGHT); //右边有空白块
    }

    if(i-2 >= 0 && mat[i-2][j] == 0)
    {
        vec.push_back(D_UP);    //上边有空白块
    }

    if(i+2 < height && mat[i+2][j] == 0)
    {
        vec.push_back(D_DOWN);  //下边有空白块
    }

    return vec;
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值