最近有一个小游戏“羊了个羊”,突然爆火。它的游戏规则很简单:把堆积在一起的牌从上到下点到临时卡牌槽里,凑齐一样的三张就能消掉,如果全部卡牌都消掉游戏就通关了。临时卡槽一共有7个位置,如果位置都满了,游戏就输了。
羊了个羊
这个游戏第一关非常简单,就像1+1=2一样。第二关难度飙升,直接变成考研。尽管通过分享游戏或者看广告可以有一些辅助道具,但是还是有许多人玩了上百局都没有通关。
有小朋友就问我说:为什么这个游戏这么难玩?它通关的概率到底有多大呢?经过我三四天的研究,今天我就把我的研究成果向大家汇报一下。
一. 游戏的设计逻辑
在网上有一个大神,把游戏的源代码找到了。
b站@码农高天

通过分析源代码,我们可以发现这个游戏的设计大约可以分为三个步骤。
首先:在桌面上设计一些卡的位置。
如果卡牌一共有15种,每一种有18张,那么应该设计15x18=270个卡位。每一卡位都有一组坐标(xi,yi,zi),其中xi和yi表示在桌面上的位置,zi表示堆叠的层数, 角标i从1到270。
其次:对卡牌进行洗牌。
设计15种不同的卡牌,每种卡牌18张,调用“乱序”函数,随机打乱270张卡的顺序。
最后:按顺序把每一张卡放进对应的卡位里。
因为“卡位”和“牌”都已经有了顺序,只要把序号相同的牌放在相应的卡位上,就完成游戏的初始化。
游戏初始化完成后
在这个过程中,“设计卡位”的步骤是人工完成的,程序员人为的设定好每一个卡的位置,并且每一天更换一次。所以,你会发现卡的排列非常对称,而且在每一天中固定不变,这就是“每日一关”的含义。
而“洗牌”的过程是程序随机完成的,所以如果你在一天中打很多局,卡牌的位置不会变,但是每个位置上的卡牌种类是变化的。
通过分析程序设计逻辑,我们会发现:游戏设计者并没有人为的设置障碍,让玩家无法通关。但是因为它的“随机洗牌“,造成许多关卡本身就是无解的。
而且,游戏能够通关的概率有多大,和程序员人工设计卡位的方式很有关系。可以想象:如果程序员把所有的卡平铺在桌面上,那程序100%是有解的。但是如果把所有卡摞成一列,那有解的可能性就微乎其微了。
这种局面有解的可能性不大
二. 对残局的数学分析
我们不妨来分析一个残局,看看游戏有解的概率大约有多少。
假设在某一天的游戏中,游戏设计师设计的卡位,最底部的五层是这样的:
最底层有一张牌:
在倒数第二层有4张牌:
在上面又压了4张牌
在上面又压了4张牌
最上面又压了1张牌:
也就是:从最下方到最上方,是1-4-4-4-1的结构,一共有14张牌
如果这个结构位于整个牌堆的最底部,根据解锁的原则,只有点开最上层的1张牌,才能解锁其他牌,所以要想通关,必须会在某个时刻面对这个残局。
那么,面对这样的局面,通关概率大概有多大呢?
我们来计算一下这种牌局通关的必要条件
假设在桌面上的最后14张牌,种类一共有N种。显然,N最小是1(所有卡牌都是同一种类的),最大是14(所有卡牌都是不同的)。可以想象:如所有的卡都是同一种,一定能够通关;如果有14种,那一定无法通关。那么,要想通关,这14张卡的种类N最多是几呢?
“羊了个羊”是一个三消游戏,要想把N个种类的牌完全消除,牌的个数至少是3N张。也就是:如果残局里每一种牌都是3张,一共3N张,就有可能完全消掉。
*如果某种牌有6张,那总的牌数就需要是3N+3张,才有可能完全消除。
可是,刚刚还说桌面上只有14张牌,现在又说有3N张,这不是矛盾吗?
别忘了:临时卡槽还有一些位置。如果刚好能够通关,3N张卡中有14张在桌面上,余下的3N-14张就应该在临时卡槽里。
还有,临时卡槽一共只有7个位置,而且装满了牌游戏就结束了,所以临时卡槽里的储存的牌最多只有6张。
这样,我们就得到了一个核心不等式:
3N-14≤6
这表示:残局中如果有N种牌,那么要想通关,牌的数量至少是3N张,其中有14张在桌面上,余下的牌在临时卡槽里,而且最多有6张。
解上述不等式,同时注意N是一个整数,得到
  N≤6
