目錄
開門見山
給出我在寫這篇代碼時的思維導圖,這篇博客也是根據思維導圖寫的
游戲結果展示
游戲思路分析
布置雷
排查雷
思路程式化
InitBoard()
DisplayBoard()
Setboom()
Findboom()
總結注意事項
開門見山
給出我在寫這篇代碼時的思維導圖,這篇博客也是根據思維導圖寫的

游戲結果展示
//因為本人有點小菜,所以就設定4*4 -->4的規格,即16格子找出4個雷以演示游戲代碼,想挑戰高難度的小伙伴可自行設定.那么就見笑了


游戲思路分析
先展示本次撰寫代碼所用到的頭檔案和源檔案展示,方便后續閱讀
| test.c | 用于測驗撰寫的掃雷代碼相關功能即對game.c中的函式進行呼叫 |
| game.h | 用于對掃雷代碼所涉及的主要函式及引數的宣告 |
| game.c | 代碼的主體,對game.h中宣告的函式進行定義 |
和三子棋(見前篇)一樣,應該有個選單,你覺得怎么好看就怎么設定----test.c
void menu()
{
printf("****************************\n");
printf("************掃雷************\n");
printf("********** 1. play *********\n");
printf("********** 0. exit *********\n");
printf("****************************\n");
}
本來menu()應該屬于game.c但是由于menu()本來就很簡單,就沒必要和game.c中的大頭"爭寵"了
布置雷
先看一張圖


