AI贪吃蛇(二)
前言
之前写过一篇关于贪吃蛇AI的博客,当时虽然取得了一些成果,但是也存在许多问题,所以最近又花了三天时间重新思考了一下。以下是之前博客存在的一些问题:
- 策略不对,只要存在找不到尾巴的情况就可能失败,所以这次的AI能保证始终找到尾巴。
- 编程思路不对,当时用C语言编写的,原有的游戏规则和AI部分有耦合,所以甚至出现了吃自己身体的状况,这是完全可以避免的。
以上两个问题是最主要的,其他地方也还是有可取之处的。下面是本次的成果。
思路
首先,我在网上找了一份贪吃蛇游戏的成品,然后在此代码的基础之上修改,添加一个AI,在每次游戏刷新时更新方向,这样,只要返回的方向错误,游戏一定会终止。同时,这种思路也比较符合实际:实际中我们玩贪吃蛇游戏其实就是通过键盘向游戏返回一个方向。现在不过是由AI负责返回方向。
AI算法
角度一:路径规划
这也是我本次采用的策略,如果看过上文提到的博客,就应该比较清楚,但是具体的策略有了变化,伪代码如下:
if (蛇身长度 > N && 策略1可行) {
执行策略1;
} else if (策略2可行) {
执行策略2;
} else if (策略3可行){
执行策略三;
}
具体解释一下:策略1
是非常无聊的做法,如下图,只要这样走一定会赢,但是显然没有任何价值,所以 N
越大,AI就越强。
策略2
是游戏早期会采用的一种策略,即利用A*算法 去寻找最短路径,如果按照路径去吃食物后还能找到尾巴就可行。但是也存在一个问题:有时候A*算法是行不通的但是稍微绕点路可能就行了,对于这种情况,我本想再添加一种策略,即遍历出所有路径,然后在策略1
不可行的情况下采用此策略,可是貌似不是目前的主要矛盾就放弃了(还有一部分原因是尝试了一下,没写出来。。。)
策略3
就是策略不可行的时候,采取的追尾策略,但是如何追,看到一些文章说朝离食物最远的方向,可是,我却遭遇了死循环:
最后,我采取的策略是选择合法的方向中,离尾巴最远(DFS距离)的那个方向。
可以看到,无论哪个策略,都是可控的,能找到尾巴的,这也是成功的关键。
角度二:博弈
这部分是我的一些思考,未实践探索。
其实,很多AI问题都可以用博弈的观点来解决,有些很明显:比如五子棋,围棋,有些则不那么明显:比如贪吃蛇。
如何用博弈的观点来看呢(描述可能不严谨):
- 博弈双方甲、乙
- 甲方选择一个空位置放置目标
- 乙方在不撞墙、不撞自身的前提下,在有限步骤内乙方需要吃掉食物。
- 如果乙方吃掉食物,身体长度加一(如何增加有多种方式,比如原代码是吃完增加,我更习惯吃的那一步发生增加)
- 如果甲没有位置放置目标则甲输,如果乙撞墙、撞自己、不能在有限步内吃到目标,任何一种情况发生都算输。
从经典贪吃蛇游戏的规则来看,甲采取的肯定不是最优策略,因为都知道食物是随机放置的,乙是否存在最优呢(这个我还不敢多做评论)
以上是我的一些想法,下图是一副几年前很火的图,图片是处理过的,用了2min,实际用时是13min,从花费的时间来看,我觉得如果这是AI(这个GIF是如何做出来的目前还不清楚,并不一定是AI),那一定得是搜索了博弈树,路径规划花13min是不太可能的:
代码概述
AI我是用JavaScript写的,才看了《JavaScript权威指南》前几章,写的很烂(这也是最后的AI中有一些“不可表述”的奇怪bug的原因),没必要细细分析,说一下大体框架:
aiSetDirection: AI入口
aiSetSituation: 将目标位置等信息存储起来,方便AI使用
aiGetPrePath: 策略1,返回第一种策略可行时的方向
aiGetBestPath: 策略2,使用A*算法返回可行时方向
aiFollowTail: 策略3,追着尾巴跑
aiJudgePath: 判断路径是否可行,策略2传入数组,策略三传入的数组实际只有一个元素
aiDFSPath: 返回蛇头朝四个方向移动(策略3中)时尾巴的DFS深度
aiTurnToDirection:将路径(下一步)转换为方向
贪吃蛇游戏是否存在最优解
目前,我还不能做最终定论,原因有以下几点:
- 目前自己掌握的资料有限,是否有同类问题的学术研究,又或者在哪已经彻底解决了这个问题(目前的倾向是不能保证100%取胜)。所以我觉得自己还需要寻找更多的资料,比如相关的AI比赛也是有的,我目前还没有了解。
- 目前无论用哪种语言,我都没法随心所欲的驾驭,需要用到的几个关键算法及其变种同样也没法熟练的使用。
- 形式化的研究还没有开始,至少目前我想,对于这个问题,我想的还不够抽象。
源代码和在线演示
源代码托管在GitHub上了,欢迎star&fork
在线演示(不能保证每次成功):AI-Snake
视频:优酷(标清);youtube(高清)
总结
花了三天吧,敲了很多垃圾代码。算法不熟练,代码不熟练。事先规划不充分。
可改进之处:
- 可以在早期多按照预定路径走几次,这样可以有效消除空位置。即
身长>N
改为身长>N || (身长>n1 && 身长<N1) || ...
可以视为一种调节机制 - 预定的路径有多种,有时候上文提到的路径可能不行(因为到达N时蛇的形态可能很特殊),所以可以多设置几种
无聊必胜
策略,防止某一策略不可行
参考
- 关于寻路算法的一些思考(1):A*算法介绍,这是第一篇,这个系列是我读的中文里最好的A*算法讲解
- Creating A Snake Game Tutorial With HTML5,我的代码就是在此基础之上建立的,一个很不错的网站,应该是个人网站
- Winning A Snake Game,GIF的视频版
- Snake - Maximum score (tool-assisted),另一个获胜的视频,没上一个那么吓人
- Snake finally beaten,关于那张经典GIF的一则新闻
- The Perfect Snake,同样是关于GIF的一篇短博文
- Смотрим до конца,GIF的出处
- Snake Online,一个关于贪吃蛇游戏的网站
- 如何用Python写一个贪吃蛇AI,国内比较火的AI贪吃蛇博文,博主的项目star数蛮高的
- 贪吃蛇 AI 的实现 snake AI,国内另一篇AI贪吃蛇的博文,比较好的就这两篇了。
- Snake,入门级讲解。
- js实现A*寻路算法,对我一个js菜鸟帮助还是很大的