开场:
A*这个经典的最短路径搜索算法--很多人都用过它快速计算迷宫出路,也有人用来进行2d游戏的路径搜索。总之在搜索路径时大都第首先想到它。
简述A*最短路径算法的方法:
目标:从当前位置A到目标位置B找到一条最短的行走路径。
方法:从A点开始,遍历所有的可走路径,记录到一个结构中,记录内容为(位置点,最小步数)
当任何第二次走到一个点的时候,判断最小步骤是否小于记录的内容,如果是,则更新掉原最小步数,
一直到所有的路径点都不能继续都了为止,最终那个点被标注的最小步数既是最短路径,而反向找跟它相连的步数相继少一个值的点连起来就形成了最短路径,当多个点相同,则任意取一条即可。
总结:A*算法实际是个穷举算法,也与课本上教的最短路径算法类似。课本上教的是两头往中间走,也是所有路径都走一次,每一个点标注最短值。
进入正题:A*在俄罗斯方块游戏中的应用。
最早的版本中,我已经在Tetris游戏中加入了A*算法,它不计算方块变形操作的,是传统A*算法的一个简单变形。差别仅在于:原算法通常是仅占一个坐标点,现在是占几个坐标点。所有的思路和算法都是相同的。同时,我应用这个路径算法的目的是判断是否有有效路径,所以并没有存储步数也并没有求最短路径。
本文主要讨论的并非这个版本的A*变化,而是包含方块变化的A*路径搜索算法。
大家都知道,俄罗斯方块在移动过程中,经常需要变化一下形势才能移动到更好的位置,如果我们找到了一个最好位置,就必须找到一条可以达到这个位置的路径去实现它,如果路径找不到,目标位置再好也不可以。
本算法的思路归纳如下:
1、数据结构:
存储结构1(所有路径点的富存储):步骤目标点(x,y),目标样式hash值,移动次数;
存储结构2(快速查找是否重复的贫存储):前三个值再次做一次hash,但这个hash是绝对不能重复的那种运算,然后借助C#本身的数据结构Dictionary<hash,times>存储所有找到点。
描述:用结构2的目的是减少查找匹配速度,而由于hash本身可能是不可逆的,我在写思路的时候,必须标明使用两个结构存储
实际过程中我会将x,y,hash值用位操作同时存储于一个int中,实际仅用存储结构2。
2、算法搜索路径规则:
起始位置:如果刚开始游戏的时候,要下降10多行才需要变形即可,可以完全不需要计算前10多行的情况,也就是可以做一个开始路径优化,但A*算法的优势就是路径最短,起始位置并不用做这个优化,也不会带来太多次的计算。
可选位移:A*s算法默认可选方向通常是上下左右,根据需要可能加入斜着4个方向;
然而我们的位移必须加上变形位移.由于方块不能向上移动,所以初步分析只允许{左移,右移,下落一格,变形}几个操作.
高级分析,实际游戏中通常下降我们会使用快速下降和瞬间下降两个操作,而这两个操作会大大减少路径搜寻时间,如果在A*中加入这个步骤可能会降低总步骤,而且会让路径看起来更舒服.
分析后一种高级方案的弊端:由于瞬间下落的可行度与一步位移的可行度计算复杂度完全不一样,可能造成算法复杂度的膨胀.路径结构需要修改,路径点的回溯寻找算法也会出现变化,必须用一个图记住所有路径.综合考虑,不适用高级分析所提出的思路解决问题.
3、找到目标后路径确定(让搜索出来的路径更和谐):
除了路径最短以外,还应该注意下面两点
尽量少的拐点(别让观众以为电脑是个疯子);
横向位移尽量短(避免在高速下落的状态下,横向位移达不到目标就停止);
为达到上面两个额外目标,我们必须在所有可选路径中按照一定的规则进行查找。
我总结的规则是:尽量先横移到目标,然后再下降,同一层的横移尽量少于3个单位。
2.A*算法:主体,主体分两个步骤,前面都说过了
3.A*算法内部第一步片段:遍历所有路径
4.A*算法中调用的内部函数,用了一堆的ref,为了降低代码量才这么干的
5.A*算法中的第二步片段:
源代码思想上就是这样,但是实际代码可能会根据版本不同有一定调整,希望看到最新代码就下载下面的连接。
或者返回开场那个连接,那里总是最新的通过俄罗斯方块浅谈游戏中的AI(序)