目录
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.注意事项
5.各方法对比(就我而言)
4.后记
1.前言
(阅读本文前,请先保证已阅读前5篇!!!!)
2.前文(rand,随机数引入)
(引入正题:)
先引入一位朋友的代码ZQK13的博客-CSDN博客
当你运行后,你会发现:
你眼前有一个不像MG的MG
怎么说呢:不好说吧,一个可行又不可行的办法
所以我简单更新了一篇······
1.rand()
为了突出随机,我们会使用一个伪随机数
它位于:
#include<stdlib.h>
这个头文件中(当然,还有很多)
但伪随机就是伪随机,你可以多次重复运行下面一段代码:
#include<iostream>
using namespace std;
int main()
{
printf("%d\n",rand());
return 0;
}
发现没有:果然是伪随机
那就要讲到第二个好东西了
2.srand,随机数种子
srand() 随机数种子(与MC中的种子一个意思(?))需要的头文件见下:
#include<stdlib.h>
#include<ctime>
using namespace std;
int main()
{
srand(time(NULL));
return 0;
}
我们以当前时间为种子,就能保证伪随机不会那么伪了(你家时间一去还能反?)
对了,强调一个重点:在windows系统中,rand()能生成的随机数只能在0~32767之间
所以:简单的用法就是
3.用法:
1. 生成0~RAND_MAX里面的数
#include<stdlib.h>
#include<ctime>
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;
}
2.生成大于RAND_MAX的数
#include<stdlib.h>
#include<ctime>
using namespace std;
int main()
{
srand(time(NULL));
int a=rand();//32768进制数的高位
int b=rand();//32768进制数的低位
int c=a*32768+b;//计算出这个数
//建议函数封装
}
3.生成负数
(凑个数)
#include<stdlib.h>
#include<ctime>
using namespace std;
int main()
{
srand(time(NULL));
int a=-rand();
}
3.正文(确信)
随机迷宫有很多种写法。
推荐两种
1.bfs写法:
1.让迷宫全是墙.
2.选起点作为迷宫的通路,然后把它的邻墙放入列表
3.当列表里还有墙时
①.从列表里随机选一个墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
(a)那就从列表里移除这面墙,让未访问的单元格成为迷宫的通路
(b)把这个格子的墙加入列表
②.如果墙两面的单元格都已经被访问过,那就从列表里移除这面墙
1.让迷宫全是墙+确定起点
void init()
{
x_p=X,y_p=Y;
start.x=X,start.y=Y;
memset(dt,WALL,sizeof(dt));
dt[X][Y]=KONG;
}
2.初始化
//迷宫大小
#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)为起点
3.循环操作:
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);
}
4.总代码:
#include<bits/stdc++.h>
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<qiang> 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;
}
2.DFS写法
思路差不多
- 初始化大地图,只有0和1的状态。其中,0和1分别代表道路和墙体,注意四周皆墙
- 靠近边缘随机选取状态为1的道路点,作为起点 a
- 在起点 a 的上下左右四个方向,跨两个寻找同样为1的道路点 c
- 如果找到,则将它们之间的墙体 b 打通,然后将 c 作为新的起点,然后继续进行第2步
- 如果四个方向都没有找到,则不断回退到上一点,直到找到有一点其他方向满足条件,然后继续查询
- 当查无可查的时候,迷宫也就填充完毕了
1.初始化
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;
}
2.DFS
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 ;
}
3.总代码
#include<bits/stdc++.h>
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;
}
3.并查集写法
- 创建所有墙的列表(除了四边),并且创建所有单元的集合,每个集合中只包含一个单元。
- 随机从墙的列表中选取一个,取该墙两边分隔的两个单元
- 两个单元属于不同的集合,则将去除当前的墙,把分隔的两个单元连同当前墙三个点作为一个单元集合;并将当前选中的墙移出列表
- 如果属于同一个集合,则直接将当前选中的墙移出列表
- 不断重复第 2 步,直到所有墙都检测过
1.创建列表
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]});
}
2.并查集基操
#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));}
3.制造迷宫
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);
}
4.(为了方便操作) 记录迷宫
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;
}
5.总代码
#include<bits/stdc++.h>
#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<qiang> 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<<wall;
if(dt[i][j]==KONG) cout<<kong;
if(dt[i][j]==Player) cout<<player;
}
cout<<endl;
}
return 0;
}
4.注意事项
1.可以把它变成头文件,把main里面的移植到一个函数里。
2.原文代码部分为本蒟蒻手打,其余均经过作者优化,所以,转载请附上原网址
3.建议只更改起点,大小,样式,其余的尽量不要动
4.地图均存在 dt[][] 里,调用 dt[][] 输出即可
5.注意!并查集生成的地图是!!!
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 (生成同样大小迷宫下)
(冒号前的数据表示测试的地图大小)
(至于以上的吗······想都不用想,DFS绝对拉,BFS绝对快)
问题:有时候n,m给大了就会报
ailed to execute "C:\Users\Administrator\Desktop\未命名4.exe":Error 0: 操作成功完成。
请问要怎么解决QwQ
其余均为本蒟蒻的主观判定
4.后记
这篇文章耗费了我很多时间。
一些代码的bug改得人都费了
能不能一键三连呀!!!( QWQ )
参考文章:
房间和迷宫:一个地牢生成算法 | indienova 独立游戏
【UE4】迷宫生成——DFS、Prim、Kruskal算法实现 - 砥才人 - 博客园 (cnblogs.com)
上一篇:
下一篇:未完待续······