摘要:作者時(shí)間網(wǎng)站地址摘要語(yǔ)言實(shí)現(xiàn)我們小時(shí)候玩過(guò)的掃雷游戲,最近看到了一些掃雷游戲的簡(jiǎn)單實(shí)現(xiàn),但是總有功能上的缺失,玩起來(lái)不那么的原汁原味,因此我增加了一些新功能確保玩家首次排雷一定不會(huì)炸死。
作者:Nico
時(shí)間: 2021-11-10
網(wǎng)站地址:[]:https://github.com/sxfinn
C語(yǔ)言實(shí)現(xiàn)我們小時(shí)候玩過(guò)的掃雷游戲,最近看到了一些掃雷游戲的簡(jiǎn)單實(shí)現(xiàn),但是總有功能上的缺失,玩起來(lái)不那么的“原汁原味”,因此我增加了一些新功能:
- 確保玩家首次排雷一定不會(huì)炸死。
- 加入了計(jì)時(shí)器記錄結(jié)束時(shí)間。
- 擴(kuò)展式排雷,展開(kāi)周圍的非雷區(qū)。
比較難的一點(diǎn)是擴(kuò)展式排雷,使用用遞歸函數(shù)處理相對(duì)方便。
探索排雷位的周圍八個(gè)區(qū)域。
總歸情況就分三類,可探索的區(qū)域?yàn)?個(gè),5個(gè),3個(gè)。但這樣分類實(shí)在麻煩,所以我們可以選擇在創(chuàng)建雷盤(pán)的時(shí)候,將二維數(shù)組的維度擴(kuò)大一些,使其不用考慮多種情況,而只用考慮探索周圍八個(gè)雷區(qū)。
我們可以給外側(cè)再加一層,即給二維數(shù)組行列分別加二,并且把外層全部設(shè)置為非雷區(qū)域,就可以解決這一問(wèn)題。
展開(kāi)周圍的非雷區(qū)
遞歸過(guò)程:如果(x,y)位置周圍八區(qū)的雷數(shù)為0,則從八個(gè)區(qū)域展開(kāi),展開(kāi)的位置的 x坐標(biāo)是從x-1到x+1,而 y 的位置是從y-1到y(tǒng)+1的范圍中,因此嵌套兩重循環(huán)。
進(jìn)入條件:只有之前沒(méi)有展開(kāi)過(guò),且坐標(biāo)在雷盤(pán)內(nèi)的位置才進(jìn)入遞歸。
終止條件:如果探索的周圍八個(gè)位置有雷,則停止,并讓該位置顯示雷的數(shù)量。
探索八區(qū)代碼實(shí)現(xiàn):
提醒:’*‘是未排雷的區(qū)域,’ ‘是代表已經(jīng)展開(kāi)過(guò)的區(qū)域。
//計(jì)算排查位置處周圍雷的個(gè)數(shù)int calculate(char mine[ROWS][COLS],int x,int y){ //因?yàn)槔椎奈恢梅诺氖亲址?’ // 加起來(lái)之后應(yīng)該分別減去‘0’,才得到雷的個(gè)數(shù) return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * "0";}
//展開(kāi)周圍都沒(méi)有雷的雷盤(pán)(擴(kuò)展式排雷)void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)//擴(kuò)展函數(shù){ //利用calculate函數(shù)判斷周圍是否有雷 if (calculate(mine, x, y) == 0) {//判斷周圍雷的個(gè)數(shù),若為0,則需要展開(kāi) show[x][y] = " ";//展開(kāi)的位置都置為空格 int i = 0; int j = 0; //該位置可以拓展才檢查周圍8個(gè)位置是否能拓展 for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (show[i][j] == "*" && i > 0 && i <= ROW && j > 0 && j <= COL) { //如果該位置未被掃過(guò)且在棋盤(pán)范圍內(nèi)則繼續(xù)遞歸調(diào)用expand函數(shù) //再依次進(jìn)入判斷周圍8個(gè)位置是能被展開(kāi)還是不能 expand(mine, show, i, j); } } } } else { show[x][y] = calculate(mine, x, y)+"0"; //不需要展開(kāi)則顯示附近雷的個(gè)數(shù) }}
這兩個(gè)函數(shù)結(jié)合起來(lái)使用便可以達(dá)到擴(kuò)展展開(kāi)的效果。
使用效果:
可以看到在選擇(5,6)后一片非雷區(qū)被展開(kāi)了,并且邊緣部分的雷個(gè)數(shù)被打印在了相應(yīng)位置。
由于實(shí)在找不到什么好看的符號(hào)代替’ ‘,看著可能會(huì)有點(diǎn)難受?,歡迎評(píng)論區(qū)給出建議!
我將代碼分為了test.c
、game.h
、game.c
三個(gè)部分。
test.c
是游戲?qū)崿F(xiàn)的主體框架。
game.h
是所用到的頭文件以及自定義函數(shù)聲明。
game.c
是游戲的具體實(shí)現(xiàn)模塊。
除了上面的遞歸有些難度外,其他的都比較易懂,不再多帶帶闡述,下面的源碼中我給出了每一步的注釋,解釋的很清楚,相信各位一邊看代碼一邊想會(huì)有更多的收獲。
#include"game.h"int main(){ srand((unsigned)time(NULL)); int input = 0; do { menu();//菜單 printf("請(qǐng)輸入->(1 / 0)/n"); scanf("%d", &input); switch (input) { case 1: game();//選擇1就進(jìn)入game break; case 0://選擇0就退出 printf("退出游戲!/n"); break; default: printf("輸入錯(cuò)誤,請(qǐng)重新輸入:/n"); break; } } while (input); return 0;}
#pragma once#include #include //時(shí)間戳函數(shù)頭文件#include //rand、srand函數(shù)頭文件#include #define ROW 9//雷的區(qū)域的行數(shù)#define COL 9//雷的區(qū)域的列數(shù)#define ROWS ROW+2//數(shù)組的一維大小#define COLS COL+2//數(shù)組的二維大小#define NUM 50//雷的個(gè)數(shù)//進(jìn)入游戲void game();//打印菜單void menu();//計(jì)時(shí)器void set_time();//初始化數(shù)組void init_array(char array[ROWS][COLS], int rows, int cols, char symbol);//布置雷void lay_mines(char mine[ROWS][COLS], int row, int col);//打印void show_interface(char show[ROWS][COLS], int row, int col);//排查雷void mine_detection(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//計(jì)算雷的個(gè)數(shù)int calculate(char mine[ROWS][COLS], int x, int y);//保證第一次安全排雷int one_safe(char mine[ROWS][COLS], int x, int y);//擴(kuò)展式排雷以及記錄周圍的雷數(shù)量void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
#include"game.h"//游戲流程void game(){ system("cls");//清屏 //創(chuàng)建兩個(gè)數(shù)組 char mine[ROWS][COLS] = { 0 };//布置雷的數(shù)組 char show[ROWS][COLS] = { 0 };//游戲界面的顯示雷個(gè)數(shù)的數(shù)組 //初始化兩個(gè)數(shù)組 init_array(mine, ROWS, COLS, "0");//初始化為‘0’ init_array(show, ROWS, COLS, "*");//初始化為‘*’ //布置雷 lay_mines(mine, ROW, COL); //打印出show數(shù)組 show_interface(show, ROW, COL); //排查雷 mine_detection(mine, show, ROW, COL); set_time();}//菜單函數(shù)void menu(){ printf("***************************/n"); printf("* ****** MENU ******* */n"); printf("* ******1.play******* */n"); printf("* ******0.exit******* */n"); printf("* ******************* */n"); printf("***************************/n");}//計(jì)時(shí)函數(shù),void set_time(){ //打印出從程序運(yùn)行到目前所用的時(shí)間 printf("本次用時(shí):%u s/n", clock() / CLOCKS_PER_SEC);}//初始化數(shù)組void init_array(char array[ROWS][COLS], int rows, int cols, char symbol){ int i = 0; int j = 0; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { array[i][j] = symbol; } }}//布置地雷void lay_mines(char mine[ROWS][COLS],int row,int col){ int count = NUM;//NUM為雷的個(gè)數(shù) while (count)//循環(huán)條件,每次count-1 { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == "0") { mine[x][y] = "1"; count--; } }}//展示游戲界面void show_interface(char show[ROWS][COLS], int row, int col){ int i = 0; int j = 0; for (i = 0; i <= col; i++)//打印出列號(hào) { printf("%d ", i); } printf("/n"); for (i = 0; i <= col; i++) { printf("—"); } printf("/n"); for (i = 1; i <= row; i++) { printf("%d|", i);//打印出行號(hào) for (j = 1; j <= col; j++) { printf("%c ", show[i][j]); } printf("/n"); } for (i = 0; i <= col; i++) { printf("—"); } printf("/n");}//排查雷void mine_detection(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col){ int cnt = 0;//cnt為已排查出的雷的個(gè)數(shù) int x = 0; int y = 0; while (cnt < ROW * COL - NUM)//循環(huán)條件是還有雷沒(méi)有排查 { int ret = 0; //輸入要排查雷的坐標(biāo) printf("請(qǐng)輸入要排查的坐標(biāo):(X,Y)/n"); scanf("%d %d", &x, &y); if (cnt == 0)//第一次排雷 { if (x >= 1 && x <= row && y >= 1 && y <= col) { //首次輸入的坐標(biāo)如果是雷,則將雷移動(dòng)到另一個(gè)位置 one_safe(mine, x, y);//保證第一次總不是雷 //因?yàn)闊o(wú)論如何都不是雷,直接跳轉(zhuǎn)到記錄雷和擴(kuò)展的函數(shù)expand goto next; } else { printf("坐標(biāo)非法,請(qǐng)重新輸入:/n"); continue; } } //確保坐標(biāo)在設(shè)定的范圍中 if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == "0") { next://goto跳轉(zhuǎn)到這里 expand(mine, show, x, y); //打印出排查過(guò)一次后的界面 show_interface(show, ROW, COL); cnt++;//排雷成功次數(shù)加一 } else { //如果排查的位置放的是‘1’則表示該位置為炸彈 printf("很遺憾,您被炸死了!/n"); //游戲結(jié)束,打印出所有雷的位置 show_interface(mine, ROW, COL); break; } } else { printf("坐標(biāo)非法,請(qǐng)重新輸入:/n"); } } if (cnt == ROW * COL - NUM)//可排雷個(gè)數(shù)為0時(shí) { printf("恭喜您,排雷成功!/n"); //游戲結(jié)束 show_interface(mine, ROW, COL); }}//計(jì)算排查位置處周圍雷的個(gè)數(shù)int calculate(char mine[ROWS][COLS],int x,int y){ //因?yàn)槔椎奈恢梅诺氖亲址?’ // 加起來(lái)之后應(yīng)該分別減去‘0’,才得到雷的個(gè)數(shù) return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * "0";}//保證第一次不會(huì)踩到雷int one_safe(char mine[ROWS][COLS], int x, int y){ int count = 1; //判斷第一次是否是雷 if (mine[x][y] == "1") { mine[x][y] = "0"; //將雷隨機(jī)放入另一個(gè)沒(méi)有雷的位置 while (count) { x = rand() % ROW + 1; y = rand() % COL + 1; if (mine[x][y] == "0") { mine[x][y] = "1"; count--; } } return 1;//是雷則返回1 } else return 0;//不是雷則返回0 //其實(shí)這里可以返回值也可以不返回,因?yàn)樵谖易铋_(kāi)始寫(xiě)代碼時(shí),最開(kāi)始的思路是: //非雷 是雷這兩種情況分別進(jìn)入不同的分支,不過(guò)后來(lái)又換了一種思路}//展開(kāi)周圍都沒(méi)有雷的雷盤(pán)(擴(kuò)展式排雷)void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)//擴(kuò)展函數(shù),判斷是否需要遞歸式展開(kāi)。{ //利用calculate函數(shù)判斷周圍是否有雷 if (calculate(mine, x, y) == 0) {//判斷周圍雷的個(gè)數(shù),若為0,則需要展開(kāi) show[x][y] = " ";//展開(kāi)的位置都置為空格 int i = 0; int j = 0; //該位置可以拓展才檢查周圍8個(gè)位置是否能拓展 for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (show[i][j] == "*" && i > 0 && i <= ROW && j > 0 && j <= COL) { //如果該位置未被掃過(guò)且在棋盤(pán)范圍內(nèi)則繼續(xù)遞歸調(diào)用expand函數(shù) //再依次進(jìn)入判斷周圍8個(gè)位置是能被展開(kāi)還是不能 expand(mine, show, i, j); } } } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/123190.html
摘要:展示雷盤(pán)和初始化雷盤(pán)不一樣,展示雷盤(pán)只需要用即可,并不需要將都展示出來(lái),只是為了我們更好的計(jì)算掃雷的位置周圍的雷的數(shù)量。 目錄 1、需求分析 2、程序架構(gòu) 3、代碼實(shí)現(xiàn)(分函數(shù)呈現(xiàn)) (1)主函數(shù)代碼實(shí)現(xiàn) 分析: 異常處理: (2)游戲主函數(shù)實(shí)現(xiàn) 分析: (3)初始化函數(shù)的實(shí)現(xiàn) 分析: (4...
摘要:設(shè)計(jì)實(shí)現(xiàn)掃雷游戲大致思路創(chuàng)建文件想法實(shí)現(xiàn)設(shè)計(jì)一個(gè)函數(shù),實(shí)現(xiàn)建議菜單循環(huán)和分支選擇游戲選項(xiàng)創(chuàng)造一個(gè)掃雷版面版面的大小最后是要可控的如何存放雷和版面的信息呢考慮排查雷時(shí)候的思路,我們要判斷該位置周圍個(gè)格子里面是否有雷初始化 ...
摘要:新人小白的第一篇博客,有什么不好之處望多提意見(jiàn)。這個(gè)掃雷小游戲主要是基于二維數(shù)組,循環(huán)與基本的函數(shù)知識(shí)等。請(qǐng)輸入坐標(biāo)提示玩家輸入坐標(biāo)。換行是為了看著好看,要不然打印出來(lái)的數(shù)組會(huì)變形的。用來(lái)接收判斷輸贏的函數(shù)的返回值。 ???????新人小白的第一篇博客,有什么不好之處望多提意見(jiàn)。 ? ? ?...
摘要:上一期咱們用語(yǔ)言實(shí)現(xiàn)了三子棋的小游戲語(yǔ)言實(shí)現(xiàn)三子棋今天我們?cè)賮?lái)寫(xiě)個(gè)掃雷的游戲,說(shuō)起掃雷,相信大家都不陌生,可能許多朋友還是玩掃雷的高手。 ? ? ?上一期咱們用C語(yǔ)言實(shí)現(xiàn)了三子棋的小游戲? C語(yǔ)言實(shí)現(xiàn)三子棋? ? ? ?今天我們?cè)賮?lái)寫(xiě)個(gè)掃雷的游戲,說(shuō)起掃雷,相信大家都不陌生,可能許多朋友還是...
摘要:條消息語(yǔ)言入門(mén)三子棋語(yǔ)言實(shí)現(xiàn)詳細(xì)版的博客博客條消息語(yǔ)言入門(mén)三子棋語(yǔ)言實(shí)現(xiàn)詳細(xì)版的博客博客我們將雷盤(pán)初始化為統(tǒng)一的符號(hào)。 目錄 1.原理簡(jiǎn)介 2.分布目標(biāo)及代碼實(shí)現(xiàn) 3.總結(jié) 1.原理簡(jiǎn)介 ?首先我們需要一個(gè)空的雷盤(pán),在其中隨機(jī)埋入十枚雷,當(dāng)我們排這顆雷時(shí),若此位置為雷,則游戲失敗,若不...
閱讀 2245·2021-11-17 09:33
閱讀 2786·2021-11-12 10:36
閱讀 3410·2021-09-27 13:47
閱讀 901·2021-09-22 15:10
閱讀 3498·2021-09-09 11:51
閱讀 1405·2021-08-25 09:38
閱讀 2765·2019-08-30 15:55
閱讀 2618·2019-08-30 15:53