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