1. 程式人生 > >c語言課程結束小專案:2048小遊戲

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時,分三個情況移動:

  1. arr[i][k] == arr[i][j]時
  2. arr[i][k] == 0時
  3. 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;

              }

             

       }

}