一、游戲介紹
二、游戲規(guī)則
三、核心算法
1、方塊的移動和合并
代碼語言:JavaScript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
主要思想:把游戲數(shù)字面板抽象成4行4列的二維數(shù)組a[4][4],值為0的位置表示空方塊,其他表示對應(yīng)數(shù)字方塊。把每一行同等對待,只研究一行的移動和合并算法,然后可以通過遍歷行來實現(xiàn)所有行的移動合并算法。在一行中,用b[4]表示一行的一維數(shù)組,使用兩個下標(biāo)變量來遍歷列項,這里使用j和k,其中j總在k的后面,用來尋找k項后面第一個不為0的數(shù)字,而k項用于表示當(dāng)前待比較的項,總是和j項之間隔著若干個數(shù)字0,或者干脆緊挨著。不失一般性,考慮往左滑動時,初始情況下j等于1,而k等于0,接著判斷j項數(shù)字是否大于0,若是,則判斷j項和k項數(shù)字的關(guān)系,分成3種情況處理,分別是(合并)P1: b[k]==b[j],(移動)P2: b[k]==0和(碰撞)P3: b[k]!=0且b[k]!=b[j];若否,則j自加1,然后繼續(xù)尋找k項后面第一個不為0的數(shù)字。 其中P1,P2和P3分別對應(yīng)如下: (合并)P1:b[k]==b[j],則b[k] = 2 * b[k](說明兩數(shù)合并了),且b[j] = 0(合并之后要將殘留的j項值清零),接著k自加1,然后進(jìn)行下一次循環(huán)。 (移動)P2:b[k]==0,則表示b[j]之前全是空格子,此時直接移動b[j]到k的位置,也就是b[k] = b[j],然后b[j] = 0(移動后將殘留的j項值清零),接著k值不變,然后進(jìn)行下一次循環(huán)。 (碰撞)P3:b[k]!=0且b[k]!=b[j],則表示兩數(shù)不相等且都不為0,此時將兩數(shù)靠在一起,也就是b[k+1] = b[j]。接著分兩種小情況,若j!=k+1,則b[j] = 0(移動后將殘留的j項值清零);若否,則表示兩數(shù)原先就靠在一起,則不進(jìn)行特殊處理(相當(dāng)于未移動)。接著k自加1,然后進(jìn)行下一次循環(huán)。 舉一個P1的例子,流程表示如下: 一行內(nèi)移動合并算法描述如下(此例為左移情況,其他方向與之類似,區(qū)別僅僅是遍歷二維數(shù)組的行項和列項的方式):
代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
int j, k;for (j = 1, k = 0; j 0) { /* 找出k后面第一個不為空的項,下標(biāo)為j,之后分三種情況 */ if (b[k] == b[j]) { /* P1情況,合并 */ b[k] = 2 * b[k]; b[j] = 0; k = k + 1; } else if (b[k] == 0) { /* P2情況,移動 */ b[k] = b[j]; b[j] = 0; } else { /* P3情況,碰撞 */ b[k + 1] = b[j]; if (j != k + 1) { /* 原先兩數(shù)不挨著 */ b[j] = 0; } k = k + 1; } }}
2、判斷游戲是否結(jié)束
立即學(xué)習(xí)“C語言免費(fèi)學(xué)習(xí)筆記(深入)”;
代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
核心思想:遍歷二維數(shù)組,看是否存在橫向和縱向兩個相鄰的元素相等,若存在,則游戲不結(jié)束,若不存在,則游戲結(jié)束。 算法代碼描述如下(board表示真正的游戲源碼中使用的二維數(shù)組):
代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
/* 檢查游戲是否結(jié)束 函數(shù)定義 */void check_game_over() { int i; for (i = 0; i <p>3、生成隨機(jī)數(shù)</p>代碼語言:javascript<i class="icon-code"></i>代碼運(yùn)行次數(shù):<!-- -->0<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16" fill="none"><path d="M6.66666 10.9999L10.6667 7.99992L6.66666 4.99992V10.9999ZM7.99999 1.33325C4.31999 1.33325 1.33333 4.31992 1.33333 7.99992C1.33333 11.6799 4.31999 14.6666 7.99999 14.6666C11.68 14.6666 14.6667 11.6799 14.6667 7.99992C14.6667 4.31992 11.68 1.33325 7.99999 1.33325ZM7.99999 13.3333C5.05999 13.3333 2.66666 10.9399 2.66666 7.99992C2.66666 5.05992 5.05999 2.66659 7.99999 2.66659C10.94 2.66659 13.3333 5.05992 13.3333 7.99992C13.3333 10.9399 10.94 13.3333 7.99999 13.3333Z" fill="currentcolor"></path></svg>運(yùn)行<svg width="16" height="16" viewbox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 15.5V3.5H14.5V15.5H4.5ZM12.5 5.5H6.5V13.5H12.5V5.5ZM9.5 2.5H3.5V12.5H1.5V0.5H11.5V2.5H9.5Z" fill="currentcolor"></path></svg>復(fù)制<pre class="prism-token token line-numbers javascript"> 核心思想:根據(jù)生成的隨機(jī)數(shù),對一定的值進(jìn)行取模,達(dá)到生成一定概率的數(shù)。在本游戲中,設(shè)定4出現(xiàn)的概率為1/10,于是可以利用系統(tǒng)提供的隨機(jī)數(shù)函數(shù)生成一個數(shù),然后對10取余,得到的數(shù)若大于0則在游戲面板空格處生成一個2,若余數(shù)等于0,則生成4。在選擇將在哪一個空格出生成數(shù)的時候,也是根據(jù)系統(tǒng)提供的隨機(jī)函數(shù)生成一個數(shù),然后對空格數(shù)取余,然后在第余數(shù)個空格出生成數(shù)字。 算法代碼描述如下(board表示真正的游戲源碼中使用的二維數(shù)組):
代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
/* 生成隨機(jī)數(shù) 函數(shù)定義 */void add_rand_num() { srand((unsigned int) time(0)); int n = rand() % get_null_count(); /* 確定在何處空位置生成隨機(jī)數(shù) */ int i; for (i = 0; i <p>4、繪制界面</p>代碼語言:javascript<i class="icon-code"></i>代碼運(yùn)行次數(shù):<!-- -->0<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16" fill="none"><path d="M6.66666 10.9999L10.6667 7.99992L6.66666 4.99992V10.9999ZM7.99999 1.33325C4.31999 1.33325 1.33333 4.31992 1.33333 7.99992C1.33333 11.6799 4.31999 14.6666 7.99999 14.6666C11.68 14.6666 14.6667 11.6799 14.6667 7.99992C14.6667 4.31992 11.68 1.33325 7.99999 1.33325ZM7.99999 13.3333C5.05999 13.3333 2.66666 10.9399 2.66666 7.99992C2.66666 5.05992 5.05999 2.66659 7.99999 2.66659C10.94 2.66659 13.3333 5.05992 13.3333 7.99992C13.3333 10.9399 10.94 13.3333 7.99999 13.3333Z" fill="currentcolor"></path></svg>運(yùn)行<svg width="16" height="16" viewbox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 15.5V3.5H14.5V15.5H4.5ZM12.5 5.5H6.5V13.5H12.5V5.5ZM9.5 2.5H3.5V12.5H1.5V0.5H11.5V2.5H9.5Z" fill="currentcolor"></path></svg>復(fù)制<pre class="prism-token token line-numbers javascript"> 核心思想:利用系統(tǒng)提供的控制臺界面清屏功能,達(dá)到刷新界面的效果,利用控制制表符位置,達(dá)到繪制游戲數(shù)字面板的效果。 由于繪制界面不算是本游戲的本質(zhì),且代碼段相對較長,所以算法描述在這里省略,讀者可以參考完整源代碼。
5、計算得分
代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
核心思想:兩塊帶數(shù)字的方格合并后的數(shù)字為合并的得分,一次上下左右移動后游戲面板上所有合并的得分總和為一次移動的得分,多次移動的得分進(jìn)行累加作為當(dāng)前總得分。 如果當(dāng)前總得分(SCORE)超過最高分(BEST),則最高分被改寫為當(dāng)前總得分,并存儲下來,下次啟動游戲時會自動載入本機(jī)存儲的最高分。
四、完整源代碼如下,敬請讀者批評指正:
代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制
#include <time.h> /* 包含設(shè)定隨機(jī)數(shù)種子所需要的time()函數(shù) */#include <stdio.h> /* 包含C的IO讀寫功能 */#include <stdlib.h> /* 包含C標(biāo)準(zhǔn)庫的功能 */#ifdef _WIN32/* 包含windows平臺相關(guān)函數(shù),包括控制臺界面清屏及光標(biāo)設(shè)定等功能 */#include <conio.h>#include <windows.h>#include <io.h>#include <direct.h>#include <shlobj.h>#else/* 包含linux平臺相關(guān)函數(shù),包括控制臺界面清屏及光標(biāo)設(shè)定等功能 */#include <termio.h>#include <unistd.h>#include <bits>#include <signal.h>#define KEY_CODE_UP 0x41#define KEY_CODE_DOWN 0x42#define KEY_CODE_LEFT 0x44#define KEY_CODE_RIGHT 0x43#define KEY_CODE_QUIT 0x71struct termios old_config; /* linux下終端屬性配置備份 */#endifstatic char config_path[4096] = {0}; /* 配置文件路徑 */static void init_game(); /* 初始化游戲 */static void loop_game(); /* 游戲循環(huán) */static void reset_game(); /* 重置游戲 */static void release_game(int signal); /* 釋放游戲 */static int read_keyboard();static void move_left(); /* 左移 */static void move_right(); /* 右移 */static void move_up(); /* 上移 */static void move_down(); /* 下移 */static void add_rand_num(); /* 生成隨機(jī)數(shù),本程序中僅生成2或4,概率之比設(shè)為9:1 */static void check_game_over(); /* 檢測是否輸?shù)粲螒颍O(shè)定游戲結(jié)束標(biāo)志 */static int get_null_count(); /* 獲取游戲面板上空位置數(shù)量 */static void clear_screen(); /* 清屏 */static void refresh_show(); /* 刷新界面顯示 */static int board[4][4]; /* 游戲數(shù)字面板,抽象為二維數(shù)組 */static int score; /* 游戲得分 */static int best; /* 游戲最高分 */static int if_need_add_num; /* 是否需要生成隨機(jī)數(shù)標(biāo)志,1表示需要,0表示不需要 */static int if_game_over; /* 是否游戲結(jié)束標(biāo)志,1表示游戲結(jié)束,0表示正常 */static int if_prepare_exit; /* 是否準(zhǔn)備退出游戲,1表示是,0表示否 *//* main函數(shù) 函數(shù)定義 */int main(int argc, char *argv[]) { init_game(); loop_game(); release_game(0); return 0;}/* 讀取鍵盤 函數(shù)定義 */int read_keyboard() {#ifdef _WIN32 return _getch();#else int key_code; if (read(0, &key_code, 1) best) { best = score; FILE *fp = fopen(config_path, "w"); if (fp) { fwrite(&best, sizeof(best), 1, fp); fclose(fp); } } /* 默認(rèn)為需要生成隨機(jī)數(shù)時也同時需要刷新顯示,反之亦然 */ if (if_need_add_num) { add_rand_num(); refresh_show(); } else if (if_prepare_exit) { refresh_show(); } }}/* 重置游戲 函數(shù)定義 */void reset_game() { score = 0; if_need_add_num = 1; if_game_over = 0; if_prepare_exit = 0; /* 了解到游戲初始化時出現(xiàn)的兩個數(shù)一定會有個2,所以先隨機(jī)生成一個2,其他均為0 */ int n = rand() % 16; int i; for (i = 0; i 0) /* 找出k后面第一個不為空的項,下標(biāo)為j,之后分三種情況 */ { if (board[i][k] == board[i][j]) { /* 情況1:k項和j項相等,此時合并方塊并計分 */ score += board[i][k++] *= 2; board[i][j] = 0; if_need_add_num = 1; /* 需要生成隨機(jī)數(shù)和刷新界面 */ } else if (board[i][k] == 0) { /* 情況2:k項為空,則把j項賦值給k項,相當(dāng)于j方塊移動到k方塊 */ board[i][k] = board[i][j]; board[i][j] = 0; if_need_add_num = 1; } else { /* 情況3:k項不為空,且和j項不相等,此時把j項賦值給k+1項,相當(dāng)于移動到k+1的位置 */ board[i][++k] = board[i][j]; if (j != k) { /* 判斷j項和k項是否原先就挨在一起,若不是則把j項賦值為空(值為0) */ board[i][j] = 0; if_need_add_num = 1; } } } } }}/* 右移 函數(shù)定義 */void move_right() { /* 仿照左移操作,區(qū)別僅僅是j和k都反向遍歷 */ int i; for (i = 0; i = 0; --j) { if (board[i][j] > 0) { if (board[i][k] == board[i][j]) { score += board[i][k--] *= 2; board[i][j] = 0; if_need_add_num = 1; } else if (board[i][k] == 0) { board[i][k] = board[i][j]; board[i][j] = 0; if_need_add_num = 1; } else { board[i][--k] = board[i][j]; if (j != k) { board[i][j] = 0; if_need_add_num = 1; } } } } }}/* 上移 函數(shù)定義 */void move_up() { /* 仿照左移操作,區(qū)別僅僅是行列互換后遍歷 */ int i; for (i = 0; i 0) { if (board[k][i] == board[j][i]) { score += board[k++][i] *= 2; board[j][i] = 0; if_need_add_num = 1; } else if (board[k][i] == 0) { board[k][i] = board[j][i]; board[j][i] = 0; if_need_add_num = 1; } else { board[++k][i] = board[j][i]; if (j != k) { board[j][i] = 0; if_need_add_num = 1; } } } } }}/* 下移 函數(shù)定義 */void move_down() { /* 仿照左移操作,區(qū)別僅僅是行列互換后遍歷,且j和k都反向遍歷 */ int i; for (i = 0; i = 0; --j) { if (board[j][i] > 0) { if (board[k][i] == board[j][i]) { score += board[k--][i] *= 2; board[j][i] = 0; if_need_add_num = 1; } else if (board[k][i] == 0) { board[k][i] = board[j][i]; board[j][i] = 0; if_need_add_num = 1; } else { board[--k][i] = board[j][i]; if (j != k) { board[j][i] = 0; if_need_add_num = 1; } } } } }}/* 清屏 */void clear_screen() {#ifdef _WIN32 /* 重設(shè)光標(biāo)輸出位置清屏可以減少閃爍,system("cls")為備用清屏命令,均為Windows平臺相關(guān)*/ COORD pos = {0, 0}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos); CONSOLE_CURSOR_INFO info = {1, 0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);#else printf(" 国产亚洲精品久久久久秋霞| 亚洲国产精品18久久久久久| 精品欧美一区二区三区久久久| 7国产欧美日韩综合天堂中文久久久久| 久久久久久久综合日本亚洲| 日本欧美国产精品第一页久久| 久久久精品人妻一区二区三区蜜桃| 婷婷久久香蕉五月综合加勒比| 久久精品国产半推半就| 久久人与动人物a级毛片| 婷婷久久综合九色综合98| 久久中文字幕人妻熟av女| 久久99国产精品久久99| 久久久SS麻豆欧美国产日韩| 青青草国产成人久久91网| 亚洲级αV无码毛片久久精品| 91麻精品国产91久久久久| 色狠狠久久AV五月综合| 波多野结衣久久精品| 国产午夜福利精品久久| 狠狠干狠狠久久| 久久精品九九亚洲精品| 久久久久高潮综合影院| 亚洲欧美久久久久9999 | 午夜欧美精品久久久久久久| 99久久婷婷国产一区二区| av国内精品久久久久影院| 久久青青草原精品国产| 亚洲中文字幕无码久久精品1| 欧美成人免费观看久久| 亚洲国产成人久久一区WWW| 精品欧美一区二区三区久久久| 欧美一区二区精品久久| 国产激情久久久久影院老熟女| 久久99精品国产麻豆| 久久精品国产亚洲av影院 | 九九久久99综合一区二区| 久久精品国产99久久无毒不卡 | 久久久久av无码免费网| 久久久久国产精品人妻| 亚洲欧美成人综合久久久|