C語言編寫2048小遊戲

一、原理

這裏以向上移動爲例,其他方向的移動類似。
因爲縱向移動不會橫向變化,所以可以單獨拿出某一列來分析。

我們先假設一個具有代表性的例子,假設當前某列數據是【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;
		  }	
	 }
}

三、效果

在這裏插入圖片描述
若有什麼問題歡迎或郵件:[email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章