2048小游戲代碼解析 C語言版


2048小游戲,也算是風靡一時的益智游戲。其背后實現的邏輯比較簡單,代碼量不算多,而且趣味性強,適合作為有語言基礎的童鞋來加強編程訓練。本篇分析2048小游戲的C語言實現代碼。

前言

游戲截圖:

 游戲實現原理:

  1. 使用終端圖形庫文件curses繪制終端里的圖形。
  2. 使用一個二維數組保存4 x 4 空格中的變量。
  3. 鍵盤輸入控制移動,經過邏輯判斷,二維數組數據變化。
  4. 二維數組數據變化后交給圖形函數顯示出來。



庫文件curses介紹:

curses是一種終端圖形繪制庫,利用curses可以在終端中繪制多種圖形。

簡單demo

#include <stdio.h> #include <curses.h>

int main() { initscr(); border(0,0,0,0,0,0,0,0); move(5,15); printw("%s","hello world"); refresh(); char ch=getch(); endwin(); return 0; }



編譯:gcc curses_demo.c -lcurses

 

深入學習請查詢相關資料。

 

2048實現代碼分析

根據2048實現原理,代碼要實現的主要有三件事:

  1. 圖形繪制
  2. 游戲邏輯操作
  3. 圖形加載邏輯結果

主程序代碼如下:

2048 C語言版代碼分析 //-------------頭文件--------------------// #include <stdio.h> #include <stdlib.h> #include <curses.h> #include <time.h> #include <unistd.h> #include <signal.h>
//--------------------------------------//

//------------------全局變量------------------------------- // 游戲主界面是一個 4*4 的 16 宮格,使用二維數組進行表示,用 0 表示空格
int a[4][4] = {0}; // 16 宮格中空格的個數
int empty; // 涉及到新產生的數字的位置的兩個變量
int old_y, old_x; //所有的C語言代碼就是在這三個函數中
int main() { //初始化函數
 init(); //游戲運行時函數
 play(); //結束函數,清屏 //endwin()來關閉 curses 模式.
 endwin(); return 0; }

 

 

main()函數代碼分析

頭文件+全局變量

頭文件中包含的庫文件如下:

  • <stdio.h> 標准輸入輸出
  • <stdlib.h> 設計到內存操作函數
  • <curses.h> 繪制圖形庫文件
  • <time.h> 時間函數
  • <unistd.h> 睡眠函數庫文件
  • <signal.h> 信號相關操作庫文件

 

主函數代碼

主函數中共有三個子函數,其中復雜的為前兩個,第三個為curses關閉的函數,沒有任何邏輯。init()函數的作用是游戲准備,繪圖函數等。play()函數是游戲運行的主要邏輯函數。

下面分別分析init()函數和play()函數的實現。

 

init()函數

  • 必要的curses庫開啟函數
  • 設置游戲開始時空格數為15
  • 產生兩個隨機數x、y作為第一個數字的位置下標
  • 調用draw()函數來繪制圖形

所有的圖形繪制都是依靠draw()函數來完成的。下面來分析該函數。

 

draw()函數

  • 使用兩個雙層循環繪制4*4窗格。
  • 調用draw_one()函數將全局變量數組a[][]中的數據顯示在空格中。

 

 draw()_one()函數

  • 將a[][]中的數字轉換成c[]中的字符
  • 循環c[]輸出字符,並顯示在圖形中

 

 

play()函數代碼

play函數是整個游戲的主題函數,負責移動,判斷等邏輯。游戲的邏輯主要分兩個步驟來完成,以向左移動為例來說明:

 

1、循環16個窗口,從(0,0)開始,如果(0,1)和(0,0)相同,則兩個相加,並將(0,1)置為0。然后繼續判斷(0,2)和現在的(0,0)是否相同,相同則相加到(0,0)中。直到(0,0)右邊都判斷完成結束。

2、以上步驟只是完成一個窗口(0,0)的判斷,使用循環將16個都完成相加。

3、所有的完成相加之后可能的情況是下面這樣的,還須將所有的數字都移到左邊。以圖中(1,1)為例,循環到(1,1)時,判斷左邊的窗口是否為0,如果為0,則向左移動一個,繼續判斷左邊是否為0,直到移動到最左邊。

 

 

