c語言課程結束小專案:2048小遊戲
《C語言程式設計基礎》學習完畢,完成第一個專案:2048小遊戲。
//思考:程式設計時,要先想整個框架還是先想每個函式的演算法。
//思考:每一個函式要不要傳引數(即陣列)
//思考:每一個函式之間的聯絡是什麼,誰呼叫誰…這裡很容易搞得頭暈,越想越覺得複雜,便不想完成專案了。
將遊戲分為幾個步驟,拆分來完成每一個函式。
一、定義一個4*4的二維陣列arr[4][4],初始化為0;
二、列印介面函式:Show_game();將陣列打印出來;
三、隨機函式:Add_rand():在陣列中隨機選出一個元素賦值,並且賦值要隨機2或者4。所以就要定義i,j兩個陣列下表變數,將隨機值%4,(求4的餘數)就會得到0、1、2、3,將隨機值賦值給i、j。還需要隨機的2和4,可以在定義一個隨機數m,m%3,得到0、1、2,在m大於1的情況下將m賦值給陣列元素arr[i][j]。
這裡的arr[i][j] = (m>1) ?2:4;這句程式碼,一句解決,厲害厲害是我最開始沒有想到的。都是c語言中最簡單的語句,但自己在實際程式設計中用得少,下意識的只會用if語句去判斷,導致程式碼可讀性差。
srand((unsigned) time (NULL));//隨機種子。Srand需要用到頭文<stdlib.h>,
這句用到time()函式,所以需要用到標頭檔案<time.h>
關於srand()函式:
3.1設定隨機的起點
3.2Void srand(unsigned int seed);
3.3引數seed,隨機生成的種子。
3.4用法:srand和rand()配合使用產生偽隨機數序列。rand函式在產生隨機數前,需要系統提供的生成偽隨機數序列的種子,rand根據這個種子的值產生一系列隨機數。如果系統提供的種子沒有變化,每次呼叫rand函式生成的偽隨機數序列都是一樣的。srand(unsigned seed)通過引數seed改變系統提供的種子值,從而可以使得每次呼叫rand函式生成的偽隨機數序列不同,從而實現真正意義上的“隨機”。通常可以利用系統時間來改變系統的種子值,即srand(time(NULL)),可以為rand函式提供不同的種子值,進而產生不同的隨機數序列
四、遊戲初始化函式InitGame():遊戲開始第一個隨機值要定為2
五、*核心演算法*上下左右移動函式:Move_****()
我們這裡分析左移(其他移動方法幾乎相同)
┏━┳━┳━┳━┓i
┣━╋━╋━╋━┫i
┣━╋━╋━╋━┫i
┣━╋━╋━╋━┫i
┗━┻━┻━┻━┛
K J J J
左移的結果:是將每行的元素移動到最左邊。
將k列設定為當前列,遍歷陣列時:i++;j++
找到arr[i][j] == 0時,不管
找到arr[i][j]不為0時,分三個情況移動:
- arr[i][k] == arr[i][j]時
- arr[i][k] == 0時
- arr[i][k] != 0 && arr[i][k] != arr[i][j] 又分兩種情況
a.j、k挨著
b.j、k不挨著
右移同理,將k列的當前放在最右邊:
┏━┳━┳━┳━┓i
┣━╋━╋━╋━┫i
┣━╋━╋━╋━┫i
┣━╋━╋━╋━┫i
┗━┻━┻━┻━┛
J J J K
上移動:
┏━┳━┳━┳━┓k
┣━╋━╋━╋━┫j
┣━╋━╋━╋━┫j
┣━╋━╋━╋━┫j
┗━┻━┻━┻━┛
i i i i
向下移動:
┏━┳━┳━┳━┓j
┣━╋━╋━╋━┫j
┣━╋━╋━╋━┫j
┣━╋━╋━╋━┫k
┗━┻━┻━┻━┛
i i i i
每一種情況都相似,所以函式只是在遍歷是將條件略微根據k、j、i修改。
六、判斷遊戲是否結束函式:GameOver(). 遊戲結束有兩個條件:1.沒有一個元素為0 2.沒有相鄰的元素相等。
條件一:定義一個變數n,初始化為0;寫了一個bool函式,遍歷每一個元素,如果陣列中有一個0元素,n++;判斷n的值,若n == 0,返回1,說明條件該成立。(這個寫法我一開始沒有想到用一個計數器n)。
條件二:同樣的方法,定義變數n,判斷陣列相鄰位置是否一樣n++。若n == 0則沒有相鄰位置元素一樣,返回1。
最後GameOver函式中判斷條件一和二,一&&二,則返回1,表示遊戲結束。
(這裡我寫複雜了,用一個bool函式就可以實現,只需要遍歷一次,找出n和m的值,if(n==0 &&m==0),則返回true,表示遊戲結束)
七、主函式中,接受鍵盤字元getchar,用到switch語句寫接收到的case
八、用到清屏系統命令system(“cls”),需要加標頭檔案<windows.h>。
九、getchar()和getch()是有區別的。它們都是輸入單個字元用的。getch()使用者不需要按回車,是讀取按鍵值用的,並且需要標頭檔案conio.h。按鍵輸入時,螢幕沒有反應。getchar()需要回車,是在輸入裝置中得到資料用的。按鍵輸入時,螢幕顯示輸入的字元.
十、上下左右箭頭:72 80 75 77.就是數字,不是字元,寫成【case ‘75’: 】就錯了。這也導致我開始從鍵盤怎麼也讀不上箭頭符號。
以下原始碼:
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
int arr[4][4] = {0};//遊戲抽象為二維陣列
int if_need_add_rand;//是否生成隨機數 1表示生成 0不生成
void ShowArr()//和下例的函式功能一樣,但是下面例子加了邊框,遊戲觀賞性高
{
for(int i = 0;i < 4;i++)
{
for(int j = 0;j < 4;j++)
{
printf("%5d",arr[i][j]);
}
printf("\n\n");
}
}
void ShowGame()//遊戲介面(參考網上,加了邊框,真的好看)
{
printf("\n\n *****2048*****\n");
printf("┏━━━┳━━━┳━━━┳━━━┓\n");
for(int i = 0;i<4;i++)
{
printf("┃");
for(int j = 0;j < 4;j++)
{
//二維陣列元素為0
if(arr[i][j] == 0)
{
printf(" ┃");//4個空格
}
else
{
printf("%3d┃",arr[i][j]);
}
}
if(i<3)
{
printf("\n┣━━━╋━━━╋━━━╋━━━┫\n");
}
else
{
printf("\n┗━━━┻━━━┻━━━┻━━━┛\n");
printf("****WELCOME****\n\n\n");
}
}
}
void Add_rand()//在隨機陣列中放入一個隨機的2或者4,2的概率要大。
{
srand((unsigned) time (NULL));//隨機種子
int i;
int j;
int m = rand() % 3;//m可能的結果為0、1、2。
while(1)
{
i = rand() % 4;
j = rand() % 4;
if(arr[i][j] == 0)
{
arr[i][j] = (m>1) ? 4 :2;//m大於1為4,否則為2。這樣2的概率為33.3%,4的概率為66.7%
break;//生成一個數,則退出
}
else
{
continue;//否則執行下一次迴圈,重新生成隨機值。
}
}
}
void InitGame()//初始化:遊戲開始有兩個數字
{
if_need_add_rand = 1;//需要生成一個隨機數
int i = rand()%4;
int j = rand()%4;
arr[i][j] = 2;//第一個數字為2
}
void Move_left()//左移動
{
for(int i = 0;i < 4;i++)
{
for(int j = 1,k = 0;j < 4;j++)
{
//先找到k項後面不為0的項
if(arr[i][j] > 0)//分3個情況
{
//1.k項 == j項
if(arr[i][k] == arr[i][j])
{
arr[i][k++] *= 2;//相當於arr[i][k++] <<= 1這樣效率更高
arr[i][j] = 0;
if_need_add_rand = 1;
}
//2.k項 == 0
else if(arr[i][k] == 0)
{
arr[i][k] = arr[i][j];
arr[i][j] = 0;
if_need_add_rand = 1;
}
//3.k項 != 0;j項 != k項
else//分2種情況
{
arr[i][++k] = arr[i][j];
if(j != k)
{
arr[i][j] = 0;
if_need_add_rand = 1;
}
}
}
}
}
}
void Move_right()//右移
{
for(int i = 0;i < 4;i++)
{
for(int j = 2,k = 3;j >= 0;j--)
{
//先找到k項前面不為0的項
if(arr[i][j] > 0)//分3個情況
{
//1.k項 == j項
if(arr[i][k] == arr[i][j])
{
arr[i][k--] *= 2;//相當於arr[i][k--] <<= 1這樣效率更高
arr[i][j] = 0;
if_need_add_rand = 1;
}
//2.k項 == 0
else if(arr[i][k] == 0)
{
arr[i][k] = arr[i][j];
arr[i][j] = 0;
if_need_add_rand = 1;
}
//3.k項 != 0;j項 != k項
else//分2種情況
{
arr[i][--k] = arr[i][j];
if(j != k)
{
arr[i][j] = 0;
if_need_add_rand = 1;
}
}
}
}
}
}
void Move_up()//上移
{
for(int i = 0;i < 4;i++)//列
{
for(int j = 1,k = 0;j < 4;j++)//行。
{
//先找到k項後面不為0的項
if(arr[j][i] > 0)//分3個情況
{
//1.k項 == j項
if(arr[k][i] == arr[j][i])
{
arr[k++][i] *= 2;
arr[j][i] = 0;
if_need_add_rand = 1;
}
//2.k項 == 0
else if(arr[k][i] == 0)
{
arr[k][i] = arr[j][i];
arr[j][i] = 0;
if_need_add_rand = 1;
}
//3.k項 != 0;j項 != k項
else//分2種情況
{
arr[++k][i] = arr[j][i];
if(j != k)
{
arr[j][i] = 0;
if_need_add_rand = 1;
}
}
}
}
}
}
void Move_down()//下移
{
for(int i = 0;i < 4;i++)//列
{
for(int j = 2,k = 3;j >= 0;j--)//行
{
//先找到k項後面不為0的項
if(arr[j][i] > 0)//分3個情況
{
//1.k項 == j項
if(arr[k][i] == arr[j][i])
{
arr[k--][i] *= 2;
arr[j][i] = 0;
if_need_add_rand = 1;
}
//2.k項 == 0
else if(arr[k][i] == 0)
{
arr[k][i] = arr[j][i];
arr[j][i] = 0;
if_need_add_rand = 1;
}
//3.k項 != 0;j項 != k項
else//分2種情況
{
arr[--k][i] = arr[j][i];
if(j != k)
{
arr[j][i] = 0;
if_need_add_rand = 1;
}
}
}
}
}
}
bool TiaoJian_1()//遊戲結束條件1:沒有格子為0,遊戲結束,return 1
{
int n = 0;
for(int i = 0;i < 4;i++)
{
for(int j = 0;j < 4;j++)
{
if(arr[i][j] == 0)
{
n++;//有格子為零,遊戲繼續
}
}
}
if( n == 0)//遊戲結束
{
return true;
}
else//遊戲繼續
{
return false;
}
}
bool TiaoJian_2()//2.相鄰(上下,左右)的值不相等,則遊戲結束,return 1
{
int n = 0;
for(int i = 0;i < 4;i++)
{
for(int j = 0;j+1 < 4;j++)
{
if((arr[i][j] == arr[i][j+1]) || (arr[j][i] == arr[j+1][i]))
{
n++;
}
}
}
if(n == 0)//表示沒有相等,結束
{
return 1;
}
else
{
return 0;
}
}
bool GameOver()//判斷遊戲:true表示結束。
{
if(TiaoJian_1() && TiaoJian_2())
{
return true;
}
else
{
return false;
}
}
int main()
{
InitGame();
Add_rand();
ShowGame();
//ShowArr();
while(1)
{
int ch;
if_need_add_rand = 0;
switch(ch = getch())//接收字元函式
{
case 'a':
case 'A':
case '4':
case 75:
Move_left();
break;
case 'd':
case'D':
case '6':
case 77:
Move_right();
break;
case'w':
case 'W':
case '8':
case 72:
Move_up();
break;
case 's':
case 'S':
case'2':
case 80:
Move_down();
break;
default:
break;
}
if(if_need_add_rand == 1)
{
Add_rand();
system("cls");//清屏系統命令
ShowGame();
//ShowArr();
}
if(GameOver())
{
printf(" ***遊戲結束***\n");
break;
}
}
}