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

2048这个游戏从刚出開始就风靡整个世界。

本技术博客的目的是想对2048涉及到相关的全部问题进行仔细的分析与讨论,得到一些大家能够接受而且理解的结果。

在这基础上,扩展2048的游戏性,使其变得更好玩。更有意思,更有耐玩性。

本技术博客涵盖了有关2048的策略。理论分析与讨论,代码简单剖析,以及代码扩展的思路。个人觉得应该是至今为止最全的2048游戏相关分析博客了。如有不论什么问题。有意思的讨论。以及想要交流的内容。欢迎大家留言~ 本篇为Part I,针对2048中各个问题进行分析与讨论。


题注

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

自此以后,我踏上了人生,哦不,玩2048的不归路啊… 刚開始的时候我也是一点都不会玩,每次最多仅仅能玩到256。

然后呢,和大多数的朋友们一样呀。也是在网上各种查攻略,查技巧。查来查去。想想自己这一大段时间没干别的,就学算法来着。

于是干脆,也别查了,自己分析分析想想最适合自己的策略吧。这一想不要紧,还真想出一些门道和一些可用的扩展来。

本人呢,又是个编程的渣渣,可是又不甘于真的成为一个编程的渣渣,于是干脆,自己改!最后呢。就有了这样一篇博客,另一个半成不成的Android版Extend 2048游戏。

本文,我尽量写的比較蛋疼。哦不。比較随性一些,不涉及非常恶心的数学分析呀什么的,仅仅是概述性质的描写叙述。对于代码来说呢,我也会写的非常粗浅。仅仅是把核心的问题和解决方法分享给大家。

确实有兴趣的朋友们能够看我改动的源码,来进一步分析改动的地方以及一些细节问题。我会将源码以文本的形式更新到本篇博客中。

本文的第一部分是学酥篇。是我这个学酥已经想明确的。有结论的思考和回答。

对游戏本身有兴趣的,希望对游戏进行进一步了解与理解的。能够看这一部分的内容。看完以后。我想对于2048这个游戏本身,朋友们也就找到最适合自己的差点儿能够确保打过的方法了。

本文的第二部分是学神篇,是我这个学酥想到了,可是似乎不能够解答的问题。对游戏有更进一步思考的,想挑战极限全然研究透彻这个游戏的朋友们。能够看这一部分的内容。这一部分我会列举我想到的有关2048非常有意思,甚至有启示意义的问题。

在此。欢迎大家补充问题。也欢迎大家在博客以下进行仔细的讨论,我们争取能把这些问题全都解决掉,彻底把2048玩吐了,嗯!

本文的第三部分为菜鸟程序猿篇,以一个2048源码为例,分析其组成框架。为进一步改造2048这个游戏做好铺垫,并在PC平台完毕了扩展。PC端不适合玩,仅仅适合对2048的一些有趣的性质进行測试。

我自觉得在Java和Android方向,自己可能还能进阶到0基础程序猿,因此这章就叫菜鸟程序猿篇啦。

本文的第四部分为0基础程序猿篇,依据第一、第二部分讨论的内容,对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的计分规则非常easy:将能合并的两个数合并后。合并的结果为这一次合并玩家所得到的分数。

假设同一时候合并了两个方格,那么得分分别计算后再相加。举几个简单的样例(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留下的数越多越大。得到的分数越高。详细分数列表例如以下:

多添加的数 多添加的分数
1024 1024*8~1024*9
  512 512*7~512*8
  256 256*6~256*7
  128 128*5~128*6
    64 64*4~64*5
    32 32*3~32*4
    16 16*2~16*3
      8 8*1~8*2
      4 4*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这么一个奇怪的数呢?我不认识作者。不知道作者当时是怎样选择这么一个数作为目标值的。

可是我能够确定的是。这个目标是选的确实是恰到优点:既不是非常难,又不是非常easy。

另一方面,大家也可能看到一个现象:即使一个人刚開始玩,连规则都不懂,它也可能上来就玩到了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*1 2^1
2*2 2^3
3*3 ?
4*4 2^11
5*5 ?
这个问题的解决直接影响Part II中扩展版的完整性。因此,相同地,我们互换CSDN上面的牛人们能够一起讨论或者測试这个问题。


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

posted @ 2018-02-04 16:31  zhchoutai  阅读(1166)  评论(0编辑  收藏  举报