2048游戏分析、讨论与扩展 - Part I - 游戏分析与讨论

来源:互联网 发布:马桶品牌 知乎 编辑:程序博客网 时间:2024/06/11 07:40

2048这个游戏从刚出开始就风靡整个世界。本技术博客的目的是想对2048涉及到相关的所有问题进行细致的分析与讨论,得到一些大家能够接受并且理解的结果。在这基础上,扩展2048的游戏性,使其变得更好玩,更有意思,更有耐玩性。本技术博客涵盖了有关2048的策略,理论分析与讨论,代码简单剖析,以及代码扩展的思路。个人认为应该是至今为止最全的2048游戏相关分析博客了。如有任何问题,有意思的讨论,以及想要交流的内容,欢迎大家留言~ 本篇为Part I,针对2048中各个问题进行分析与讨论。

题注

2048这款游戏刚开始出的时候没有提起我的一点兴趣…我的一个高中同学曾经在微信上求助(当然了,因为隐私问题我把姓名和头像都抹掉了):

自此以后,我踏上了人生,哦不,玩2048的不归路啊… 刚开始的时候我也是一点都不会玩,每次最多只能玩到256。然后呢,和大多数的朋友们一样呀,也是在网上各种查攻略,查技巧。查来查去,想想自己这一大段时间没干别的,就学算法来着。于是干脆,也别查了,自己分析分析想想最适合自己的策略吧!这一想不要紧,还真想出一些门道和一些可用的扩展来。本人呢,又是个编程的渣渣,但是又不甘于真的成为一个编程的渣渣,于是干脆,自己改!最后呢,就有了这样一篇博客,还有一个半成不成的Android版Extend 2048游戏。

本文,我尽量写的比较蛋疼,哦不,比较随性一些,不涉及非常恶心的数学分析呀什么的,只是概述性质的描述。对于代码来说呢,我也会写的非常粗浅,只是把核心的问题和解决方法分享给大家。确实有兴趣的朋友们可以看我修改的源代码,来进一步分析改动的地方以及一些细节问题。我会将源代码以文本的形式更新到本篇博客中。

本文的第一部分是学酥篇,是我这个学酥已经想明白的,有结论的思考和回答。对游戏本身有兴趣的,希望对游戏进行进一步了解与理解的,可以看这一部分的内容。看完以后,我想对于2048这个游戏本身,朋友们也就找到最适合自己的几乎能够确保打过的方法了。

本文的第二部分是学神篇,是我这个学酥想到了,但是似乎不能够解答的问题。对游戏有更进一步思考的,想挑战极限完全研究透彻这个游戏的朋友们,可以看这一部分的内容。这一部分我会列举我想到的有关2048很有意思,甚至有启发意义的问题。在此,欢迎大家补充问题,也欢迎大家在博客下面进行细致的讨论,我们争取能把这些问题全都解决掉,彻底把2048玩吐了,嗯!

本文的第三部分为菜鸟程序员篇,以一个2048源代码为例,分析其组成框架,为进一步改造2048这个游戏做好铺垫,并在PC平台完成了扩展。PC端不适合玩,只适合对2048的一些有趣的性质进行测试。我自认为在Java和Android方向,自己可能还能进阶到初级程序员,因此这章就叫菜鸟程序员篇啦。

本文的第四部分为初级程序员篇,根据第一、第二部分讨论的内容,对2048这个游戏在Android下进行进一步的扩展,使其变得更好玩,更耐玩,还更有挑战性。不过,因为第二部分中一些比较关键的问题没有解决,所以这个扩展版实际上还没有完成,在此呼吁CSDN上面有兴趣的朋友们,一起来进行更新,看看能不能把它变得更棒!

由于文章太长,本文分成两篇博客分别撰写。本篇为Part I,主要是游戏分析与讨论,涵盖了第一部分和第二部分的内容。后面准备撰写和发布的Part II,主要是游戏的实现和扩展,涵盖了第三部分和第四部分的内容。