具體的代碼分析:

  • 使用一個死循環while(1)來循環接收輸入,判斷等。
  • ch=getch()接收輸入,switch case來判斷輸入的內容。

 

具體分析其中一個,以向左移動為例:

  • 循環判斷每一個空格中的數字
  • 如果為0,則繼續下一次循環;如果不為0,則判斷其右邊的格子中數字是否相同。
  • 如果右邊格子中的數字和當前格子中相同,那么相加,繼續判斷右邊的右邊直到數組到底。
  • 所有相同的數字都相加結束之后,將所有的數字整體向左移動。
case 68:    // 左移方向鍵
    for(y = 0; y < 4; y++) for(x = 0; x < 4; ) { //如果a[y][x]中的值為0,則跳過該次循環
            if(a[y][x] == 0) { x++; continue; } else //如果a[y][x]中的值不為0,則
 { //循環的思想是,以傳入的坐標為基點,向右邊搜索,如果右邊的和基點相等,則將兩個點相加 //相加之后繼續向后搜索,還有相等的則繼續相加。直到超出范圍而退出。 //下一次循環繼續搜索。


                for(i = x + 1; i < 4; i++) { //判斷a[y][x+1],即該空格的右邊的對應位置是否為0,如果為0則退出本次循環
                    if(a[y][i] == 0) { continue; } else //如果不為0,則判斷是否相等,如果相等則相加,然后將后面一個置0,退出循環
 { if(a[y][x] == a[y][i]) { a[y][x] += a[y][i]; a[y][i] = 0; //當有相加產生之后,會多出一個空格。這里體現空格的減少。
                            empty++; break; } else { break; } } } //當該次循環退出之前,只是完成了該基點的一次搜索,所以要向右邊移動一個,繼續下一次的業務。
                x = i; } } //當所有的相加都完成之后,要將數據整體向左移動,體現在數據全都左移。 //將所有的數值向左移
    for(y = 0; y < 4; y++) for(x = 0; x < 4; x++) { //如果該位為0,則不動,因為需要移動的是有數值的項
            if(a[y][x] == 0) { continue; } else //如果該為不為0,則向左移動一位,前提是判斷該位的左邊是空位
            {    //如果前面是左邊是空位,則移動到空位上,繼續判斷左邊的左邊是不是空位,如果是則繼續移動,直到所有的都移動完成。 
                for(i = x; (i > 0) && (a[y][i-1] == 0); i--) { a[y][i-1] = a[y][i]; a[y][i] = 0; //一旦移動過,就將move置1,方便后面空格中生成新的數據
                    move = 1; } } } break;

當switch case結束之后,要完成當前狀態的檢驗:

  • 判斷空格的剩余數,如果剩余小於等於0,則游戲結束
  • 如果不等於0,則判斷空格是否等於上次或者有移動,兩者有一個成立,就要生成新的數字
  • 產生新數字的下標a[x][y]中的x、y,直到該x、y對應的數組a[][]上為空值時才成功
  • 判斷該x、y是否為最佳出現地點
  • 生成不等於0或者2的時候退出。(不明白的這里的邏輯)

在第四步中使用了函數cnt_value()來判斷下一個數字出現的最佳地點,后面單獨分析。

 

cnt_value()

該函數主要選擇出下一個數字的最佳出現地點,原則就是周圍的空格數最多。

  • 以上一步驟產生的x、y為起點,算出其左右的空格數。
  • 循環所有的空格,統計其周圍8個空格的空格數。如果有空格數大於起點的,則生成新的數字。

cnt_one()為具體計算某一個空格周圍8個格子的空格數的函數。邏輯簡單,不做具體分析。

 

 cnt_one()

 

整個邏輯都很正常,除了這最后的cnt_value()函數,這里要找出四周空格最多的一個窗口,而直接將隨機產生的窗口作為周圍空格最多的就不合理了(max並非最多)。后面只要出現空格數大於該窗口的就選為下一個數字出現的窗格,實際獲取的並未最多空格的下標。應該是統計出4*4窗格中周圍空格數最多的,然后在最多的窗口處出現下一個數字。不知道是我分析有誤還是代碼如此?如果有讀者看出還請指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM