2048格子位置判定

举报
用户已注销 发表于 2021/11/19 04:50:36 2021/11/19
【摘要】 最近发现2048可以自定义: 只看下面的16个格子,不看上面的统计数据。 看到这么多格子,首先很明显,至少有1个是2,而且至少有1个是4 可能全部由2和4组成,也可能由2和4和8组成,等等。 总之,这样就有个问题,因为分不清楚不同的数字,所以很难玩下去。 下面考虑这个问题: 如果2、4、8、16等等,所有的...

最近发现2048可以自定义:



只看下面的16个格子,不看上面的统计数据。

看到这么多格子,首先很明显,至少有1个是2,而且至少有1个是4

可能全部由2和4组成,也可能由2和4和8组成,等等。

总之,这样就有个问题,因为分不清楚不同的数字,所以很难玩下去。


下面考虑这个问题:

如果2、4、8、16等等,所有的格子都是一模一样的,那么2048会有多少种看起来不一样的局面?

根据时间,可以分为3类。

第一类:刚开始的时候,有2个格子。

第二类:某次滑动之后,系统暂时还没添加2

第三类:系统添加2之后,玩家还没滑动


这只是根据时间来分而已,它们所包含的局面互相都各有重合的,所以考虑重合的问题是本文的关键。

虽然第一类比较简单,但是第二类更规则,应该从第二类开始分析。

第二类满足一个重要的规则:所有的格子都靠着某条边。

比如说,如果最后一次滑动是向下滑,那么所有的格子都是靠着下面的。

这样,每一列都有5种情况,即这一列的格子数目是0、1、2、3、4

这样,一共是5*5*5*5=625种情况。

但是,第三类就非常复杂了。

一方面,最新添加的2的位置不好分析,另外一方面,存在既靠着下面又靠着左边的情况,这样讨论就十分复杂了。

解决这种讨论十分繁琐的问题的途径之一,自然是编程了。


首先,信息的表示。

用1个16位二进制整数代表一个局面,从上到下,从左往右,比如上面的图片就是0000 0010 0011 1111

然后定义合法的局面。

合法的局面有2种,一种是刚开始的时候有2个格子,即第一类时间,

还有一种是第二类时间和第三类时间的合并,即要么全部靠着某条边,要么除了某个格子之外全部靠着某条边。

(没有格子是不算靠着边的)

除此之外的局面自然是非法的。

但是要说这些局面就一定都是合法的,还要看2048的规则。

实际上,只要是满足上述条件就一定是合法的,因为存在仅由2、4、8、16构成的对应的局面,详情略。

因为总局面只有2^16=65536种,所以直接枚举就可以了,不需要用计数的方法。

虽然4个方向是等价的,但是现在有了确定的编码方案,4个方向就不一样了,最容易判断的,是是否靠右边。

靠右边就是4行都靠右边,而一行里面,靠右边就只有5种情况:0000、0001、0011、0111、1111

判断一行的话,只需要一个简单的函数ok就行

bool ok(int n)
{
	if (n != 0 && n != 1 && n != 3 && n != 7 && n != 15)return false;
	return true;
}
判断一个局面是否靠右,只需要4次调用ok函数即可

bool right(int n)
{
return ok(n & 15) && ok((n >> 4) & 15) && ok((n >> 8) & 15) && ok((n >> 12) & 15);
}
判断一个局面是否靠左,可以转化成是否靠右的问题。
总局面有65536种,即从0到65535,这些局面可以两两配对。
i+j=65535的2个局面i和j即为配对的局面,这样的2个局面是互补的。

这样,是否靠左的问题就可以转化成是否靠右的问题了。
所以我们可以得到,判断是否靠左或者靠右的函数left_or_right:
bool left_or_right(int n)
{
	if (right(n))return true;
	return right(65535 - n);
}

然后判断是否靠上或者靠下,这个问题可以转化成是否靠左或者靠右。
类似上面的做法,这里同样需要一种变换,不过不像上面的互补这么简洁,这里的变换是沿着对角线翻转。


所以,这样我们就得到了判断一个局面是否靠着某条边的函数all_direction:
bool all_direction(int n)
{
	if (n == 0)return false;
	if (left_or_right(n))return true;
	return left_or_right(n - (((n>>4)%2 - (n>>1)%2) * 14 + ((n>>8)%2 - (n>>2)%2) * 252 + ((n>>9)%2 - (n>>6)%2) * 448 + ((n>>12)%2 - (n>>3)%2) * 4088 + ((n>>13)%2 - (n>>7)%2) * 8064 + ((n>>14)%2 - (n>>11)%2) * 14336));
}
其中专门把n=0的情况排除掉了。

现在可以在主函数里面统计第二种合法局面的个数了。
int main()
{	int r[65536];
	for (int i = 0; i < 65536; i++)
	{
		r[i] = 0;
		if (all_direction(i))r[i] = 1;
		for (int k = 0; k < 16; k++)if ((i&(1<<k))!=0)
		if (all_direction(i^(1<<k)))r[i] = 1;
	}
	int result = 0;
	for (int i = 0; i < 65536; i++)result += r[i];
	cout << result;
	return 0;
}
结果是11755

除此之外,还有第一种合法局面,即刚开始的2个格子。
任选2个格子,得到的局面都是第一种合法局面,但是 这2种情况有重合的部分,只需要考虑,不在这1175种情况之中的 情况。
即2个格子是不靠边的,去掉其中一个格子之后,另外一个格子也是不靠边的。
这样,2个格子都不能靠边,即只能在中间的4个格子里面选取2个不同的格子,所以一共有6种情况。

所以最后的结果是11755+6=11761

文章来源: blog.csdn.net,作者:csuzhucong,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/nameofcsdn/article/details/52961854

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。