我正在为一个基于文本的游戏编写AI,我遇到的问题是我如何确定如何确定墙的最薄部分 . 例如,以下代表2D Map ,其中'^'是想要通过'*'字符表示的墙到达标记为'X'的位置的字符:
------------------
| X * |
|***** |
|**** |
|*** |
|** ^ |
| |
| |
| |
------------------
我已经连续几天都在考虑这个问题而且我的想法已经用完了 . 我尝试过使用*算法,当它试图通过墙角时,g-cost非常高 . 不幸的是,算法决定永远不会在墙上寻找路径 .
代理只能左右上下而不是对角线,一次只能移动一个空格 .
上面例子中通过墙壁的最短路径是一条,因为它只需要通过一个'*'字符 .
我只需要几个简单的想法:)
4 回答
所有流行的图搜索算法通常用具有一些实数(即浮动/双重)成本的成本来制定 . 但这不是必需的 . 所有您真正需要的成本都是严格的订购和添加等操作 .
您可以对此应用标准A * .
定义表格
(a,b)
的成本a
是墙细胞上的移动次数b
是正常单元格上的移动次数 .定义这些单元格的排序如下:
[(a1,b1) < (a2,b2)] == [(a1<a2) || (a1==a2 && b1<b2)]
这只是一个字典排序,我们总是喜欢在墙壁上移动更少的移动,然后我们更喜欢在正常细胞上移动更少的移动
如下定义对这些成本的添加操作:
(a1,b1) + (a2,b2) == (a1+a2,b1+b2)
将启发式(即剩余目标距离的下限估计)定义为
(0,b)
,其中b
是距目标的曼哈顿距离一个直接的反对意见可能是“通过启发式,在试图穿过墙壁之前,必须探索墙外的整个空间!” - 但这正是要求的 .
根据信息和要求,您实际上 the optimal A *启发式 .
更复杂的方法可以提供更好的最佳案例性能,将上述内容与bidirectional search结合起来 . 如果您的目标位于一个很小的区域内,双向搜索可以在搜索的早期找到一些候选"cheapest paths through the wall" .
只需将其作为加权图,并给所有“墙壁”一个荒谬的大重量 .
小心不要溢出整数 .
假设任何数量的移动总是比通过墙壁便宜(意味着10000000000非穿墙移动比1穿墙移动便宜)(否则设置成本适当),我可以看到任何算法的特殊情况我可以想到这几乎不涉及搜索整个 Map ,所以......
从源头做一个详尽的A *,停在墙上,记录每个位置的最短路径 .
对于靠近墙壁的每个探索位置,穿过墙壁 . 然后将所有这些(如适用)的A *组合起来,再次停在墙壁上 .
对于靠近墙壁的这些新探索位置中的每一个,穿过墙壁并继续A * .
等等......直到我们找到目标 .
跳过已经探索过的位置 - 新路径应该总是比已经存在的路径长 .
你真正想要做A *而不是BFS的唯一原因是因为当你到达包含目标的区域时,这将允许更少的探索 . 这是否更有效取决于 Map .
正如Sint所提到的,如果开始时间总是在一个开阔的区域并且终点位于一个小区域内,那么反转这种搜索会更有效率 . 但这只有在您知道是否真的时才适用 . 检测它不太可能有效,一旦你有了,你就会失败,如果两者都在合理大小的区域 .
An example:
初始BFS:
穿过墙壁和BFS(没有BFS发生,因为他们无处可去,但通过墙壁):
(我们可以忽略的那些标有
%
)步入墙和BFS(BFS到目标):
使用Dijkstra .
因为你're dealing with a text-based game, I find it highly unlikely that you'谈论大于1000×1000字符的 Map . 这将以非常低的成本
O(n*logn)
为您提供最佳答案,并且代码非常简单直接 .基本上,每个搜索状态都必须跟踪两件事:到目前为止你走了多少墙,以及有多少常规的空白空间 . 通过假设例如每个墙具有要遍历的2 ^ 16的成本,可以将其编码为用于搜索和标记矩阵的单个整数 . 因此,Dijkstra的自然顺序将确保首先尝试具有最少墙壁的路径,并且在穿过墙壁之后,您不会重复已经到达的路径而不经过尽可能多的墙壁 .
基本上,假设一个32位整数,一个状态已经通过5个空格和3个墙,将看起来像这样:
0000000000000011 0000000000000101
. 如果你的 Map 真的很大,像迷宫一样,墙壁很多,墙壁很少,或者诸如此类的东西,你可以调整这种表示来为每个信息使用更多或更少的位,或者如果你觉得更舒服,甚至可以使用更长的整数,如如果存在需要遍历超过65,000个空白空间的最短路径,则此特定编码示例将为"overflow" .使用单个整数而不是两个整数(对于墙/空间)的主要优点是,您可以使用单个简单的
int mark[MAXN][MAXM];
矩阵来跟踪搜索 . 如果您在穿过5
墙时到达了特定的方格,则无需检查是否可以使用4个,3个或更少的墙到达它以防止无用状态的传播 - 此信息将自动嵌入到整数中,只要将walls
的数量存储到更高的位中,就不会在具有更高的"wall cost"时重复路径 .这是C中一个完全实现的算法,考虑它是伪代码,以便更好地可视化和理解上面提出的想法:)