1. 程式人生 > >C語言小遊戲: 2048.c

C語言小遊戲: 2048.c

概要:2048.c是一個C語言編寫的2048遊戲,本文將詳細分析它的原始碼和實現。C語言是一種經典實用的程式語言,本身也不復雜,但是學會C語言和能夠編寫實用的程式還是有一道鴻溝的。本文試圖通過一個例子展示如何用C語言實現一個簡單但有用的程式。

一、程式簡介

本文分析的程式碼是mevdschee在GitHub上的專案2048.c,遊戲的規則和安裝說明都可以到主頁檢視,本文不再贅述。順便一提,這個程式雖然是純C編寫的,但是它適用於Linux終端,因此如果你想要看一下執行效果可能需要一個Linux.

2048.c原始碼只有一個檔案,也就是2048.c。它支援圖形和色彩,右上角顯示分數,下面是操作說明。介面整體看起來挺簡潔美觀,我們一會看一下它是怎麼做到的。

二、程式碼結構

我們先看一下程式所包含的函式,大體瞭解它的結構和功能。

程式入口和測試:

  • main (argc,argv[])
  • test ()

繪製介面相關:

  • getColor (value,color,length)
  • drawBoard (board[][])
  • setBufferedInput (enable):設定終端的行為
  • signal_callback_handler (signum)

遊戲邏輯:

  • findTarget (array[],x,stop)
  • slideArray (array[])
  • rotateBoard (board[][])
  • moveUp (board[][])
  • moveLeft (board[][])
  • moveDown (board[][])
  • moveRight (board[][])
  • findPairDown (board[][])
  • countEmpty (board[][])
  • gameEnded (board[][])
  • addRandom (board[][])
  • initBoard (board[][])

從函式的引數中可以看出,遊戲使用的主要的資料結構是一個二維陣列,在主函式中定義: uint8_t board[SIZE][SIZE] 。SIZE的值預設是4,這是2048遊戲面板的一般大小,下文直接稱為4。陣列中的元素儲存的是指數,例如如果顯示的數是1024,那麼儲存的應該是10。在初始化過程中,該陣列被填滿0.

主函式中完成一些初始化和設定工作,然後進入主迴圈。在迴圈中接受使用者的鍵盤輸入,然後呼叫相應的函式。

三、圖形繪製函式

 1 void drawBoard(uint8_t board[SIZE][SIZE]) {
 2     uint8_t x,y;
 3     char color[40], reset[] = "\033[m";
 4     printf("\033[H");
 5 
 6     printf("2048.c %17d pts\n\n",score);
 7 
 8     for (y=0;y<SIZE;y++) {
 9         for (x=0;x<SIZE;x++) {
10             getColor(board[x][y],color,40);
11             printf("%s",color);
12             printf("       ");
13             printf("%s",reset);
14         }
15         printf("\n");
16         for (x=0;x<SIZE;x++) {
17             getColor(board[x][y],color,40);
18             printf("%s",color);
19             if (board[x][y]!=0) {
20                 char s[8];
21                 snprintf(s,8,"%u",(uint32_t)1<<board[x][y]);
22                 uint8_t t = 7-strlen(s);
23                 printf("%*s%s%*s",t-t/2,"",s,t/2,"");
24             } else {
25                 printf("   ·   ");
26             }
27             printf("%s",reset);
28         }
29         printf("\n");
30         for (x=0;x<SIZE;x++) {
31             getColor(board[x][y],color,40);
32             printf("%s",color);
33             printf("       ");
34             printf("%s",reset);
35         }
36         printf("\n");
37     }
38     printf("\n");
39     printf("        ←,↑,→,↓ or q        \n");
40     printf("\033[A"); // one line up
41 }
View Code

在drawBoard函式中我們看到繪製的實現過程。函式的主體是一個for迴圈,每迴圈一次畫一行,這裡指的是Board中的一行。迴圈體中有3個小for迴圈,每個迴圈畫出終端中的一行,也就是說Board的一行是終端的3行。每個格子的尺寸是3行7列,最中間的位置是數字,如果沒有數字則輸出一個點。其他區域則用空格填充。

細心的朋友可能已經發現,外迴圈的變數是y,內迴圈的變數為x,這樣一來board[0][0]到board[3][0]表示的是第1行,board[0][1]到board[3][1]表示第2行,這種對應關係需要特別注意。

"\033m"之類的符號用於控制終端的顏色和其他一些行為。下面給出本程式中出現的用法,更多控制序列的用法可以參考這個網頁。通過輸出帶顏色的空格和字元,2048.c在終端中實現了類似圖形介面的效果。

\33[0m 關閉所有屬性 
\33[30m -- \33[37m 設定前景色 
\33[40m -- \33[47m 設定背景色 
\33[nA 游標上移n行 
\33[nB 游標下移n行 
\33[nC 游標右移n行 
\33[nD 游標左移n行 
\33[y;xH設定游標位置 
\33[2J 清屏 
\33[?25l 隱藏游標 
\33[?25h 顯示游標 

 四、遊戲邏輯

我們現在已經知道遊戲的主要資料結構,以及如何將它顯示在螢幕上,我們接下來要關注遊戲羅傑是怎麼實現的。2048遊戲本身非常簡單,其實我們只想關心劃的那一下是怎麼實現的。我們已經看到2048.c實現了moveUp、moveLeft、moveDown、moveRight四個函式,表示4個劃的方向。

moveUp函式看起來也非常簡單,它僅僅呼叫4次slideArray函式。還記得剛剛說過的二維陣列和盤面的對應規則嗎,矩陣的每一行代表的是盤面的一列,因此每次滑動一個一維陣列,實際上滑動的是一列。slideArray函式負責將陣列從高index到低index滑動,對應在螢幕上,也就是向上滑動了。

1 bool moveUp(uint8_t board[SIZE][SIZE]) {
2     bool success = false;
3     uint8_t x;
4     for (x=0;x<SIZE;x++) {
5         success |= slideArray(board[x]);
6     }
7     return success;
8 }
View Code

slideArray函式和它的輔助函式findTarget任務已經比較簡單明瞭,就不需要詳細說了。需要注意的就是在滑的時候合併的塊不能第二次合併了,例如2 2 2 2一次合併的結果是4 4,而不會是8.

其他幾個函式實現比較巧妙,作者先把盤面進行旋轉,然後再呼叫這個moveUp函式實現。作者通過rotateBoard函式把這個4x4的矩陣旋轉90度。陣列的下標可以通過建立座標系得到。

 1 void rotateBoard(uint8_t board[SIZE][SIZE]) {
 2     uint8_t i,j,n=SIZE;
 3     uint8_t tmp;
 4     for (i=0; i<n/2; i++) {
 5         for (j=i; j<n-i-1; j++) {
 6             tmp = board[i][j];
 7             board[i][j] = board[j][n-i-1];
 8             board[j][n-i-1] = board[n-i-1][n-j-1];
 9             board[n-i-1][n-j-1] = board[n-j-1][i];
10             board[n-j-1][i] = tmp;
11         }
12     }
13 }
View Code

瞭解了這些資訊,再看其他的函式比如countEmpty、addRandom等就非常簡單了,大家直接去看程式碼就可以了。

五、總結

2048.c這個小遊戲雖然只有400多行,但復現了2048遊戲的精髓。而且程式以純C語言實現,沒有使用ncurses之類的第三方庫,得到了很不錯的效果。實現的過程也有一些精巧的地方,例如如何把問題化繁為簡的,如何避免多次編寫move函式。其實2048.c不僅可以拿來閱讀,無聊的時候玩一局也是相當不錯的。