2048的背景
2014年Gabriele Cirulli利用周末的时间写2048这个游戏的程序
2048的玩法规则
每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。不断的叠加最终拼凑出2048这个数字就算成功。
Linux字符版2048的难点
- 上下左右移动的算法
- 上下左右移动之后的合并算法
- 判断游戏结束
Linux字符版2048代码实现
Linux字符版2048我们会使用 4*4 的二维数组来替换图形化2048的4*4的格子方阵。
采用键盘输入wads字符来控制上下左右移动
移动算法解析
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
我们将以上的矩阵看作是2048的格子方阵,那么如果我们现在是进行左移动,则应该是将所有的1都向左边靠拢。
变成如下形式:
1 0 0 0
1 0 0 0
1 0 0 0
1 0 0 0
分析一下,什么情况下我们才会需要去进行移动。是不是只有当左侧有空位(即有0)的时候,右侧有数值(即有1)的时候需要将1移动到0的位置。
所以我们代码可以这样写:
// 假设已经定义好了二维数组:int block[4][4]={0};
// 先变量二维数值的列数
for(int h = 0;h < 4;h++)
{
// 再遍历二维数组的列数
for(int l = 0;l < 4;l++)
{
/*
判断,我们当前遍历到的二维数组的元素是不是空位(即是否为0)
如果当前元素为空位的话,我们从当前位置向右查询,是否存在有数值位。
*/
if(block[h][l] == 0)
{
// 进入此if语句则表示当前元素为空位,我们可以从当前位置的后一位向右查询了
// 因为l是列数,表示当前列,我们要想从后一位开始,那么behind就需要+1了
for(int behind = l+1;behind < 4;behind++)
{
/*
如果behind对应的元素不为空位(即不为0)
的时候说明是一个有数值位,需要向左移动
*/
if(block[h][behind] != 0)
{
/*
如果能进这个只有两种情况
1)block[h][l] 后一位就是数值位
2)block[h][l] 和 block[h][behind] 中间的都是0
*/
// 如果进入了这里就可以把数值位和空位交换了
int temp = block[h][l];
block[h][l] = block[h][behind];
block[h][behind] = temp;
}
}
}
}
}
// 当整个循环结束了之后所有数值都会向左边聚拢
通过以上向左移动的代码,我们可以知晓向右移动的代码,只需要将h和l的初始值改为3,循环条件改为 >= 0。且behind的初始值改为 = l,循环条件改为 >= 0 即可实现向右移动。
那么向上和向下移动,也只需要。将block引用元素时,将h和l的位置进行调换,即:block[h][l]变为block[l][h]。当然block[h][behind]也需要改为block[behind][h]即可完成上下移动。
那么移动的逻辑和代码没问题了。合并要怎么解决呢?
合并算法解析
1 1 0 0
1 0 1 0
1 2 2 1
1 2 0 1
上面我们实现了移动的算法部分,那么合并的算法部分我们也可以分析一下。什么情况下才需要进行合并。
2 0 0 0
2 0 1 0
1 2 2 1
1 2 1 0
是不是只有当两个数值位相同的情况下,且数值位中间没有其他数值位的时候才可以合并。
所以合并的代码我们可以这样写:
// 假设已经定义好了二维数组:int block[4][4]={0};
// 先遍历二维数值的列数
for(int h = 0;h < 4;h++)
{
// 再遍历二维数组的列数
for(int l = 0;l < 4;l++)
{
/*
判断,我们当前遍历到的二维数组的元素是不是有数值的位置
如果是有数值的位置,我们则需要向后进行查找与其相等的值进行合并
*/
if(block[h][l] != 0)
{
// 进入此if语句则表示当前元素为不为空位,我们可以从当前位置的后一位向右查询了
// 因为l是列数,表示当前列,我们要想从后一位开始,那么behind就需要+1了
for(int behind = l+1;behind < 4;behind++)
{
/*
如果behind对应的元素不为空位(即不为0)
的时候说明是一个有数值位,对比是否和block[h][l]一样
*/
if(block[h][behind] == block[h][l])
{
/*
如果能进这个只有一种情况
也就是block[h][behind] == block[h][l]
*/
// 如果进入了这里就可以把block[h][l]和block[h][behind]进行合并
block[h][l] += block[h][behind];
block[h][behind] = 0;
}
}
}
}
}
// 当整个循环结束了之后所有就会合并相邻的数值位,或者中间是空位的相同数值位
🆗当我们知道移动和合并的原理及代码实现了之后,我们可以尝试将合并和移动写成一个函数,比如向左移动(+合并)的操作。
// 假设已经定义好了二维数组:int block[4][4]={0};
// 先遍历二维数值的列数
for(int h = 0;h < 4;h++)
{
// 再遍历二维数组的列数
for(int l = 0;l < 4;l++)
{
/*
判断,我们当前遍历到的二维数组的元素是不是有数值的位置
如果是有数值的位置,我们则需要向后进行查找与其相等的值进行合并
*/
if(block[h][l] != 0)
{
// 进入此if语句则表示当前元素为不为空位,我们可以从当前位置的后一位向右查询了
// 因为l是列数,表示当前列,我们要想从后一位开始,那么behind就需要+1了
for(int behind = l+1;behind < 4;behind++)
{
/*
如果behind对应的元素不为空位(即不为0)
的时候说明是一个有数值位,对比是否和block[h][l]一样
*/
if(block[h][behind] == block[h][l])
{
/*
如果能进这个只有一种情况
也就是block[h][behind] == block[h][l]
*/
// 如果进入了这里就可以把block[h][l]和block[h][behind]进行合并
block[h][l] += block[h][behind];
block[h][behind] = 0;
}
}
}
/*
判断,我们当前遍历到的二维数组的元素是不是有数值的位置
如果是有数值的位置,我们则需要向后进行查找与其相等的值进行合并
*/
if(block[h][l] != 0)
{
// 进入此if语句则表示当前元素为不为空位,我们可以从当前位置的后一位向右查询了
// 因为l是列数,表示当前列,我们要想从后一位开始,那么behind就需要+1了
for(int behind = l+1;behind < 4;behind++)
{
/*
如果behind对应的元素不为空位(即不为0)
的时候说明是一个有数值位,对比是否和block[h][l]一样
*/
if(block[h][behind] == block[h][l])
{
/*
如果能进这个只有一种情况
也就是block[h][behind] == block[h][l]
*/
// 如果进入了这里就可以把block[h][l]和block[h][behind]进行合并
block[h][l] += block[h][behind];
block[h][behind] = 0;
}
}
}
}
}
// 当整个循环结束了之后所有就会合并相邻的数值位,或者中间是空位的相同数值位
以上代码就可以实现向左移动的同时将相同且相邻的数值位进行合并操作了。
那么我们接下来可以来实现怎么判断是否可以继续游戏,继续游戏的前提是可以移动或者是可以合并才能继续游戏。
那么代码我们可以写成如下
// 假设已经定义好了二维数组:int block[4][4]={0};
// 先遍历二维数值的列数
for(int h = 0;h < 4;h++)
{
// 再遍历二维数组的列数
for(int l = 0;l < 4;l++)
{
/*
如果当前元素是空位,则表示可以进行移动那么游戏没有结束
*/
if(block[h][l] == 0)
{
// 游戏没有结束,需要做的操作
}
/*
如果当前元素的同列的下一行是相同的或者是同行的下一列是相同的数值
则表示可以合并,游戏没有结束。
*/
if((h+1<4&&l+1<4)(block[h][l] == block[h][l+1])||(block[h][l]==block[h+1][l]))
{
// 游戏没有结束,需要做的操作
}
}
}
// 如果当循环都没有确定游戏可以继续,那么说明没有可以移动和合并的操作了,就游戏结束了。
那么以上就是我实现字符版2048的移动和合并算法的过程,以及代码了!
这里也有我的完整代码,大家互相学习。