这几天在做项目时遇到了瓶颈,到处去翻资料找寻解决灵感,在indienova翻到篇翻译文章讲地牢生成算法,这篇文章本身倒没太多让人耳目一新的东西(不过作者在文章里用的示例确实很赞,动态的展示算法实现的过程,非常的清晰),但里面提到了一个前辈—— Jamis Buck,顺藤摸瓜的就去看了他的博客(http://weblog.jamisbuck.org/),结果一着迷就花了一天工夫在上面,这位前辈对迷宫生成的研究确实深!而且还出了本书叫《Maze for programmers》,可惜国内并不能买到。
有人在评论里称他为“The King of Mazes”。
这位前辈在一篇文章里(点击 阅读原文 可跳转至此网址)总结了他这么多年来搜集总结的11种迷宫生成算法,虽然并没有全部都消化一遍,但也确实受益匪浅。
这里我就简单介绍下这里面我最喜欢的2种算法—— Growing Tree algorithm和Eller’s algorithm。在这里 具体算法实现我可能不会完全介绍,主要是介绍下算法的思路,因为我觉得算法最大的作用不是拿来就用,而是开阔思路,除非它刚好完全符合你现在的需要,你可以奉行下“拿来主义”。而且 有些算法需要自己去按照思路自己实现一遍才能更加感受到它的精妙。
这个算法其实很容易理解,就是从一个迷宫起点开始做一个类似随机深度优先搜索,直到无法前进为止。然后通过一个启发式算法从已经探索过的节点中,寻找一个结点再一次重复上一步,直到整个迷宫里所有的结点都被探索过为止。算法过程中需要一个数据结构用来存储所有可能做为起点的节点列表。
这个算法也是Jamis Buck自己目前最喜欢的算法。因为这个算法有一个很神奇的地方,就在于它可以通过调整启发算法变成另外2个算法( recursive backtracker & Prim’s algorithm )。简单说,如果每次都挑选最新加入的一个节点,那么这个算法就等同于 recursive backtracker, 而如果每次都去随机选取,它的实现就跟 Prim’s algorithm 非常类似。
令人惊艳的算法 —— Eller’s algorithm
这个算法简直令我吃惊!在看到这个算法之前,我一直都觉得做迷宫生成是一个图类遍历问题,但是这个算法的厉害之处就在于,它的迷宫是一行一行,一格一格生成的。它的实现过程,嗯,你可以想像一个老式打字机,在那一格一格把迷宫给你敲出来,仿佛迷宫已经生成好了,只是打印出来而已。所以,这个算法几乎是一个标准的O(m*n)算法, 也基本上是所有算法里最快的。
Jamis Buck表示这个算法是他遇到的最难以理解并且最疯狂的算法
这里我简单介绍下这个算法,算法前提认为每个房间都属于一个独立的Set。
取下一行房间,每2个属于不同Set的房间都有一定概率(通常为50%)打通,即将这2个房间合并入同一个Set。
检查这一行,如果是最后一行跳转至4。否则,每个房间均有一定概率(通常50%)向下打通,同时保证这一行的每一个Set至少有一个房间向下打通。
重复1。
将这一行所有不同Set的房间全部打通,迷宫生成完毕。
简单,高效,虽然我也自己实现了这个算法,但至今我还是没能Get到当初想出这个算法设计者的智慧,值得慢慢回味。
最后
以上说的算法都是用于生成“完美”迷宫,即所有的房间与房间之间只有一条通路,迷宫内是没有环形结构的。而其实这些算法只要你稍加改造,就都可以变成“不完美”迷宫的。
另外有一个有意思的是有个人提问Jamis Buck说有没有能够生成“一笔画”迷宫的算法。他的答案很有意思,只要你把所有通道对半切,把他们从“I”型变成“U”型,就够了,唔,这个智慧让我回味了半天。最后的最后,放张迷宫大神的照片吧,也感谢他的无私分享。
“The king of Maze”
一点补充
感谢 Ruibin.Chow的建议,我把之前写过的一些示例代码放到github上了,以后这里的代码示例也都会放上去,地址是 https://github.com/yestein/LuaSample。
PS:这个Eller’s algorithm的实现写的比较“匆忙”,等以后有工夫“润色”下再放上去吧。