啰啰嗦嗦的说了一大堆,最后提几个声明:

  • 本文的所有理论分析中的图像,使用MathType构造,截图后上传;
  • 本文的所有图片为本人原创;
  • 2048程序的例子原始链接为:https://github.com/PeterCxy/2048。其遵守开源协议。大家可以以非商业为目的对代码进行修改和调试。
  • 感谢我们实验室跟我一起为2048 High起来的开发大牛FBA(拼音缩写)同学,为修改Android版Extend 2048的界面做出了巨大的贡献。FBA同学在网上的昵称为firefix。他的CSDN博客地址我得给大家问问,他也是刚开的技术博客,相信以后他的博客中会有很多好玩的东西;感谢另外一个实验室,跟我一样蛋疼的好玩的老师在这篇博客中跟我进行的讨论,让我对于学神问题进行了进一步的推广,并且能够找到一种可行的解决学神问题的方法。他的网上昵称为king,可是个在学院颇受欢迎的萌老师哦!

第一部分-学酥篇-2048游戏分析

1. 分数计算和分数分析

分析

首先,我们来关注一下2048中大家都不怎么关注,但是游戏里面又不可缺少的细节:2048的计分。2048是如何记分的?为什么达到2048的目标时分数总在一个范围内变化呢?

大家运行自己的2048,简单的做几个位置的移动,就能够看出2048的一些记分的特点了。2048的计分规则非常简单:将能合并的两个数合并后,合并的结果为这一次合并玩家所得到的分数。如果同时合并了两个方格,那么得分分别计算后再相加。举几个简单的例子(2015.07.27更新,此处非常感谢一位面试官指出了这里的一个错误,下面第2种情况应该得到20分,而不是40分。明年或者后年入职后需要当面致谢~):

  • 如果把一个2和一个2合并,那么玩家得4分;如果把1个4和1个4合并,那么玩家得8分,以此类推;
  • 如果同时合并了两个2和2,那么玩家得(2+2) + (2+2) = 8分。如果同时合并了1个2和2,以及1个8和8,那么玩家得(2+2) + (8+8) = 20分,以此类推;

那么,如果合并到2048,玩家得到的分数应该是多少呢?我们有:

2048 = 1024 + 1024

1024 = 512 + 512

  512 = 256 + 256

  256 = 128 + 128

  128 = 64 + 64

    64 = 32 + 32

    32 = 16 + 16

    16 = 8 + 8

      8 = 4 + 4

所以,玩家至少得到的分数为:2*1024 + 4 * 512 + 8 * 256 + 16 * 128 + 32 * 64 + 64 * 32 + 128 * 16 + 256 * 8 + 512 * 4 = 2048 * 9 = 18432

然而,我们玩的过程中会发现,新增加的方格有时候会直接产生4,而非2。这种产生会影响到玩家得得分。因为如果产生了一个4,而非一个2,那么玩家合并的时候,4 = 2 + 2的这个分数就没有了,玩家会因此损失4分。这种损失是不可避免的,而且确实实实在在地影响了玩家得最终得分。所以,对于只合并到单一的2048这个数,玩家最多得到的分数为:18432 + 1024 * 2 = 20480。这也就是为什么大家在玩的过程中,达到2048时自己游戏的分数一般都大概为20000分的原因了。

但是呢,有的玩家确实在合并到2048时拿到了更高的分数。这是因为在获得2048的时候,面板上面还有很多合并2048没有使用的数,而且合并到2048留下的数越多越大,得到的分数越高。具体分数列表如下:

多增加的数多增加的分数10241024*8~1024*9  512512*7~512*8  256256*6~256*7  128128*5~128*6    6464*4~64*5    3232*3~32*4    1616*2~16*3      88*1~8*2      44*0~4*1

每个1024,分数会增加2048 * 8 到 2048 * 9之间;而且,由于出4的概率比较小(从后面的源代码分析中也可以看出,游戏每次有10%的概率出现4,90%的概率出现2),因此分数会比较接近于预估最高分。举个例子,如果玩家合并到2048时面板上的情况如图:


那么,其大致分数应该可以估计为:
最高分 = 2048*10 + 1024*9 + 8*2 + 4*1 = 29716
最低分 = 2048*9 + 1024*8 + 8*1 + 4*0 = 26632

为了进一步验证正确性,我来举几个我玩完版本的例子。

Example 1

预计最高分:2048*10 + 16*3 + 2 * (8*2) + 3 * (4*1) = 20572

