我使用expectimax优化开发了2048 AI,而不是@ovolve算法使用的最小值搜索。AI简单地对所有可能的移动执行最大化,然后对所有可能瓦片产生进行期望(由瓦片的概率加权,即,4个瓦片为10%,2个瓦片为90%)。据我所知,不可能删减expectimax优化(除了删除极不可能的分支),因此所使用的算法是经过仔细优化的暴力搜索。
表演
AI在其默认配置(最大搜索深度为8)中执行移动需要10毫秒到200毫秒,具体取决于板位置的复杂性。在测试中,AI在整个游戏过程中实现了每秒5-10次的平均移动速度。如果搜索深度被限制在6次移动,AI可以轻松地每秒执行20次以上的移动,这使得观看更加有趣。
为了评估AI的得分表现,我运行了100次AI(通过远程控制连接到浏览器游戏)。对于每个平铺,以下是该平铺至少实现一次的游戏比例:
2048: 100%
4096: 100%
8192: 100%
16384: 94%
32768: 36%
所有跑步的最低得分为124024分;最高得分为794076分。平均得分为387222。AI从未未能获得2048个区块(因此它从未在100场游戏中输掉过一次游戏);事实上,它在每次运行中至少实现一次8192平铺!
以下是最佳跑步记录的截图:
这场比赛在96分钟内进行了27830次移动,即平均每秒4.8次移动。
实施
我的方法将整个电路板(16个条目)编码为单个64位整数(其中瓦片是nybbles,即4位块)。在64位机器上,这使得整个电路板可以在单个机器寄存器中传递。
位移位操作用于提取单独的行和列。单个行或列是16位的量,因此大小为65536的表可以对在单个行或行上操作的转换进行编码。例如,移动被实现为预计算的“移动效果表”中的4个查找,该表描述了每次移动如何影响单个行或列(例如,“向右移动”表包含条目“1122->0023”,描述了当向右移动时,行[2,2,4,4]如何变为行[0,0,4,8])。
评分也使用表格查找来完成。这些表包含对所有可能的行/列计算的启发式得分,一个板的最终得分只是每行和每列的表值之和。
这种棋盘表示,以及移动和得分的表格查找方法,允许AI在短时间内搜索大量游戏状态(在我2011年中期笔记本电脑的一个核心上,每秒超过10000000个游戏状态)。
expectimax搜索本身被编码为递归搜索,它在“期望”步骤(测试所有可能的平铺生成位置和值,并根据每个可能性的概率加权其优化分数)和“最大化”步骤(检测所有可能的移动并选择具有最佳分数的移动)之间交替。当树搜索看到之前看到的位置(使用换位表)、达到预定义的深度限制或达到极不可能达到的板状态时(例如,通过从起始位置开始一行获得6“4”块而达到),树搜索终止。典型的搜索深度为4-8次移动。
启发式
使用几种启发式方法将优化算法引向有利位置。启发式算法的精确选择对算法的性能有着巨大的影响。各种启发式算法被加权并组合成一个位置得分,这决定了给定的董事会位置有多“好”。然后,优化搜索将旨在最大化所有可能董事会位置的平均得分。如游戏所示,实际得分不用于计算棋盘得分,因为它的权重太大,有利于合并瓦片(当延迟合并可能产生很大的好处时)。
最初,我使用了两种非常简单的启发式方法,即为开放正方形和边缘值较大的正方形授予“奖金”。这些启发式算法表现得很好,经常达到16384,但从未达到32768。
Petr Morávek(@xivicurk)使用了我的AI,并添加了两种新的启发式方法。第一个启发式是对非单调行和列的惩罚,这些行和列随着排名的增加而增加,从而确保小数字的非单调行不会强烈影响分数,但大数字的非非单调行会严重影响分数。第二个启发式算法除了计算开放空间之外,还计算了潜在合并(相邻的相等值)的数量。这两种启发式方法用于将算法推向单调板(更容易合并),以及大量合并的板位置(鼓励其在可能的情况下对齐合并以获得更大的效果)。
此外,Petr还使用“元优化”策略(使用称为CMA-ES的算法)优化了启发式权重,其中权重本身被调整以获得可能的最高平均分数。
这些变化的影响极其显著。该算法在大约13%的时间内实现了16384个瓦片,在90%的时间内完成了它,并且该算法在1/3的时间内开始实现32768个瓦片(而旧的启发式算法从未产生过32768个)。
我相信启发式方法还有改进的空间。这个算法肯定还不是“最佳”的,但我觉得它已经接近了。
人工智能在超过三分之一的游戏中获得32768分,这是一个巨大的里程碑;我会很惊讶地听到是否有人类玩家在官方游戏中达到了32768(即不使用保存状态或撤销等工具)。我认为65536瓷砖触手可及!
你可以自己尝试人工智能。该代码位于https://github.com/nneonneo/2048-ai.