2048游戏:我做了多少步?
Posted
技术标签:
【中文标题】2048游戏:我做了多少步?【英文标题】:2048 game: how many moves did I do? 【发布时间】:2014-07-23 11:39:06 【问题描述】:2048 不久前还很流行。每个人都玩它,很多人都发布了他们的成就(我自己也在其中)的漂亮截图。然后在某个时候,我开始怀疑是否有可能说出某人玩了多长时间才能达到这个分数。我进行了基准测试,结果证明(至少在我拥有的 android 应用程序上)一秒钟内最多只能进行一次动作。因此,如果您玩的时间足够长(并且足够快),那么您所做的移动次数与您玩的秒数非常接近。现在的问题是:是否有可能通过 2048 游戏的屏幕截图来计算进行了多少步。
这是一个示例截图(实际上是我迄今为止在游戏中的最大努力):
从截图中你可以看到当前的场地布局和玩家获得的分数。那么:这些信息是否足以计算出我做了多少步?如果是,那么算法是什么?
注意:我想提醒您,只有当两个瓷砖“组合”时才会得分,得分的数量是新瓷砖的值(即被组合的瓷砖值的总和)。
【问题讨论】:
【参考方案1】:简短的回答是可以仅使用此信息来计算移动次数。我将解释执行此操作的算法,并尝试分步发布我的答案。每一步都将是一个旨在帮助您解决问题的观察。我鼓励读者在每次提示后尝试单独解决问题。
观察第一:每次移动后恰好出现一个图块。该图块是 4 或 2。因此我们需要做的是计算出现的图块数量。至少在我玩的游戏版本中,游戏总是从 2 块瓷砖开始,其中 2 块是随机放置的。
我们不关心字段的实际布局。我们只关心上面的数字。当我解释我的算法时,这一点会变得更加明显。
查看字段上单元格中的值,我们可以计算出如果在 每个 移动之后出现 2 的得分是多少。调用该值twos_score
。
出现的四点数等于 twos_score
和 actual_score
的差除以 4。这是正确的,因为如果要从两个 2-s 形成 4,我们会得到 4
分,而如果 4 立即出现,我们得分 0
。拨打四人号码fours
。
我们可以计算出在场上形成所有数字所需的二进制数。之后,我们需要从该值中减去2 * fours
,因为单个 4 代替了两个 2。打电话给twos
。
使用这些观察结果,我们能够解决问题。现在我将更详细地解释如何执行这些单独的步骤。
如果只有两个出现,如何计算分数?
我将证明,要形成数字 2n,玩家将得分 2
n
*(n - 1)
点数(使用归纳法)。
2
k
*(k - 1)
对于 k + 1:2k + 1 只能由值 2k 的两个数字组合而成。因此用户将得分2
k
*(k - 1) + 2
k
*(k - 1) + 2
k+1
(两者的得分合并的数字加上新数字的分数)。
这等于:2
k + 1
*(k - 1) + 2
k+1
= 2
k+1
* (k - 1 + 1) = 2
@987654348 @* k
。这样就完成了归纳。
因此,如果只有两个出现,我们需要遍历板上的所有数字并使用上面的公式累积我们为它们得到的分数。
如何计算在字段上形成数字所需的二进制数?
更容易注意到形成2
n
所需的二进制数是2
n - 1
。可以再次使用归纳来完成严格的证明,但我将把它留给读者。
代码
我将在c++
中提供解决问题的代码。但是我不使用任何语言特定的东西(来自vector
,它只是一个动态扩展的数组),所以它应该很容易移植到许多其他语言。
/**
* @param a - a vector containing the values currently in the field.
* A value of zero means "empty cell".
* @param score - the score the player currently has.
* @return a pair where the first number is the number of twos that appeared
* and the second number is the number of fours that appeared.
*/
pair<int,int> solve(const vector<vector<int> >& a, int score)
vector<int> counts(20, 0);
for (int i = 0; i < (int)a.size(); ++i)
for (int j = 0; j < (int)a[0].size(); ++j)
if (a[i][j] == 0)
continue;
int num;
for (int l = 1; l < 20; ++l)
if (a[i][j] == 1 << l)
num = l;
break;
counts[num]++;
// What the score would be if only twos appeared every time
int twos_score = 0;
for (int i = 1; i < 20; ++i)
twos_score += counts[i] * (1 << i) * (i - 1);
// For each 4 that appears instead of a two the overall score decreases by 4
int fours = (twos_score - score) / 4;
// How many twos are needed for all the numbers on the field(ignoring score)
int twos = 0;
for (int i = 1; i < 20; ++i)
twos += counts[i] * (1 << (i - 1));
// Each four replaces two 2-s
twos -= fours * 2;
return make_pair(twos, fours);
现在要回答我们做了多少步,我们应该将此函数返回的对的两个值相加并减去两个,因为两个带有 2 的图块会立即出现。
【讨论】:
这不是给你玩家进行的合并次数,这只是移动次数的上限吗?每次移动可以合并 2、4、6、8 等...瓷砖,具体取决于它们是否同时排列。在我看来,这确实是你能做的最好的事情,不用猜测。 @GuyGreer 不,这个计算是准确的。我也验证过很多案例。请再次阅读答案。我会考虑所有的合并以及你为他们打分的分数 我玩了一个测试游戏,数了 115 步,但你的算法给出了 117。我尝试了另一种方法,我数了 81 步,但你的算法给出了 83。正如我所说,你的算法给出了一个上限关于移动的数量,而不是实际数量。 @GuyGreer 您应该减去两个,因为前两个 2 会立即出现,因此您不会为它们移动。我忘了在我的答案中包含这个。您可以验证这对您的两次尝试都有效。我已经尝试了超过 1000 次相同的动作,并且成功了。请更详细地解释为什么您认为这是一个上限。 没关系,我现在明白了。感谢您的耐心等待 =)。以上是关于2048游戏:我做了多少步?的主要内容,如果未能解决你的问题,请参考以下文章