预计最低分:2048*9 + 16*2 + 2 * (8*1) = 18480

Example 2

预计最高分 = 2048*10 + 64*5 + 16*3 + 8*2 + 3 * (4*1) = 20881

预计最低分 = 2048*9 + 64*4 + 16*2 + 8*1 + 3 * (4*0) = 18728

2. 最大解分析

结论

在网上的很多朋友实际上已经分析了最大解的问题。这里面比较知名的,总结的也比较全的文章是《知乎》上面的一篇,链接为:http://m.zhihu.com/question/23492860。本篇文章也就不把这篇文章再粘贴过来占用篇幅啦。我们在这里只是把它从单纯的4*4,扩展到n*n的情况:

1*1的情况下,最大解为4,如图。


2*2的情况下,最大解为32 = 2^5 = 2^(2*2 + 1),如图。


3*3的情况下,最大解为1024 = 2^10 = 2^(3*3 + 1),如图。


4*4的情况下,最大解为2^17 = 2^(4*4 + 1),如图。其中,我们知道最小的格必须是4,所以有n - 14 = 2,因此n = 16。在这种情况下是能够全合并的,因此最高分可以得到2^17。


以此类推,n*n的情况下,最大解也就显然为2^(n*n + 1)。具体为什么这样最优,以及为何不可能再高了,请大家参看《知乎》上面的帖子。

策略

这个问题的研究实际上也给出了2048这个游戏的通用直观解法:尽可能让方格里面的数从大到小以回行排列,整个排列就像一个贪吃蛇一样,并且让最大的数卡在角落中。在网上的很多文章和帖子往往都只强调了“最大数在角落”这一点,而没有强调按照“贪吃蛇”方式安排其余数这一点。这也是很多朋友玩得到1024,就是到不了2048的原因。

按照上面的分析,排成“贪吃蛇”方式的理由就很直观了:合并的时候为了合并方便。至于为什么要把最大数放在角落里面呢?这是游戏性质决定的,固定一个角落,方格会都向固定的角落上面靠。如果不固定角落的话,最大数周围产生的小数很难合并到较大数中。我相信大家在玩游戏的过程中也有所体会吧~ 如果不小心把最大数移出角落,结果在最大数旁边生成一个2,那么把它合并起来几乎就是不可能的事情了。绝大多数已经知道诀窍的人,仍然输的原因基本都是如此:万不得已让最大数离开了一下角落,结果在最大数旁边产生了一个小数…

3. 达到目标解的移动步数分析

Analysis

那么,2048游戏如果推广到n*n的情况下,是否可以一直玩下去,而电脑(手机)屏幕可以显示的下呢?这个问题就涉及到“达到目标解的移动步数”这个问题了。简单些,就是说:最后玩到2048的时候,我们到底进行了多少次移动,或者说滑动了多少次屏幕呢?这个问题也会让大家了解到,玩成功一次2048大约需要多长的时间。

实际上,最后得到的2048是靠一个个生成的2和4加起来的。在这里为了简化讨论,我们假设每一次移动产生的方格都是2。这样的话,我们有如下的递推关系:

产生2048需要移动1次(2个1024相加)

产生2个1024需要移动2次(4个512分别相加)

产生4个512需要移动4次(8个256分别相加)

产生8个256需要移动8次(16个128分别相加)

产生16个128需要移动16次(32个64分别相加)

产生32个64需要移动32次(64个32分别相加)

产生64个32需要移动64次(128个16分别相加)

产生128个16需要移动128次(256个8分别相加)

产生256个8需要移动256次(512个4分别相加)

产生512个4需要移动512次(1024个2分别相加)

因此,总移动次数为:512+256+128+64+32+16+8+4+2+1 = 1024 - 1 = 1023次。大家玩2048的时候,算上游戏移动的动画的话,假定0.5s移动一次,那么一次2048的时间大致为:0.5s/次 * 1023次 = 511.5s,约为8.5分钟。这也就是为什么2048特别适合消磨时光的原因了… 时间长度恰到好处,正好是人体工作中间需要休息的大概时间。

这里实际上还有一个问题,在大家玩的时候,很可能出现同时合并好几个解的情况。举个例子,在下图中,我们向上(或者向下)移动就能够同时合并4个数:

