//全文复制,可直接使用,具体的解析看注释,函数都是使用的比较通俗易懂的写法,不可直接抄袭
#include
#include
#include
#include
#include
void printmap();//输出整个二维数组和分数信息
void printline(int i);//输出某一行
void putnewnum();//在空白单元格内随机放置2或4
int getemptynum();//获取空白单元格数量
void copydata();//数据复制
int comparedata();//判断数据前后是否一致
int getmax();//获取当前二维数组中最大的数字
int gameover();//判断游戏是否结束
void moveup();//上移
void movedown();//下移
void moveleft();//左移
void moveright();//右移
int color(int num);//num为每一种颜色所代表的数字,范围是0~15
int setcolor(int n);//给不同数字附不同颜色
//全局变量
int data[4][4];
int old_data[4][4];
int count=0;//次数
int sum=0;//分数
//显示部分
void printmap()//输出整个二维数组和分数信息
{
printf("┏━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┓n");
printline(0);
printf("┣━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━┫n");
printline(1);
printf("┣━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━┫n");
printline(2);
printf("┣━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━━┫n");
printline(3);
printf("┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━┛n");
printf("最大值:%dn",getmax());
printf("游戏分数:%dn",sum);
printf("操作次数:%dn",count);
printf("剩余空间:%dn",getemptynum());
printf("退出游戏:qn");
if(gameover()==1)printf("很遗憾游戏结束!按q键退出");
if(getmax()>=2048)printf("恭喜你通关了!n");
}
void printline(int i)//行
{
int j;//列
for(j=0;j<4;j++){
printf("┃");
if(data[i][j]==0)printf(" ");
else {
setcolor(data[i][j]);
printf(" %4d ",data[i][j]);
color(7);//制表变回白色
}
}
printf("┃n");
}
int getemptynum()//获取空白单元格数量n
{
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;
}
void putnewnum()//在空白单元格内随机放置2或4
{
int i,j,a,b;
a=1+rand()%getemptynum();//生成随机数,范围:[1,空白单元格数];
b=(rand()%2+1)*2;//b为2或4
for(i=0;i<4;i++){
for(j=0;j<4;j++){
if(data[i][j]==0){
a--;
if(a==0)data[i][j]=b;//把2或4放入某个空白单元格内
}
}
}
}
void copydata()
{
int i,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
old_data[i][j]=data[i][j];
}
int comparedata()
{
int i,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
if(old_data[i][j] != data[i][j])return 0;
return 1;
}
int getmax()//获取当前二维数组中最大的数字
{
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 moveup()
{
int i,j,m,n;
for(i=0;i<4;i++)//列
{
//移动操作
j=0;n=0; //j为当前判定的所在行
while(n<3&&j<3)//n为移动的次数
{
if(data[j][i]==0) //当数据为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;//上面的保存合并后的数字
sum=sum+data[j][i];
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,m,n;
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)
{
data[j][i]=data[j][i]*2;
sum=sum+data[j][i];
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,m,n;
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;
sum=sum+data[i][j];
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,m,n;
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;
sum=sum+data[i][j];
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 color(int num)//num为每一种颜色所代表的数字,范围是0~15
{
//SetConsoleTextAttribute是设置控制台窗口字体颜色和背景色的函数
//GetStdHandle是获得输入、输出或错误的屏幕缓冲区的句柄
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),num);
return 0;
}
int setcolor(int n)//给不同数字附不同颜色
{
switch(n){
case 2:color(7);break;//白色
case 4:color(14);break;//淡黄色
case 8:color(6);break;//黄色
case 16:color(2);break;//绿色
case 32:color(11);break;//淡浅绿色
case 64:color(9);break;//淡蓝色
case 128:color(1);break;//蓝色
case 256:color(13);break;//淡紫色
case 512:color(5);break;//紫色
case 1024:color(8);break;//灰色
case 2048:color(4);break;//红色
default: break;
}
return 0;
}
int gameover()
{
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 start()
{
int x;
putnewnum();//生成一个2或4
printmap();//显示
//getch()用于瞬时读取按下的键,不等待回车和换行键的到来
while((x=getch())!='q'){//按q退出游戏
switch(x){
case 0xE0://0xE0是方向键的开头,上(0xE0,72)、下(0xE0,80)、左(0xE0,75)、右(0xE0,77),按下方向键,getch()会读到2字节
switch(x=getch()){
case 72:moveup();break;
case 80:movedown();break;
case 75:moveleft();break;
case 77:moveright();break;
default: break;
}
if(comparedata()==0)//数据不同
{
putnewnum(); //放置新的数字
copydata(); //备份数组的数据
count++; //操作的次数+1
}
system("cls");//清屏
printmap();//显示
default:break;
}
}
}
int main()
{
srand(time(NULL));//NULL代表空值,用系统当前时间设置rand()随机序列种子,保证每次运行随机序列不一样
start();
return 0;
}



