AI 贪吃蛇

之前在qq空间、朋友圈和微博装逼,现在要来实现了。
原因是因为一张很早的贪吃蛇吃遍全图的gif,很是华丽,然后当时就感觉这应该是程序实现的,怎么说呢,虽然是数学专业的高材生(自封的),但当时才大二,对一些最优化算法并不是特别了解,而且编程能力还很渣,所以当初也没有还原这个东西的想法。

现在偶然间又看到这张图,正好手头的工作都干完了,所以有了尝试实现的想法。因为工作用的Java么,为了省事,就直接选择了热咖啡(听说最近麦当劳改名叫金拱门0.0),正好以前也研究过swing。出于时间原因(加班狗伤不起),而且这次主要关注的是算法,游戏框架就没有自己写,直接使用了某Java实现的贪吃蛇游戏做了修改,因为这些代码是学生时代不知从哪扒的,所以原作者已经不可考了,但是还要感谢原作者的贡献。

贪吃蛇AI这个东西,在我之前应该已经有很多人搞过。感觉原图就是个大神,至今我仍然没有发现他是使用什么方式实现的。在网上找到过几篇有关的帖子,某个同学(可能是是很多同学)用BFS写了一个,参见http://t.cn/RSdtCdF。在我看来BFS是个不错的策略,然而跑了一下代码发现这种方式并跑不完全图,但是可以让蛇多撑一会。。。还有某位同志用py写了一个,然而py我对他的库了解不深,没看懂,只是扒下来跑了一下,发现这种算法在小概率条件下能跑完全图,但是更多的情况是蛇在绕着一条路线兜圈子。

一开始我的想法是把问题抽象成无向的最短路问题,印象比较深刻的有dijkstra算法和floyd算法,然而实现之后,就发现了问题,在寻路中,要计算蛇头与食物的最短路径,并且要躲避蛇身与墙壁,所以我定义蛇身与墙壁的距离为无穷大。使用简单的最短路计算时(这应该是一个贪心法),只会考虑当前蛇头距食物的最短距离,而当蛇吃掉食物时,往往会让自己陷入某些不好的情景: 
1.蛇身被困在某个与食物(下一个食物)不连通的一个封闭区域里,因为周围都是无穷大,所以蛇找不到食物的位置,不知道往哪走了,这种情况就只能GG了.
2蛇能找到食物,但是吃完实物后会陷入死路无处可走,这种情况下有经验的玩家一般会选择绕一绕的思路给蛇腾开位置,但是‘贪心’的蛇不会考虑下一步的路径,于是也GG了。

所以,实践证明只用最短路是不行的,以为随着蛇的移动地图的形势在变化,受到上面同学的启发,可以发现蛇追着尾巴走可以保证是安全的,因为蛇尾随着移动总会留出空间。

接下来设计算法,因为形势比较复杂,所以每次只让蛇走一步。这里做一个简单的判断。最开始我们先用最短路让程序跑起来,蛇按贪心法走的思路如下
if(没死){
最短路向是食物移动一步
}else{GG}
当然,我们知道这样一定会GG,然后,根据我们之前的思路,每走一步都算下蛇头到食物的路径,有的话就去吃食物,没有的话就去追蛇尾。但是有些特殊情况蛇头到蛇尾和食物都找不到路径,这里就要参考一下经典的游戏逻辑,采用wander的策略,就是让蛇头在可行范围内随机移动,再去尝试寻找路径。
if(蛇头到食物有路径){
	if(蛇头到达食物后能找到走到蛇尾的路径){
	蛇去吃食物
	}else{
	跟着蛇尾走
	}
}else{
沿着尾巴走
}

     然后这样又出现了另一种情况,到达一定长度后,蛇头貌似会一直追蛇尾,从而会来回循环的转,这样虽然没有GG,那也就一直死循环了。到这里脑子就有点不够用了。
这里其实还有一种取巧的办法,就是让蛇沿着一条始终可以遍历整个地图的路径来回运动,比如可以让蛇沿s形运动,然后一侧留下一条路逃生,吃完后再绕回来始终这样走,就可以保证始终不死还可以吃到食物,不过这样就没多大意思了。
后来,发现业界的寻路算法中,A*这种启发式算法用的比较广泛,然后简单了解了一下,(这里可以看一下前辈的东西A*算法),然后受到启发,蛇不一定总要走最短路,蛇在追蛇尾的时候可以走最远路,这样的话可以尽量把格子空出来,于是采用了
if(追食物){
走最短路
}
if(追蛇尾){
走最远路
}

 这里走最远路使用的A*,其实走最短路也可以用A*的,不过前边用了BFS和其他的几种,懒得写了。
 然后,我发现还有更坑爹的,(本来不想传图片的,因为麻烦0-0),看图发现,这样走会留下很多洞,一开始还没有多大问题,往后走就会发现这些洞会给蛇造成很大的困扰,中期会让蛇多走很多路,到后期就可能陷入很不利的环境。这里给句妈卖批,我都想去改食物出现的算法了。然而这样太没意思,还不如让蛇一直走s跑完全图。


      没有办法,还得去想办法,自己装的b含着泪也要装完。然后就要去想解决方案,要想不留下洞,走的时候就要尽可能多的绕,尽量把空白堆满,使用分支定界等方法给出了好几种策略,发现后期还是不可避免的出现了空格,这是就想到了既然s形可以走遍全图,那么走到后期时,我们不如就无脑s形绕,反而要比最短路还快一些(发现这里边还有一定的哲学道理,应了中国一句老话,欲速则不达,有时候找捷径还不如踏踏实实按部就班往前走)。
于是我们判断,当蛇身的格子数占到地图的一半后,我们就看做进入后期,这时开始绕。
 虽然还是没有大神的图潇洒,但总算能跑完。代码在github持续更新,有兴趣或有其他思路的朋友可以交流下。


欢迎评论和点赞,如果能去github给个star就更好了。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章