这样的合并显然会减少总的合并和移动次数。但是我们说,这种情况并不是每次都发生,同时,我们还会经常发生移动但不合并任何数的情况。因此,这种并行合并的情况以及移动不合并的情况,我们不进行讨论(或者说根本没有办法讨论…)。

但是,我们要发现一个问题,对于2048来说,移动次数为2048 / 2 - 1次。那么如果到了4096呢?移动次数就变为了4096 / 2 - 1 = 2047次。换算成时间就为17分钟,这还可以接受。那么到8192呢?移动次数就变为了8192 / 2 - 1 = 4095次。换算成时间就为34分钟。也就是说,随着目标值的增长,游戏时间会成2次方指数长度增长。

那么,假设一个人每0.5s移动一次,365*24*3600s不停地进行游戏,假设游戏是可以一直玩下去的话,那么在这段时间内,其总共能够移动365*24*3600 / 0.5 = 62208000次,约为2^26,因此其能达到的最大分数为2^27,这还是在假设游戏过程中一直往高分玩。因此,2048这个游戏在n*n的情况下,理论上是可以一直玩下去的,但是玩下去的时间是一个天文数字。

Example

在此,我们给一个例子。这个例子的运行时靠第三部分的程序而来的。我们让计算机随机进行移动,如果无法再移动,则游戏自动扩大一个维度继续玩。也就是说,如果4*4玩不动了,游戏就扩展成为5*5的继续。我让计算机一共进行31536000次移动,来看看结果计算机扩展到了多大。这个等待是相当漫长的… 几乎用了快10分钟的时间才运行完毕,最后的结果图如下:

也就是说,游戏刚刚扩展到了9*9(我们可以看到面板上还有相当多的空白部分,表示离填满还远得很)。因此,在后续扩展中,最多让方格是10*10的,那么任何正常的人类,在有限的时间内几乎都无法玩到这种情况。

第二部分-学神篇-2048游戏讨论

1. 必然解和通用解讨论

其实呢,2048有一个特别显而易见,但是又特别难以回答的问题:2048为什么把4*4的目标值设置成了2048这么一个奇怪的数呢?我不认识作者,不知道作者当时是如何选择这么一个数作为目标值的。但是我可以确定的是,这个目标是选的确实是恰到好处:既不是很难,又不是很简单。

另一方面,大家也可能看到一个现象:即使一个人刚开始玩,连规则都不懂,它也可能上来就玩到了256甚至512。这实际上牵扯到了2048中一个非常难分析的问题:必然解,通用解,和最大解讨论。

首先给这几个名词下个定义。

  • 必然解:在n*n的情况下,瞎玩也能玩到的解,成为必然解。
  • 通用解:在n*n的情况下,很会玩,用最优的策略,必然能够得到的解。注意,这种情况下是玩家很会玩,知道最佳策略,但是无法控制产生方格的位置以及产生的数。
  • 最大解:在n*n的情况下,很会玩,用最优的策略,并且每次方格产生的位置都是最理想的情况下能拿到的最大分数。

对于最大解的分析,在第一部分已经得到结论了。那么,必然解和通用解呢?这是一个很难分析的问题… 我们从简单的情况依次进行分析。

1*1

这种情况是最简单的。第一个方格出现4,那么达到4;如果出现2,那么达到2;因此,这种情况和玩家会不会玩没有关系,直接得到结论:

  • 必然解 = 2
  • 通用解 = 2
  • 最大解 = 4

2*2

这种情况分析起来就比较复杂了… 大家可以试一试自己玩玩2*2的情况,似乎必然解为8。也就是说,即使随便按,游戏也至少能玩到8。为什么呢?我们可以用枚举的方法来看,具体过程就不细说了,我把非对称的例子排除,只给大家展示有必要展示的情况,如下图:

这是我能想到的最恶劣情况,但是即使这样也是能够合并出8的。同样地,这里也没有什么策略的情况了。

然而,是不是一定能够弄出16呢?答案是否定的。下面的情况就没办法弄出16(方框框住的是新出的数字):

这实际上也和策略没什么关系,因为每一步都是必须要走的(忽略对称情况)。因此我们可以得出结论:

  • 必然解 = 8
  • 通用解 = 8
  • 最大解见第一部分

