<>一、原理

这里以向上移动为例,其他方向的移动类似。
因为纵向移动不会横向变化,所以可以单独拿出某一列来分析。

我们先假设一个具有代表性的例子,假设当前某列数据是【2】【0】【2】【8】(0代表空白),如下图:

再来分析每个过程:
过程①->②,移动: 从下往上,通过平移的方式把中间的所有空白格(即data[x][y]=0)消除,这有点像“删除字符串中的空格题目”。
从这个特例中可以看出,只需【2】【8】同时要向上平移1次就能把中间的所有空白格消除得到②,

当然实际情况有多种类型,例如:

(a)需要把【8】向上平移2次
(b)需要把【16】平移3次(这也是最多移3次的情况)

很明显全空或全满的情况也不需要移动
【0】 【2】
【0】 【8】
【0】 【4】
【0】 【32】

过程②->③,合并:
在这一过程中我们要反过来,从上往下找相邻的两个单元格内是否有相同的数字,图中【2】【2】【8】【0】开头的两个数字相同,则把第二个【2】加到第一个【2】,并且把第二个【2】清空,得到③【4】【0】【8】【0】


过程③->④,再次移动:
经过过程②->③得到的【4】【0】【8】【0】中间产生了一个空白格,很明显不是我们想要的结果,这时我们再次用过程①->②的方法再次向上平移(<=3次),即可消除所有空白格,从而得到某列的最终结果④【4】【8】【0】【0】。

处理向上移动的完整代码如下,处理其他方向的同理
void moveUP() { int i,j; int m=0,n=0; for(i=0;i<4;i++)//列 { //移动操作 j=0;n=0;
//j为当前判定的所在行 while(n<3 && j<3)//n为移动的次数 { if(data[j][i]==0) //若发现空白格 {
for(m=j;m<3;m++)//下方数据向上平移1格,覆盖空白格 data[m][i]=data[m+1][i]; data[3][i]=0;
//最后一行置0 n++; //移动的次数+1 }else j++; //否则:让当前判定的所在行+1 } //合并操作
for(j=0;j<3;j++)//行 if(data[j][i]==data[j+1][i] && data[j][i]!=0)//相同(且不是0)则合并
{ // 0和0不需要合并 data[j][i]=data[j][i]*2;//上面的保存合并后的数字 data[j+1][i]=0; //下面的清零 }
//移动操作 j=0;n=0; while(n<3 && j<3) { if(data[j][i]==0) { for(m=j;m<3;m++)
data[m][i]=data[m+1][i]; data[3][i]=0; n++; }else j++; } } }
<>二、程序