即:如果牌局的最底层14张牌如我假设排列,那么牌局有解的必要条件是最后14张牌的种类小于等于6种。
必须注意:即使满足这个条件,牌局依然不一定有解。但是不满足这个条件,牌局一定是无解的。
*在我讲到这个问题时,很多朋友说我做错了,因为一共270张牌一定是3个一组消去的,所以最后不可能剩余不成组的14张牌。其实,这种说法是没有理解我的设计意图。因为程序员的随机乱序,最后14张牌是什么都有可能。但是,如果最后14张牌的种类数多余6种,那么就必然无法通过消去达到这个残局,也就是无解的。如果要有解,首先要达到这个残局,那就必须满足N≤6的条件。
三. 蒙特卡罗方法
一共15种牌,每种18张,随机排列在270个位置上,在最后14个位置上牌的种类数小于6种,概率有多大呢?显然这不是一个好算的数学问题。
不过幸好,我们有了计算机,可以通过蒙特卡罗方法得到问题的估计值。即让计算机随机尝试10000次,看看有多少次满足条件,就能估算这个概率了。
具体的模拟方法是这样的:
模拟程序代码如下:
通过实验,我发现在10000次统计中,底层14张牌的种类小于等于6种的次数大约在40在70次之间。我又把以上的程序循环执行了1000次,得到了在一万局中,“可能有解”的局数的分布关系如下:
1000次、10000局中“可能有解”的局数统计分布

你会发现:一万局中,可能有解的局数大约成正态分布,期望数53,标准差7,也就是我们得出了以下结论:
如果游戏最底部的五层如我的设计,那么游戏有解的概率上限平均值为0.5%。我们有95%的把握说:游戏有解的概率上限不超过0.7%
也就是说,玩1000把,最多也就只有5到7局是“有可能有解”的。实际上,我还只是考虑了14张牌的一个残局,真实的游戏牌局比我设计的更坑,有解的概率很有可能再低10到100倍,即0.1%甚至0.01%!
游戏之所以不能通关,不是我们的智商问题,而是因为程序员偷懒做了个大随机,造成关卡本身无解。
四. 如何改进程序
如果我设计这个游戏,肯定不会允许无解的情况发生。我会按照这样的思路设计游戏:
第一步:设计卡位顺序:
1. 人为设定270个卡位。
*在没有放卡之前,这些卡位都是空的。
2. 按照“从上到下“的解锁条件,随机生成一条解锁路径。
*因为同一层卡牌有很多张,解锁的路径也非常多。我们在这非常多的路径中,让程序随机寻找一条符合规定的路径即可。
3. 把解锁路径倒过来,作为卡位的顺序。
第二步:设计卡牌顺序
15种卡牌,每种6组,每组3张,按照同组三张在一起的原则,进行组间乱序排列。
*和原来程序的区别在于:在我们的设计中,乱序后的卡牌依然是三张一样的牌连在一起的。
第三步:将所有卡牌按序放回桌面的对应卡位上
反复随机执行下列操作之一:
1. 取出卡牌序列中一组牌,2张放在临时卡槽中,另1张按序放在桌面卡位上。
2. 将临时卡槽中随机的一张牌按序放到桌面卡位上。
*其实,这两个步骤就是把消除卡片的顺序倒过来。注意,在临时卡槽空的时候只能做第一步,临时卡槽的牌超过4张时只能做第二个步骤,而且做第一步骤时要保证卡槽内不能已经有相同的牌。
这样,我设计了一条消除路径(第一步),又按照消除的规则把卡牌一个个倒着放回到路径上(第二步和第三步),所以最终一定是有解的,而且,这也并不会降低游戏的可玩性。怎么样,我这个游戏设计师还不错吧!
据说这两天“羊了个羊”因为铺天盖地的批评,已经把游戏难度调低了。我看它采用的方法貌似是增加某几种牌的比例,减少另外几种牌的比例。可是这样做就大大降低了游戏性。
我依然建议他们,用我的方法修改一下游戏。
END
继续阅读
阅读原文