3*3

这种情况就更复杂了,甚至我们无法枚举出所有情况。实际上,因为大家也没玩过3*3,对这种情况的研究也不深,所以这个地方我们只能暂时放在这里,等看到能人把这个地方填补完整了。

4*4

这种情况是大家经常讨论的情况。这个讨论实际上还有一个等价说法:如果会玩的话,用最佳策略,是否意味着2048是一定能够达到的呢?首先我们知道,2048一定不是必然解,因为不会玩的时候大家都玩不到嘛。现在的问题是,2048是不是通用解?这个就不能让我们一个一个去试了,因为即使最会玩的人,也有可能出现失误。然而出现失误了是说游戏本身玩不到2048,还是说因为失误导致的失败呢?这个问题最好由计算机来回答。

这里必须要感谢King老师了!在撰写这篇博客的过程中,King老师还跟我进行了一下讨论,如图:

King老师给我提供了一个stackoverflow空间,里面真的有人通过程序实验了一把。图里面的链接不方便大家点击,我在这里重复一下:http://stackoverflow.com/questions/22342854/what-is-the-optimal-algorithm-for-the-game-2048。我把有关的关键论述也放在博客中来:

I developed a 2048 AI using expectimax optimization, instead of the minimax search used by @ovolve's algorithm. The AI simply performs maximization over all possible moves, followed by expectation over all possible tile spawns (weighted by the probability of the tiles, i.e. 10% for a 4 and 90% for a 2). As far as I'm aware, it is not possible to prune expectimax optimization (except to remove branches that are exceedingly unlikely), and so the algorithm used is a carefully optimized brute force search.

Performance

The AI in its default configuration (max search depth of 8) takes anywhere from 10ms to 200ms to execute a move, depending on the complexity of the board position. In testing, the AI achieves an average move rate of 6-10 moves per second over the course of an entire game. If the search depth is limited to 6 moves, the AI can easily execute 20+ moves per second, which makes for someinteresting watching.

To assess the score performance of the AI, I ran the AI 100 times (connected to the browser game via remote control). For each tile, here are the proportions of games in which that tile was achieved at least once:

2048: 100%4096: 97%8192: 76%16384: 13%

The minimum score over all runs was 27536; the maximum score achieved was 377792. The median score is 157652. The AI never failed to obtain the 2048 tile (so it never lost the game even once in 100 games).

也就是说,用最优策略的话,运行了100次,对于2048都成功了,而4096有97%的概率成功了。这样就意味着,4096并不是通用解。那么,这是否意味着2048是通用解呢?也不尽然… 实际上,如果测试了很多很多次,突然又一次没跑出来2048,那么也就意味着2048并不是通用解。但是到现在为止,还是都成功了的… 因此,我们只能断言:

  • 必然解 = 未知
  • 通用解 = 2048
  • 最大解见第一部分

对于n*n,我们确实是无法进行分析了。这里呼唤CSDN上面的牛人们,能够一起来讨论,或者测试这个问题。

2. n*n下目标解的设定问题

那么,接下来又来了一个好玩的问题。在4*4下,游戏作者设定了2048作为目标值。这似乎是通用解的必然结果。那么,在n*n的条件下,目标解应该设定为多少呢?这又是一个到现在为止没有解决的问题。对于一个游戏来说,我们不能将目标解设定为一个靠概率才能够达到的数,要不然即使最会玩的人,也有可能会失败。因此,讲目标解设定为通用解似乎是一个正确的方法。但是,按照前面的讨论,我们确实得不到n*n下的通用解,甚至只能进行猜测。我们只能得到如下的结论(“?”为不确定或者未做测试的结果):

维度目标解设定值1*12^12*22^33*3?4*42^115*5?这个问题的解决直接影响Part II中扩展版的完整性。因此,同样地,我们互换CSDN上面的牛人们能够一起讨论或者测试这个问题。


好啦,Part I就到这里。如果有其他好玩的,值得讨论的问题,欢迎大家留言,我也会逐渐继续补充这一篇技术博客,使之成为最全的有关2048游戏的分析与讨论博客。下一部分,我们将切换到代码的实现,分析看看如何实现2048这个游戏,以及能够进行什么样好玩的扩展。

3 0