添加头文件、定义个4*4的数组,及其他全局变量
#include<stdio.h> #include<conio.h> #include <stdlib.h> #include<time.h> int
data[4][4]; int data_old[4][4];//数据的备份,用于比较data[][]是否变化 int opTimes=0;//记录操作的步数
数据的复制和比较(主函数中会用到)
copyData():将data[][] 中的数据备份到data_old[][]中
int compareData():比较data[][] 和data_old[][]中的数据是否相同
void copyData() { int i,j; for(i=0;i<4;i++) for(j=0;j<4;j++)
data_old[i][j]=data[i][j]; } int compareData() { int i,j; for(i=0;i<4;i++)
for(j=0;j<4;j++) if(data_old[i][j] != data[i][j])return 0; return 1; }
获取空白单元格是数量
int getEmptyNum() { int i,j,n=0; for(i=0;i<4;i++) for(j=0;j<4;j++)
if(data[i][j]==0) n++; return n; }
在空白单元格的随机位置上放置数字2
void putNewNum() { int i,j,c=0; c=1+rand()%getEmptyNum();//生成随机数,范围:[1,空白单元格数)
for(i=0;i<4;i++) for(j=0;j<4;j++) if(data[i][j]==0) { c--; if(c==0)
data[i][j]=2;//把2放入某空白单元格内 } }
获取当前分数(即二维数组中最大的数字)
int getMaxScore() { int max=0; int i,j; for(i=0;i<4;i++) for(j=0;j<4;j++) {
if(data[i][j]>max)max=data[i][j]; } return max; }
显示部分
void printLine(int L)//打印某一行 { int i=0; for(i=0;i<4;i++) { printf("│");
if(data[L][i]==0)printf("\t"); else printf("%d\t",data[L][i]); } printf("│\n");
} void printMap()//打印整个二维数组和分数信息 {
printf("┌-------┬-------┬-------┬-------┐\n"); printLine(0);
printf("├-------┼-------┼-------┼-------┤\n"); printLine(1);
printf("├-------┼-------┼-------┼-------┤\n"); printLine(2);
printf("├-------┼-------┼-------┼-------┤\n"); printLine(3);
printf("└-------┴-------┴-------┴-------┘\n"); printf("分数:%d\n",getMaxScore());
printf("操作次数:%d\n",opTimes); printf("剩余空间:%d\n",getEmptyNum()); }
处理“上”、“下”、“左”、“右”移动
void moveUP() { int i,j; int m=0,n=0; for(i=0;i<4;i++)//列 { //移动操作 j=0;n=0;
//j为当前判定的所在行 while(n<3 && j<3)//n为移动的次数 { if(data[j][i]==0) //若发现空白格 {
for(m=j;m<3;m++)//下方数据向上平移1格,覆盖空白格 data[m][i]=data[m+1][i]; data[3][i]=0;
//最后一行置0 n++; //移动的次数+1 }else j++; //否则:让当前判定的所在行+1 } //合并操作
for(j=0;j<3;j++)//行 if(data[j][i]==data[j+1][i] && data[j][i]!=0)//相同(且不是0)则合并
{ // 0和0不需要合并 data[j][i]=data[j][i]*2;//上面的保存合并后的数字 data[j+1][i]=0; //下面的清零 }
//移动操作 j=0;n=0; while(n<3 && j<3) { if(data[j][i]==0) { for(m=j;m<3;m++)
data[m][i]=data[m+1][i]; data[3][i]=0; n++; }else j++; } } } void moveDOWN() {
int i,j; int m=0,n=0; for(i=0;i<4;i++)//列 { //移动操作 j=3;n=0; while(n<3 && j>0) {
if(data[j][i]==0) { for(m=j;m>0;m--)data[m][i]=data[m-1][i]; data[0][i]=0; n++;
}else j--; } //合并操作 for(j=3;j>0;j--)//行 if(data[j][i]==data[j-1][i] &&
data[j][i]!=0)//相同(且不是0)则合并 { data[j][i]=data[j][i]*2;//下面的保存合并后的数字
data[j-1][i]=0; //上面的清零 } //移动操作 j=3;n=0; while(n<3 && j>0) { if(data[j][i]==0)
{ for(m=j;m>0;m--)data[m][i]=data[m-1][i]; data[0][i]=0; n++; }else j--; } } }
void moveLEFT() { int i,j; int m=0,n=0; for(i=0;i<4;i++) { //移动操作 j=0;n=0;
while(n<3 && j<3) { if(data[i][j]==0) {
for(m=j;m<3;m++)data[i][m]=data[i][m+1]; data[i][3]=0; n++; }else j++; } //合并操作
for(j=0;j<3;j++) if(data[i][j]==data[i][j+1] && data[i][j]!=0) {
data[i][j]=data[i][j]*2; data[i][j+1]=0; } //移动操作 j=0;n=0; while(n<3 && j<3) {
if(data[i][j]==0) { for(m=j;m<3;m++)data[i][m]=data[i][m+1]; data[i][3]=0; n++;
}else j++; } } } void moveRIGHT() { int i,j; int m=0,n=0; for(i=0;i<4;i++) {
//移动操作 j=3;n=0; while(n<3 && j>0) { if(data[i][j]==0) {
for(m=j;m>0;m--)data[i][m]=data[i][m-1]; data[i][0]=0; n++; }else j--; } //合并操作
for(j=3;j>0;j--) if(data[i][j]==data[i][j-1] && data[i][j]!=0) {
data[i][j]=data[i][j]*2; data[i][j-1]=0; } //移动操作 j=3;n=0; while(n<3 && j>0) {
if(data[i][j]==0) { for(m=j;m>0;m--)data[i][m]=data[i][m-1]; data[i][0]=0; n++;
}else j--; } } }
判断游戏是否结束
int isGameover() { int i,j; for(i=0;i<4;++i) { for(j=0;j<4;++j) {
if(data[i][j]==0)//任意一点为空,游戏继续 return 0; if(i>0) {
if(data[i-1][j]==data[i][j])//任意两个相邻的单元值相同,游戏继续 return 0; } if(j>0)
if(data[i][j-1]==data[i][j]) return 0; } } return 1; }
主函数
void main() { int ch; srand(time(NULL));//用系统当前时间设置rand()随机序列种子,保证每次运行随机序列不一样
putNewNum();//先生成两个2 putNewNum(); copyData(); //备份数组 printMap(); //显示 while(
(ch=getch())!=0x1B ) { /* 按Q键退出的死循环 */ switch(ch) { case 0xE0:
switch(ch=getch()) {//识别按下键 case 72: moveUP(); break; case 80: moveDOWN();
break; case 75: moveLEFT(); break; case 77: moveRIGHT(); break; default: break;
} if(compareData()==0)//数据不一样说明格子中的数字能且被移动了 { putNewNum(); //放置新的数字‘2’
copyData(); //备份数组的数据 opTimes++; //操作的次数+1 } system("cls"); //清屏 printMap();
//显示 if(isGameover())printf("游戏结束,按Q键退出\n");
//if(getMaxScore()>=2048){}//这里也可以判断分数>=2048,提示通关 break; default:break; } } }
<>三、效果


若有什么问题欢迎或邮件:LiHooo@qq.com