兩個問題1.雷該放到哪呢?2.雷該用什么表示呢?
答:1.雷應該放到一個字符陣列,不妨設為boom[ROWS][COLS]
2.雷用'1'表示,非雷用'0'表示,注意這里的'1'和'0'是字符1和0. 為什么選擇用'1'和'0'?文末總結部分會詳細給出
ps:這里的ROWS和COLS對應上圖的11行11列,即橙色框.有的小伙伴就會問了我的棋盤不是9*9的嘛,設定成11*11豈不是浪費空間!實則不然,這樣設定就可以避免產生陣列越界的問題.何出此言?
我們看上面的綠色框,它的作用是去排查(x,y)周圍八個格子的字符1(雷)的個數,排查(8,8)當然不會有陣列越界,那我要是去排查(1,1) (1,2)...(9,9)也就是最外圍的那32個格子,會出現什么情況?綠色框會訪問不屬于紅色框的內容,(不恰當的比喻:你去拿了不該屬于你的東西)這當然是不行的.計算機會無情的告訴你error.所以呢,我們應該設定為11*11的,最外圍那一圈的元素可以不用,但不能沒有!我們只操作中間的9*9就行了.
排查雷
像上圖的綠色框,排查了(8,8),周圍有3個雷,排查出來了該怎么辦呢?是不是該把這個"3"存起來呀.怎么存呢?當然也是用陣列咯.
這時候就有小伙伴躍躍欲試了:int show[ROWS][COLS],創建了一個整形陣列;
那我只能說:你這波啊,還在第一層.且往下看
我打算用char show[ROWS][COLS],一個字符型別的陣列.為什么要這么設計?這里先賣個關子,在函式定義部分會給出原因.
ok,以上便是我們的思路分析了,真正的大頭來了
思路程式化
第一步當然是撰寫我們的test.c了
//游戲測驗模塊
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("****************************\n");
printf("************掃雷************\n");
printf("********** 1. play *********\n");
printf("********** 0. exit *********\n");
printf("****************************\n");
}
void game()
{
//關于雷的資訊存盤
//1. 創建一個陣列來存放雷
char boom[ROWS][COLS] = { 0 };
//2. 創建一個陣列來顯示排查出的雷
char show[ROWS][COLS] = { 0 };
//初始化兩個陣列
InitBoard(boom, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//列印棋盤
//DisplayBoard(boom, ROW, COL);
DisplayBoard(show, ROW, COL);//
//布置雷
Setboom(boom, ROW, COL);
//DisplayBoard(boom, ROW, COL);
//掃雷
Findboom(boom, show, ROW, COL);
}
void test()
{
int choice = 0;
srand((unsigned int)time(NULL));//時間戳
do
{
menu();
printf("請選擇:>");
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("選擇錯誤,重新選擇!\n");
break;
}
} while (choice);
}
int main()
{
test();
return 0;
}
可以發現是不是很精簡且簡單
main()呼叫了test(),test()呼叫了menu()如果輸入1,test()又呼叫了game(),game()又呼叫了一系列的函式.然后我們才能玩到掃雷游戲.
細心的小伙伴會發現里面有個 #include "game.h" 這其實是頭檔案名的一種形式,只不過這個是我們自己定義的罷了,不像 #include<stdio.h> #include<math.h> 這類是vs自帶的.你只需要記住:自己定義的頭檔案要參考的話要加 " "
函式要"先宣告后使用"
我們來看看game.h里面有什么
1 //函式宣告模塊
2 //一些資料的全域定義,方便后續修改
3
4
5
6 #define ROW 9
7 #define COL 9
8
9 #define ROWS ROW+2
10 #define COLS COL+2
11 //雷的數量
12 #define BOOM_COUNT 10
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <time.h>
17 //函式回傳型別 函式名 函式引數
18 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
19 void DisplayBoard(char board[ROWS][COLS], int row, int col);
20 void Setboom(char board[ROWS][COLS], int row, int col);
21 void Findboom(char boom[ROWS][COLS], char show[ROWS][COLS], int row, int col);
18-21行這些函式是game()反復呼叫的,宣告已經有了,還差定義,我們把他們定義在game.c里面
下面一一介紹
InitBoard()
| 將陣列初始化為你想要的內容 InitBoard() 回傳型別 viod 引數 一個char型別陣列,兩個整形數,一個字符數 還記得上面賣的關子嗎,我把show[][]也定義為char型別,現在來告訴你答案,我把show[][]定義為和boom[][]一樣的char型別,就是為了,讓InitBoard()一個函式操作兩個陣列,使代碼更精簡,避免了我要因為初始化show[][]去重新定義另一個函式 下面的DisplayBoard()也是同樣的道理
|
DisplayBoard()
列印陣列內容
| void DisplayBoard(char board[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); printf("|");//緊接著列印陣列內容 for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } } |
Setboom()
布置雷->把boom[][]里對應坐標換成字符1 '1'
隨機設定雷的位置
| void Setboom(char board[ROWS][COLS], int row, int col) { int count = BOOM_COUNT; while (count) { int x = rand() % row + 1;//讓x范圍是1-9 int y = rand() % col + 1;//讓y范圍是1-9 if (board[x][y] == '0') { board[x][y] = '1'; count--; } } } |
Findboom()
找出boom[x][y]周圍的字符1 '1' 并把找到的'1'的個數存到show[x][y]
| //去找我們想排查的那個坐標周圍的8個格子中雷的數量 int get_boom_count(char boom[ROWS][COLS], int x, int y) {
return boom[x - 1][y] + boom[x - 1][y - 1] + boom[x][y - 1] + boom[x + 1][y - 1] + boom[x + 1][y] + boom[x + 1][y + 1] + boom[x][y + 1] + boom[x - 1][y + 1] - 8 * '0'; } void Findboom(char boom[ROWS][COLS], char show[ROWS][COLS], int row, int col)//兩個陣列,相同的坐標,一個表示雷,一個表示坐標周圍的雷的個數 { int x = 0; int y = 0; int rest_blank = 0;//用來表示剩余的不是雷的格子的個數
while (rest_blank < row * col - BOOM_COUNT) { printf("請輸入要排查的坐標:>"); scanf("%d%d", &x, &y); //這個是時候應該先判斷坐標合法性,即要排查的坐標應該在9*9的格子的范圍 if (x >= 1 && x <= row && y >= 1 && y <= col) { //坐標合法 //1. 是雷 if (boom[x][y] == '1') { printf("很遺憾,你被炸死了\n"); //起碼要讓玩家"死得明白" DisplayBoard(boom, row, col);//展示一下雷的分布 break; } else //2.不是雷 { //計算x,y坐標周圍有幾個雷 int count = get_boom_count(boom, x, y); //把找到的雷的個數存到show陣列里 show[x][y] = count + '0';//一個整數轉換為對應的字符加上'0' 如 3+'0'=='3' DisplayBoard(show, row, col); //找到一個雷rest_blank就自增 rest_blank++; } } else { printf("輸入坐標非法,請重新輸入!\n"); } } //判斷一下是否等于所有非雷的格子的數量 if (rest_blank == row * col - BOOM_COUNT) { printf("恭喜你,排雷成功\n"); DisplayBoard(boom, row, col); } } |
最后來看一下game.c的內容其實就是上面幾個函式的匯總
//函式定義模塊
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[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);
printf("|");//緊接著列印陣列內容
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void Setboom(char board[ROWS][COLS], int row, int col)
{
int count = BOOM_COUNT;
while (count)
{
int x = rand() % row + 1;//讓x范圍是1-9
int y = rand() % col + 1;//讓y范圍是1-9
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
//去找我們想看的那個坐標周圍的8個格子中雷的數量
int get_boom_count(char boom[ROWS][COLS], int x, int y)
{
return boom[x - 1][y] +
boom[x - 1][y - 1] +
boom[x][y - 1] +
boom[x + 1][y - 1] +
boom[x + 1][y] +
boom[x + 1][y + 1] +
boom[x][y + 1] +
boom[x - 1][y + 1] - 8 * '0';
}
//
void Findboom(char boom[ROWS][COLS], char show[ROWS][COLS], int row, int col)//兩個陣列,相同的坐標,一個表示雷,一個表示坐標周圍的雷的個數
{
int x = 0;
int y = 0;
int rest_blank = 0;//用來表示剩余的不是雷的格子的個數
while (rest_blank < row * col - BOOM_COUNT)
{
printf("請輸入要排查的坐標:>");
scanf("%d%d", &x, &y);
//這個是時候應該先判斷坐標合法性,即要排查的坐標應該在9*9的格子的范圍
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//坐標合法
//1. 是雷
if (boom[x][y] == '1')
{
printf("很遺憾,你被炸死了\n");
//起碼要讓玩家"死得明白"
DisplayBoard(boom, row, col);//展示一下雷的分布
break;
}
else //2.不是雷
{
//計算x,y坐標周圍有幾個雷
int count = get_boom_count(boom, x, y);
//把找到的雷的個數存到show陣列里
show[x][y] = count + '0';
DisplayBoard(show, row, col);
//找到一個雷rest_blank就自增
rest_blank++;
}
}
else
{
printf("輸入坐標非法,請重新輸入!\n");
}
}
//判斷一下是否等于所有非雷的格子的數量
if (rest_blank == row * col - BOOM_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(boom, row, col);
}
}
ok,到這大頭部分就講完了
總結注意事項
#define _CRT_SECURE_NO_WARNINGS 1
這其實是為了讓編譯器在編譯scanf時不報錯,因為一些編譯器(比如我自用的VS2019)會認為scanf不安全,建議你用scanf_s.但是我不聽它的建議,我就要用scanf,就在代碼前面加上上面那句就行了
關于雷的設定問題
充分利用數字的屬性, '1'表示雷, 2表示周圍有2個雷 這是實實在在的2,
'2'可以作為陣列元素被列印出來,讓我們在視覺上認為周圍有兩個雷,這是字符'2',卻有了數字的屬性.
可能有點繞,哈哈哈,但應該理解的過來
關于陣列型別設定的問題
其實一開始我也沒想到把show[][]設定為char型別,我是在撰寫InitBoard()的時候,突然想到的.我去初始化兩個陣列,能不能用一個函式啊,但是一個函式只能初始化一個型別的陣列,且初始化物件和初始化內容必須是同一種型別(我不想強制型別轉換嗷)
初始化 char 型別的boom[][]為 '0' (開始的時候應該沒有雷) 要一個函式
初始話 int 型別的show[][] 為 '*' (一開始展示給我們看的應該全部是*,要把雷的位置遮起來,就不讓你看,哼) 又有點行不通
所以就干脆把show[][]也定義為char型別,這樣一來,陣列型別和初始化內容的型別不就統一了嘛.
而且DiaplayBoard()也可以不用為型別發愁
游戲中的不足之處
1.在數周圍8個格子的時候,get_boom_count()可以用回圈,我為什么不用呢,因為直觀,小伙伴們更容易理解
2.在排查雷的時候,不能展開一片.感興趣的小伙伴可以用遞回
排查boom[x][y]周圍8個格子--> 1.是雷,就計數存到show[x][y] 2.不是雷,排查它周圍的八個格子
到外圍的一圈都是雷為止(其實我也不太懂掃雷的游戲規則).為了避免一些格子的重復排查,可以定義一個陣列專門來表示某個格子是否被排查過.陣列初始化為0,只要排查過,對應坐標就賦值為1.
3.用遞回求斐波那契額數列,體現了上述思想,可以看我寫的另一篇博客(算是波小廣告啦)斐波那契數列可是藝術啊_HandsomeDog_L的博客-CSDN博客
小tips
建議小伙伴在寫這種型別的代碼時能有一個清晰的思路,怎么才能辦到呢-------思維導圖
當然大佬除外
上一篇寫的三子棋也是有思維導圖的,忘發出來了,我把它放到三子棋評論區吧,感興趣的小伙伴可以去看看
由于編者水平實在有限,有什么錯誤之處望指正,非常感謝您的反饋
本文一共一萬字左右,(大部分是代碼),看完眼睛注意休息哦,回見啦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301615.html
標籤:其他
