迷宫生成 - 递归除法(它是如何工作的?)
Posted
技术标签:
【中文标题】迷宫生成 - 递归除法(它是如何工作的?)【英文标题】:Maze generation - recursive division (how it works?) 【发布时间】:2017-01-09 17:41:02 【问题描述】:我想学习如何生成一个完美的迷宫。我试图理解递归除法算法。我不明白如何实现这个算法。这是我的代码:
bool maze[20][20];
void initMaze()
for(int i = 0; i < 20; i++)
for(int j = 0; j < 20; j++)
maze[i][j] = false;
for(int i = 0; i < 20; i++)
maze[0][i] = true;
for(int i = 0; i < 20; i++)
maze[19][i] = true;
for(int i = 0; i < 20; i++)
maze[i][0] = true;
for(int i = 0; i < 20; i++)
maze[i][19] = true;
int randNum(int min, int max)
return rand()%(max-min+1)+min;
void drawVWall(int minY, int maxY, int x)
int passage = randNum(minY, maxY);
for(int i = minY; i <= maxY; i++)
if(i != passage)
maze[i][x] = true;
void drawHWall(int minX, int maxX, int y)
int passage = randNum(minX, maxX);
for(int i = minX; i <= maxX; i++)
if(i != passage)
maze[y][i] = true;
void divison(int x, int y, int maxx, int maxy, int h)
if(h)
if(maxx <= 2)
return;
int wallY = randNum(y, maxy);
drawHWall(x, maxx, wallY);
if(wallY < maxy - wallY)
divison(x, wallY, maxx, maxy, !h);
else if(wallY > maxy - wallY)
divison(x, y, maxx, wallY, !h);
else
if(maxy <= 2)
return;
int wallX = randNum(x, maxx);
drawVWall(y, maxy, wallX);
if(wallX < maxx - wallX)
divison(wallX, y, maxx, maxy, !h);
else if(wallX > maxx - wallX)
divison(x, y, wallX, maxy, !h);
initMaze();
divison(2, 2, 18, 18, true);
这段代码有问题,因为它经常冻结程序(某处的无限循环),但如果它有效,它会生成一个像这样的“迷宫”: Generated 'maze'
我不想问你我的代码。我只是想了解如何实现这个算法,我哪里错了,我应该怎么做。我需要生成一个没有封闭区域的完美迷宫。
【问题讨论】:
递归似乎难倒很多人。一个常见的聪明的说法是:“一个人必须理解递归才能理解递归。”您可以从这个项目中退后一步,尝试使用更简单的问题来理解递归。这是 Wikipedia 的链接以开始使用。 en.wikipedia.org/wiki/Recursion. 查看这个可爱的debug 博客寻求帮助。从您到目前为止的帖子来看,您似乎没有做太多调试程序。您没有描述您对 RDA 的理解不足的地方,或者您的程序与您所理解的不匹配的地方。简而言之,“我不明白”和“它只是给了我这个 blob”不足以描述问题。 请注意,递归除法会产生丑陋的迷宫,因为长长的顶层墙很突出。见:weblog.jamisbuck.org/2011/1/12/… 非常感谢您的回答。 【参考方案1】:首先,您必须决定如何设计迷宫。您使用整个单元格来表示墙壁或通道。这意味着您对选择作为新墙壁和通道的数字有一些限制。不应创建示例中的厚墙,即两个垂直墙相邻且它们之间没有通道。
使用您的方法,N
× N
迷宫由 (2*N + 1)
× (2*N + 1)
的数组表示。你有“迷宫”坐标 0 到 N
和“网格”坐标 0 到 2*N + 1
。在创建墙壁时在迷宫坐标中执行算法并使用网格坐标。
在网格坐标中,墙壁必须在偶数索引处,而走廊必须在奇数索引处。两个索引偶数始终是最终迷宫中的一堵墙,两个索引奇数始终是一个空闲单元。一偶一奇表示有可能穿墙。
为了说明,这里是一个 4×4 的迷宫:
X 0 1 2 3
x 0 1 2 3 4 5 6 7 8
# # # # # # # # # 0
# + + + # 1 0
# + # + # + # # # 2
# + + + # 3 1
# + # + # + # # # 4
# + + + # 5 2
# + # + # + # # # 6
# + + + # 7 3
# # # # # # # # # 8
y Y
小写字母(x
,y
)是网格坐标;大写字母(X
、Y
)是迷宫坐标。井号 #
始终是墙壁,但加号 +
最终要么是通道,要么是墙壁。
第二个问题是递归本身。当您建造了一堵墙时,您已将活动空间细分为两个区域。您现在必须将算法应用于两个新区域。
所以而不是:
if (wallY < maxy - wallY)
divison(x, wallY, maxx, maxy, !h);
else if (wallY > maxy - wallY)
divison(x, y, maxx, wallY, !h);
你应该这样做:
divison(x, wallY, maxx, maxy, !h);
divison(x, y, maxx, wallY, !h);
当然你必须有一个条件来停止你的递归,但你已经有了这个条件:当空间太小时,你不会再建造更多的墙。
您示例中的大房间表明您只递归到迷宫的一个新子部分。大房间是您没有递归到的房间。
【讨论】:
我明白了。谢谢你的解释,我会努力解决的。以上是关于迷宫生成 - 递归除法(它是如何工作的?)的主要内容,如果未能解决你的问题,请参考以下文章