目录
1.前言
2.前文(rand,随机数引入)
1.rand()
2.srand,随机数种子
3.用法:
1. 生成0~RAND_MAX里面的数
2.生成大于RAND_MAX的数
3.生成负数
3.正文(确信)
1.bfs写法:
1.让迷宫全是墙+确定起点
2.初始化
3.循环操作:
4.总代码:
2.DFS写法
1.初始化
2.DFS
3.总代码
3.并查集写法
1.创建列表
2.并查集基操
3.制造迷宫
4.(为了方便操作) 记录迷宫
5.总代码
4.三方法代码综合(2023/7/14新增)
5.注意事项
6.各方法对比(就我而言)
4.后记
(阅读本文前,请先保证已阅读前5篇!!!!)
(引入正题:)
先引入一位朋友的代码ZQK13的博客-CSDN博客
当你运行后,你会发现:
你眼前有一个不像MG的MG
怎么说呢:不好说吧,一个可行又不可行的办法
所以我简单更新了一篇······
为了突出随机,我们会使用一个伪随机数
它位于:
#include
这个头文件中(当然,还有很多)
但伪随机就是伪随机,你可以多次重复运行下面一段代码:
#include
using namespace std;
int main()
{
printf("%d\n",rand());
return 0;
}
发现没有:果然是伪随机
那就要讲到第二个好东西了
srand() 随机数种子(与MC中的种子一个意思(?))需要的头文件见下:
#include
#include
using namespace std;
int main()
{
srand(time(NULL));
return 0;
}
我们以当前时间为种子,就能保证伪随机不会那么伪了(你家时间一去还能反?)
对了,强调一个重点:在windows系统中,rand()能生成的随机数只能在0~32767之间
所以:简单的用法就是
#include
#include
using namespace std;
int main()
{
srand(time(NULL));
int mod=114;
int a=rand()%mod;//生成0~113之间的数
int b=rand()%mod+mod;//生成114~227之间的数
int c=rand()%514+114;//生成114~627之间的数
//公式:p=rand()%a+b; 生成 b ~ b+a-1 (包括两端)的随机数
//and so on
return 0;
}
#include
#include
using namespace std;
int main()
{
srand(time(NULL));
int a=rand();//32768进制数的高位
int b=rand();//32768进制数的低位
int c=a*32768+b;//计算出这个数
//建议函数封装
}
(凑个数)
#include
#include
using namespace std;
int main()
{
srand(time(NULL));
int a=-rand();
}
随机迷宫有很多种写法。
推荐两种
1.让迷宫全是墙.
2.选起点作为迷宫的通路,然后把它的邻墙放入列表
3.当列表里还有墙时
①.从列表里随机选一个墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
(a)那就从列表里移除这面墙,让未访问的单元格成为迷宫的通路
(b)把这个格子的墙加入列表
②.如果墙两面的单元格都已经被访问过,那就从列表里移除这面墙
void init()
{
x_p=X,y_p=Y;
start.x=X,start.y=Y;
memset(dt,WALL,sizeof(dt));
dt[X][Y]=KONG;
}
//迷宫大小
#define m 11
#define n 11
//构成
#define player '!'
#define wall '#'
#define kong ' '
//起点
#define X 1
#define Y 1
//操作
#define down 1
#define right 2
#define left 4
#define up 8
//迷宫内
#define WALL -1
#define KONG 2
注意:只能更改 m n player wall X Y (注意大小写)
可以更改n,m为任意奇数,player为玩家样式,wall为墙样式,(X,Y)为起点
while(where.size())
{
int r=rand()%(where.size());
qiang what=where[r];
x_p=what.x;
y_p=what.y;
switch(what.f)
{
case down:{x_p++;break;}
case right:{y_p++;break;}
case left:{y_p--;break;}
case up:{x_p--;break;}
}
if(dt[x_p][y_p]==WALL) dt[what.x][what.y]=dt[x_p][y_p]=KONG,put_wall();
where.erase(where.begin()+r);
}
#include
using namespace std;
//迷宫大小
#define m 11
#define n 11
//构成
#define player '!'
#define wall '#'
#define kong ' '
//起点
#define X 1
#define Y 1
//操作
#define down 1
#define right 2
#define left 4
#define up 8
//迷宫内
#define WALL -1
#define KONG 2
struct qiang
{
int x,y,f;
qiang(int xx,int yy,int ff) {x=xx,y=yy,f=ff;}
};
struct play_er{int x,y;}start,end;
vector where;
int x_p,y_p;
int dt[1010][1010];
void init()
{
x_p=X,y_p=Y;
start.x=X,start.y=Y;
memset(dt,WALL,sizeof(dt));
dt[X][Y]=KONG;
}
void put_wall()
{
if(x_p+1<=m&&dt[x_p+1][y_p]==WALL) where.push_back(qiang(x_p+1,y_p,down));
if(y_p+1<=n&&dt[x_p][y_p+1]==WALL) where.push_back(qiang(x_p,y_p+1,right));
if(x_p-1>=1&&dt[x_p-1][y_p]==WALL) where.push_back(qiang(x_p-1,y_p,up));
if(y_p-1>=1&&dt[x_p][y_p-1]==WALL) where.push_back(qiang(x_p,y_p-1,left));
}
int main()
{
init();
srand(time(NULL));
put_wall();
while(where.size())
{
int r=rand()%(where.size());
qiang what=where[r];
x_p=what.x;
y_p=what.y;
switch(what.f)
{
case down:{x_p++;break;}
case right:{y_p++;break;}
case left:{y_p--;break;}
case up:{x_p--;break;}
}
if(dt[x_p][y_p]==WALL) dt[what.x][what.y]=dt[x_p][y_p]=KONG,put_wall();
where.erase(where.begin()+r);
}
for(int i=0;i<=m+1;i++)
{
for(int j=0;j<=n+1;j++)
{
if(i==start.x&&j==start.y) printf("%c",player);
else if(dt[i][j]==KONG) printf("%c",kong);
else printf("%c",wall);
}
printf("\n");
}
return 0;
}
思路差不多
- 初始化大地图,只有0和1的状态。其中,0和1分别代表道路和墙体,注意四周皆墙
- 靠近边缘随机选取状态为1的道路点,作为起点 a
- 在起点 a 的上下左右四个方向,跨两个寻找同样为1的道路点 c
- 如果找到,则将它们之间的墙体 b 打通,然后将 c 作为新的起点,然后继续进行第2步
- 如果四个方向都没有找到,则不断回退到上一点,直到找到有一点其他方向满足条件,然后继续查询
- 当查无可查的时候,迷宫也就填充完毕了
void init()
{
start.x=X,start.y=Y;
memset(dt,WALL,sizeof dt);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==1||i==n||j==1||j==m) dt[i][j]=WALL;
if(i%2==0&&j%2==0) dt[i][j]=KONG;
else dt[i][j]=WALL;
}
}
dt[X][Y]=KONG;
}
void dfs(int x,int y)
{
bool f[5]={0};
while(1)
{
if(f[0]&&f[1]&&f[2]&&f[3]) return ;
int r=rand()%4;
int nx=fx[r][0],ny=fx[r][1],zx=zj[r][0],zy=zj[r][1];
if(x+nx<1||x+nx>n||y+ny<1||y+ny>m) {f[r]=1;continue;}//return ;
if(dt[x+zx][y+zy]!=WALL||v[x+zx][y+zy]||v[x+nx][y+ny]) {f[r]=1;continue;}//return ;
f[r]=1;
dt[x+zx][y+zy]=KONG;
v[x+zx][y+zy]=v[x+nx][y+ny]=1;
dfs(x+nx,y+ny);
}
return ;
}
#include
using namespace std;
//迷宫大小(必须奇数)
#define m 11
#define n 11
//构成
#define player '!'
#define wall '#'
#define kong ' '
//起点(必须偶数且在迷宫边缘)
#define X 2
#define Y 2
//迷宫内
#define WALL -1
#define KONG 2
struct play_er{int x,y;}start;
int dt[1010][1010];
int fx[5][5]={{2,0},{-2,0},{0,2},{0,-2}};
int zj[5][5]={{1,0},{-1,0},{0,1},{0,-1}};
bool v[1010][1010]={0};
void init()
{
start.x=X,start.y=Y;
memset(dt,WALL,sizeof dt);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==1||i==n||j==1||j==m) dt[i][j]=WALL;
if(i%2==0&&j%2==0) dt[i][j]=KONG;
else dt[i][j]=WALL;
}
}
dt[X][Y]=KONG;
}
void dfs(int x,int y)
{
bool f[5]={0};
while(1)
{
if(f[0]&&f[1]&&f[2]&&f[3]) return ;
int r=rand()%4;
int nx=fx[r][0],ny=fx[r][1],zx=zj[r][0],zy=zj[r][1];
if(x+nx<1||x+nx>n||y+ny<1||y+ny>m) {f[r]=1;continue;}//return ;
if(dt[x+zx][y+zy]!=WALL||v[x+zx][y+zy]||v[x+nx][y+ny]) {f[r]=1;continue;}//return ;
f[r]=1;
dt[x+zx][y+zy]=KONG;
v[x+zx][y+zy]=v[x+nx][y+ny]=1;
dfs(x+nx,y+ny);
}
return ;
}
int main()
{
init();
srand(time(NULL));
dfs(X,Y);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==start.x&&j==start.y) printf("%c",player);
else if(dt[i][j]==KONG) printf("%c",kong);
else printf("%c",wall);
}
printf("\n");
}
return 0;
}
- 创建所有墙的列表(除了四边),并且创建所有单元的集合,每个集合中只包含一个单元。
- 随机从墙的列表中选取一个,取该墙两边分隔的两个单元
- 两个单元属于不同的集合,则将去除当前的墙,把分隔的两个单元连同当前墙三个点作为一个单元集合;并将当前选中的墙移出列表
- 如果属于同一个集合,则直接将当前选中的墙移出列表
- 不断重复第 2 步,直到所有墙都检测过
void init()
{
for(int i=1;i<=n*n;i++) fa[i]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int d=0;d<2;d++)
if(i+dx[d]>0&&j+dy[d]>0&&i+dx[d]<=n&&j+dy[d]<=n)v.push_back((qiang){i,j,i+dx[d],j+dy[d]});
}
#define Hash(a,b) (a-1)*n+b
int find(int f){return fa[f]==f?f:fa[f]=find(fa[f]);}
void merge(qiang zmx){fa[find(Hash(zmx.fx,zmx.fy))]=find(Hash(zmx.sx,zmx.sy));}
bool judge(qiang zmx){return find(Hash(zmx.fx,zmx.fy))==find(Hash(zmx.sx,zmx.sy));}
while(!v.empty())
{
int r=rand()%v.size();
if(!judge(v[r])) merge(v[r]),mp[v[r].fx][v[r].fy][v[r].sx][v[r].sy]=KONG;
v.erase(v.begin()+r);
}
void vec_to_int()
{
int idx=1,jdx=0;
for(int i=1;i<=2*n+1;i++) dt[1][i]=WALL;
for(int i=1;i<=n;i++)
{
dt[i+idx][1]=WALL;
jdx=1;
for(int j=1;j<=n+1;j++) dt[i+idx][j+jdx]=KONG,((j!=n)&&(mp[i][j][i][j+1]==KONG))?dt[i+idx][j+(++jdx)]=KONG:dt[i+idx][j+(++jdx)]=WALL;
idx++,jdx=1;
for(int j=1;j<=n+1;j++) dt[i+idx][j+jdx]=WALL,((i!=n)&&(mp[i][j][i+1][j]==KONG))?dt[i+idx][j+(++jdx)]=KONG:dt[i+idx][j+(++jdx)]=WALL;
dt[2*n+1][2*n+1]=WALL;
}
dt[X][Y]=Player;
}
#include
#define Hash(a,b) (a-1)*n+b
using namespace std;
//起点,可自己设,为偶数
#define X 2
#define Y 2
//迷宫大小
#define n 124//生成2*n+1的矩阵 2~126
//不要动m
#define m 125//数组大小,不要改
//迷宫内
#define WALL -1
#define KONG 2
#define Player 11
//样式:
#define wall '#'
#define kong ' '
#define player '!'
int fa[m*m*m*m+100],mp[m][m][m][m],dt[m*2+2][m*2+2];
int dx[2]={0,1},dy[2]={1,0};
struct qiang{int fx,fy,sx,sy;};
vector v;
int find(int f){return fa[f]==f?f:fa[f]=find(fa[f]);}
void merge(qiang zmx){fa[find(Hash(zmx.fx,zmx.fy))]=find(Hash(zmx.sx,zmx.sy));}
bool judge(qiang zmx){return find(Hash(zmx.fx,zmx.fy))==find(Hash(zmx.sx,zmx.sy));}
void init()
{
for(int i=1;i<=n*n;i++) fa[i]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i+1<=n) v.push_back((qiang){i,j,i+1,j});
if(j+1<=n) v.push_back((qiang){i,j,i,j+1});
}
}
void vec_to_int()
{
int idx=1,jdx=0;
for(int i=1;i<=2*n+1;i++) dt[1][i]=WALL;
for(int i=1;i<=n;i++)
{
dt[i+idx][1]=WALL;
jdx=1;
for(int j=1;j<=n+1;j++) dt[i+idx][j+jdx]=KONG,((j!=n)&&(mp[i][j][i][j+1]==KONG))?dt[i+idx][j+(++jdx)]=KONG:dt[i+idx][j+(++jdx)]=WALL;
idx++,jdx=1;
for(int j=1;j<=n+1;j++) dt[i+idx][j+jdx]=WALL,((i!=n)&&(mp[i][j][i+1][j]==KONG))?dt[i+idx][j+(++jdx)]=KONG:dt[i+idx][j+(++jdx)]=WALL;
dt[2*n+1][2*n+1]=WALL;
}
dt[X][Y]=Player;
}
int main()
{
srand((unsigned int)time(NULL));
init();
while(!v.empty())
{
int r=rand()%v.size();
if(!judge(v[r])) merge(v[r]),mp[v[r].fx][v[r].fy][v[r].sx][v[r].sy]=KONG;
v.erase(v.begin()+r);
}
vec_to_int();
for(int i=1;i<=2*n+1;i++)
{
for(int j=1;j<=2*n+2;j++)
{
if(dt[i][j]==WALL) cout<
发现之前注释有欠缺,升个级
#include
using namespace std;
//地图基操
int m,n;//大小
char player='!',wall='#';//内饰
int x,y;//起点
#define kong ' ' //路
#define down 1 //向下
#define right 2 //向右
#define left 4 //向左
#define up 8 //向下
#define WALL -1 //墙
#define KONG 2 //路
#define Player 11//van家
#define Hash(a,b) (a-1)*n+b//二维bcj基本操作
//结构体
struct qiang{int x,y,f;qiang(int xx,int yy,int ff){x=xx,y=yy,f=ff;}};//墙
struct play_er{int x,y;}start,end;//角色
struct obstacle{int fx,fy,sx,sy;}zmx;
//变量
vector where;//bfs记录
vector b;//并查集记录
int x_p,y_p;//起点
int dt[1010][1010];//地图
int fx[5][5]={{2,0},{-2,0},{0,2},{0,-2}};//间隔一面墙的方向
int zj[5][5]={{1,0},{-1,0},{0,1},{0,-1}};//不间隔的方向
bool v[1010][1010]={0};//dfs记录是否走过
int fa[244140725],mp[125][125][125][125];//并查集的集合
int dx[2]={0,1},dy[2]={1,0};//并差集的方向
//bfs函数
void put_wall();//bfs入vector
void b_init();//bfs初始化
void bfs();//生成
void bfs_print();//打印
//dfs函数
void d_init();//dfs初始化
void dfs(int,int);//深搜生成
void dfs_print();//输出
//bcj函数
int find(int);//并查集 查找+路径压缩
void merge(obstacle);//合并
bool judge(obstacle);//判断是否在一起
void bcj_init();//初始化
void vec_to_int();//把vector与mp里面的值转移到 dt里面,方便后期操作
void bcj_inint();//生成
void bcj_print();//输出
//BFS
void put_wall()//bfs入vector
{
if(x_p+1<=m&&dt[x_p+1][y_p]==WALL) where.push_back(qiang(x_p+1,y_p,down));//下墙
if(y_p+1<=n&&dt[x_p][y_p+1]==WALL) where.push_back(qiang(x_p,y_p+1,right));//右墙
if(x_p-1>=1&&dt[x_p-1][y_p]==WALL) where.push_back(qiang(x_p-1,y_p,up));//上墙
if(y_p-1>=1&&dt[x_p][y_p-1]==WALL) where.push_back(qiang(x_p,y_p-1,left));//左墙
}
void b_init()//bfs初始化
{
srand(time(NULL));//随机数种子
x_p=x,y_p=y;//记录起点
start.x=x,start.y=y;//记录起点
memset(dt,WALL,sizeof(dt));//地图初始化
dt[x][y]=KONG;//起点为空
put_wall();//起点初始化
//bfs生成的基本思想就是从一个空开始,四周入队
//再随机选择队中的一个,重复上一次操作
}
void bfs()
{
while(where.size())
{
int r=rand()%(where.size());//随机选择
qiang what=where[r];//取出
x_p=what.x;//维护
y_p=what.y;//维护
switch(what.f)//走一步
{
case down:{x_p++;break;}//下
case right:{y_p++;break;}//右
case left:{y_p--;break;}//左
case up:{x_p--;break;}//上
}
if(dt[x_p][y_p]==WALL) dt[what.x][what.y]=dt[x_p][y_p]=KONG,put_wall();//入队
where.erase(where.begin()+r);//删掉
}
}
void bfs_print()//打印
{
//bfs会有原点,是从原点 洪水式填充,所以建议 中等难度
for(int i=0;i<=m+1;i++)
{
for(int j=0;j<=n+1;j++)
{
if(i==start.x&&j==start.y) printf("%c",player);
else if(dt[i][j]==KONG) printf("%c",kong);
else printf("%c",wall);
}
printf("\n");
}
}
//DFS
void d_init()//dfs初始化
{
srand(time(NULL));//老规矩
start.x=x,start.y=y;//初始化
memset(dt,WALL,sizeof dt);//同上
for(int i=1;i<=n;i++)//可以去看一看我配的图片,就能懂了
{
for(int j=1;j<=m;j++)
{
if(i==1||i==n||j==1||j==m) dt[i][j]=WALL;
if(i%2==0&&j%2==0) dt[i][j]=KONG;
else dt[i][j]=WALL;
}
}
dt[x][y]=KONG;//起点开始
}
void dfs(int x,int y)
{
bool f[5]={0};//当时,本蒟蒻把这一排放进了while内部,然后因为无法跳出循环调了一个小时······
while(1)
{
if(f[0]&&f[1]&&f[2]&&f[3]) return ;//四个方向搜索完了
int r=rand()%4;
int nx=fx[r][0],ny=fx[r][1],zx=zj[r][0],zy=zj[r][1];
if(x+nx<1||x+nx>n||y+ny<1||y+ny>m) {f[r]=1;continue;}//当时,我把continue写成了return 0···
if(dt[x+zx][y+zy]!=WALL||v[x+zx][y+zy]||v[x+nx][y+ny]) {f[r]=1;continue;}
f[r]=1;//这个方向走了
dt[x+zx][y+zy]=KONG;//打通
v[x+zx][y+zy]=v[x+nx][y+ny]=1;//标记,注意,不更新x+nx,y+ny会有回路
dfs(x+nx,y+ny);//继续深搜
}
return ;
}
void dfs_print()//输出
{
//因为dfs是一条路走到底,所以主路会很明显,副路的主路也很明显,建议作为简单难度食用
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(i==start.x&&j==start.y) printf("%c",player);
else if(dt[i][j]==KONG) printf("%c",kong);
else printf("%c",wall);
}
printf("\n");
}
}
//BCJ
int find(int f){return fa[f]==f?f:fa[f]=find(fa[f]);}//并查集 查找+路径压缩
void merge(obstacle zmx){fa[find(Hash(zmx.fx,zmx.fy))]=find(Hash(zmx.sx,zmx.sy));}//合并
bool judge(obstacle zmx){return find(Hash(zmx.fx,zmx.fy))==find(Hash(zmx.sx,zmx.sy));}//判断是否在一起
void bcj_init()//初始化
{
srand((unsigned int)time(NULL));//随机数种子
for(int i=1;i<=n*n;i++) fa[i]=i;//自定义
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i+1<=n) b.push_back((obstacle){i,j,i+1,j});//连接
if(j+1<=n) b.push_back((obstacle){i,j,i,j+1});//连接
}
}
void vec_to_int()//把vector与mp里面的值转移到 dt里面,方便后期操作
{
int idx=1,jdx=0;
for(int i=1;i<=2*n+1;i++) dt[1][i]=WALL;
for(int i=1;i<=n;i++)
{
dt[i+idx][1]=WALL;
jdx=1;
for(int j=1;j<=n+1;j++) dt[i+idx][j+jdx]=KONG,((j!=n)&&(mp[i][j][i][j+1]==KONG))?dt[i+idx][j+(++jdx)]=KONG:dt[i+idx][j+(++jdx)]=WALL;
//连接
idx++,jdx=1;
for(int j=1;j<=n+1;j++) dt[i+idx][j+jdx]=WALL,((i!=n)&&(mp[i][j][i+1][j]==KONG))?dt[i+idx][j+(++jdx)]=KONG:dt[i+idx][j+(++jdx)]=WALL;
//连接
//当时少打+1,调错一上午
dt[2*n+1][2*n+1]=WALL;
}
dt[x][y]=Player;
}
void bcj_inint()//生成
{
while(!b.empty())
{
int r=rand()%b.size();
if(!judge(b[r])) merge(b[r]),mp[b[r].fx][b[r].fy][b[r].sx][b[r].sy]=KONG;//合并
b.erase(b.begin()+r);//出去!
}
}
void bcj_print()
{
for(int i=1;i<=2*n+1;i++)
{
for(int j=1;j<=2*n+2;j++)
{
if(dt[i][j]==WALL) cout<
1.可以把它变成头文件,把main里面的移植到一个函数里。
2.原文代码部分为本蒟蒻手打,其余均经过作者优化,所以,转载请附上原网址
3.建议只更改起点,大小,样式,其余的尽量不要动
4.地图均存在 dt[][] 里,调用 dt[][] 输出即可
5.注意!并查集生成的地图是!!!
BFS生成 | DFS生成 | 并查集生成 | |
时间 |
123:中 299:优 567:差 |
123:优(真的) 299:差 567:优 |
123:差 299:/ 567:/ |
空间 | 中 |
优 |
差 |
码量 | 中 | 少 | 多 |
主路(一 般) | 不明显 | 明显 | 不明显 |
岔路(一般) |
多 | 少 | 多 |
地图大小设置 | 0~1009 | 0~567 | 0~124 |
I/O reads | 55:中 123:优 299:差 |
55:差 123:差(特差) 299:优 |
55:优 123:中 299:/ |
时间、空间、I/Oreads判断 c++ build benchmarks (生成同样大小迷宫下)
(冒号前的数据表示测试的地图大小)
问题:有时候n,m给大了就会报
ailed to execute "C:\Users\Administrator\Desktop\未命名4.exe":Error 0: 操作成功完成。
请问要怎么解决QwQ
其余均为本蒟蒻的主观判定
这篇文章耗费了我很多时间。
一些代码的bug改得人都费了
能不能一键三连呀!!!( QWQ )
参考文章:
房间和迷宫:一个地牢生成算法 | indienova 独立游戏
【UE4】迷宫生成——DFS、Prim、Kruskal算法实现 - 砥才人 - 博客园 (cnblogs.com)
上一篇:c++游戏小技巧5:隐藏光标+移除快速编辑模式+按钮模拟完整版_L('ω')┘脏脏包└('ω')」的博客-CSDN博客
下一篇:c++游戏小技巧7:system 综合_L('ω')┘脏脏包└('ω')」的博客-CSDN博客