- 1、本文档共7页,可阅读全部内容。
- 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 5、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 6、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 7、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 8、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
推箱子推动路径搜索算法.pdf
推箱子游戏的一个箱子推动路径搜索算法
发表于 2012 年 05 月 14 日
Object 1
作者:杨超
地址:http://sokoban.ws/blog/?p=298
推箱子自 1981 年诞生至今,已经超过 30 年了。推箱子软件的功能也有了很大的改进。从刚开始的只能
用键盘一步一步的移动,到上世纪 90 年代后期 起,用电脑辅助搜索一个箱子的推动路径,从而实现用鼠
标两次点击(或是拖放)就能实现一个箱子的任意推动。可以说,电脑辅助路径搜索,已经成为推箱子
软件 的一个标准功能,使得人们从繁琐的逐步操作中解放出来,在更大一些的关卡中探寻更多的挑战和
乐趣。
一个箱子的推动路径搜索并不是一个很难的算法,用最基本的广度优先搜索算法 (Breadth-first
search) ,就可以很快地找到一个推动数最少(但此时移动步数不一定最少)的路径。我2002 年春写
《Final Sokoban》第一次实现了这个算法,2004 年写《M2 Sokoban》又实现了一次。这两次都是用
C 或 C 在 Windows 平台下写的。2010 年后写 Java Applet 《 SokoPlayer 》 ,用C 写 Linux 下的
《 USokoban 》 和用 Javascript 写《 SokoPlayer HTML5 》 ,都实现过这个算法。下面简单说一下这个
算法的要点。
首先,必须明确搜索的一个结点是什么。我们是考虑选中一个箱子后,不推其他箱子的前提下,把选中
的箱子推到一个目的地。于是,在推动这个箱子的过程 中,其他箱子可以视为墙体。又我们以推动一步
为基本的单位来搜索,不以移动一步来搜索。所以搜索的一个结点由两个要素确定:一是箱子的位置,
二是人相对于 箱子的方向。这是因为箱子可以把人隔在关卡中不同的区域。
以下面这个简单的关卡为例:
(图一)
可以点击下面链接玩这一关。
http://sokoban.ws/sokoplayer/?w=4h=9lvl=HHHH|H__H|H__H|H__H|H__H|HH$H|_HaH|
_H.H|_HHH
这一关的按广度优先搜索得到的树如下:
在上述搜索树中我们看到,结点 a 和结点 f 的箱子位置是一样的,但是由于人的位置不同,所以是两个
不同的结点。一般地,当前要推动的箱子最多把关卡隔为四个不同区域(即上下左右,在程序中可以用
0 ,1 ,2 ,3 或其他常数表示)。所以若关卡大小不超过 n*n ,则搜索总的结点不超过 4n*n 个。
广度优先搜索通常要用到一个先入先出(First-in first-out)的队列(Queue)来保存搜索过程中的结点。根据
前面的分析,每个结点只需保存箱子位置和人相对于箱子的位置。如,结点 a 可记为 [(C, 6) 下] ,其中
(C, 6)是图一中箱子标尺坐标,“下”表示人相对于箱子的位置。这样,一个结点在搜索队列中用 2 到 3 个
字节便可以保存。
其次,搜索过程中,结点可能重复出现,我们必须记住哪些结点前面已经访问过了,只把新的结点添加
到搜索队列中。比如上面例子中,结点 e 箱子有向上和向下推两种可能。向上推得到新结点 g ;向下推得
到的本质上是结点 c ,这个在前面已经出现,所以忽略不要。
幸好,我们已经分析过了,总结点不超过 4n*n 个,我们可以用一个 4n*n 个单元的数组来记录结点是否
已经访问过。初始时,数组全为 0 ,每遇到一个 新结点,它在数组上对应的位置改为 1 ,表示访问过了。
于是,搜索中,我们每推一步得到一个结点,只需查看该数组的相应位置是 0 还是 1 ,快速判断这个结点
是 否新结点。
比如,结点 c 我们可以记为[(C,4)下]。我们确认它是一个新结点加入队尾时,把[(C,4)下]对应的位置标
记为 1。注意,同时我们把 [(C,4)左]和[(C,4)上]也标记为 1 ,因为这三者本质上是同一个结点。在具体
的算法实现上,我们检查一下人能否从箱子下方在不推动任何箱子的前 提下,自由地移动到箱子的其他
方向。
有了以上的分析,我们可以把总的算法描述如下(假设关卡不超过 80 x 80 ):
(1)初始化准备 :建立搜索队列 q ,把初始结点入队。用数组 t[80][80][4] 来记录结点是否访问过,初始
化为全为 o ,然后把数组 t 中与初始结点对应的位置和与之等价的位置标记为 1。
(2) 主循环 :根据队列是否为空 ,分别执行操作(2.1)或(2.2)
(2.1) 若队列非空 ,让队头结点出队。从此结点出发
文档评论(0)