源码及原理
最近用纯c++实现了了一个2048的附加ai版本。由于教育网github不稳定,所以源码用了oschina 的仓库
clone地址http://git.oschina.net/zhao02/game2048withAI.git
基本原理是minimax search。由http://stackoverflow.com/questions/22342854/what-is-the-optimal-algorithm-for-the-game-2048/22389702#22389702学习而来。
关于该原理的解释这儿写的很清楚,也不乏中文翻译。本文假设读者已经了解该基本算法。
本文只想说说不一样的部分——改进和进一步改进方案。
改进
我觉得当搜索深度高于5之后,AI算法有明显的停顿感。大致算了下,假设没一步移动都可以使屏幕上只剩一个方块,则搜索树最下层的节点数为120的n次方。明显需要剪枝
关于剪枝
原本的算法中除了α-β剪枝之外,在每一次α节点(也就是添加方块的节点处)进行了额外的剪枝。此外,将每次的AI计算时间做了限制。以此消除停顿感。
我做了两处修改:
1、额外的剪枝
基本思路是用简单的方法选出所有能生成的新方块中最讨厌的那个或者那几个,将其余的减掉。然后走正常的搜索流程。关键是简单的算法怎么实现。我的实现方法是遍历每个新生成的方块,分别计算其同一行/列上最先遇到的方块数值差,取2个数之差中小的那个做权值。找到权值最大的那个或那几个,就是我们要找的。
比如图中圈出来的2行方向最先遇到4,差值为2,列方向最先遇到2,差值为0,所以圈出的2的权值就是0.把所有空白格子取值2和4 的情况全部算完,就能找到最讨厌的那个(格子+数字)剪枝完成。
但是这样做有个问题:一开始空格很多,遍历有点慢!
所以有了第二个改进:
2、开始乱按n步
其实也不全是乱按。不知读者有没有发现,如果一开始轮流按 :下、右两个方向,基本都能合成128。所以一开始20到30步下右间隔着按就好,不用minimax搜索,这个随便按的步数试过50,发现也不算太大。
进一步改进方案
其实人的搜索步数是多少?我觉得是1,我们顶多想下按了某个方向之后,如果哪哪出现几,我们就能怎样。所以我把搜索深度设置为1 。根据结果观察,发现可以有这么两个改进
关于max函数
目前的max函数只关心最大的数字,以至于出现许多我认为应该合成的数字没有合成。所以我觉得对合成第二大甚至第三大的数字也应该有加成。max的返回变为largest+0.5*larger+0.25*large系数有待考证
主方向
什么叫主方向。我们玩着玩着就会发现,最大数字在的那一行或者一列我们尽量不想去动他。这时行或者列就成了主方向。
为什么要确定主方向呢?我尝试n此发现,我们只要关心主方向上是不是平滑,是不是单调等等,另外的那个方向数字大小似乎没有那么大的影响。
去掉if(win)的剪枝
原算法中有如果合成2048那么不继续搜索直接选择该方向的剪枝,我觉得这直接导致最后会走几步臭棋。以至于合成2048后就离死不远了。所以干脆去掉,或者,调大点。这会造成能合成2048的时候不去合成,但这又怎么样呢?谁说我们的目标是2048^ ^