掃雷游戲
運(yùn)行環(huán)境以Dev-C++、Visual Studio 2022、MacOS的命令行和Xcode為主
1.掃雷游戲分析和設(shè)計(jì)
-
1.1 功能說(shuō)明
-
在控制臺(tái)通過(guò)菜單選項(xiàng)(繼續(xù) or 結(jié)束)完成掃雷
-
掃雷區(qū)域?yàn)?
8 * 8,默認(rèn)布置雷的個(gè)數(shù)為10個(gè) -
排查雷區(qū)的邏輯
-
如果當(dāng)前位置恰好是雷區(qū),表示踩雷,結(jié)束游戲
-
如果當(dāng)前位置不是雷區(qū),則顯示該位置外圍繞一圈的
8個(gè)位置中雷的數(shù)目 -
如果所有非雷區(qū)的位置都找到了,則排雷成功,結(jié)束游戲
-
-
-
1.2 游戲界面(以
4 * 4雷區(qū),3個(gè)雷為例)- 初始界面
![image]()
- 排雷界面
![image]()
- 踩雷界面
![image]()
- 成功界面
![image]()
![image]()
-
1.3 數(shù)據(jù)結(jié)構(gòu)分析
- 掃雷過(guò)程中,雷區(qū)的布置和排查掉的雷區(qū)信息(即哪個(gè)位置沒(méi)有埋雷)均需要存儲(chǔ)。由于掃雷區(qū)域?yàn)?
8 * 8,比較容易想到8 * 8的二維數(shù)組,如下圖。假設(shè)設(shè)雷位置存儲(chǔ) 1,非雷位置存儲(chǔ) 0
![image]()
- 以排查位置
(3, 4)是否有雷為例,遍歷它周圍一圈的藍(lán)色區(qū)域,統(tǒng)計(jì)周圍雷的個(gè)數(shù)為2
![image]()
- 以排查位置
(8, 3)是否有雷為例,依然遍歷它周圍一圈的藍(lán)色區(qū)域,統(tǒng)計(jì)周圍雷的個(gè)數(shù)。此時(shí)發(fā)現(xiàn)最下方的3個(gè)區(qū)域已越界,若其中存在隨機(jī)數(shù),會(huì)對(duì)雷數(shù)統(tǒng)計(jì)產(chǎn)生影響
![image]()
- 為此,將原有掃雷區(qū)域增加 2 行 2 列,擴(kuò)為 ````10 * 10```,最外圈(綠底區(qū)域)都初始化為0但不計(jì)入布雷圈
![image]()
-
再回到位置
(3, 4),它周邊雷的個(gè)數(shù)2應(yīng)當(dāng)被記錄下來(lái),作為后續(xù)排雷的參考。倘若也記錄在"雷區(qū)信息數(shù)組"中,雷的布置信息0 與 1與雷的個(gè)數(shù)信息0 1 2 ...容易混淆 -
綜上,使用
10 * 10數(shù)組mine來(lái)存儲(chǔ)雷區(qū)信息的同時(shí),再引入一個(gè)10 * 10數(shù)組show用于存儲(chǔ)排查出的雷的信息。如上圖中mine[4][5] = 0,show[4][5] = 2;
char min[10][10] = {0}; // 存儲(chǔ)雷的信息 char show[10][10] = {0}; // 存儲(chǔ)排查出的非雷位置周圍的雷的個(gè)數(shù)信息![image]()
- 掃雷過(guò)程中,雷區(qū)的布置和排查掉的雷區(qū)信息(即哪個(gè)位置沒(méi)有埋雷)均需要存儲(chǔ)。由于掃雷區(qū)域?yàn)?
-
1.4 文件結(jié)構(gòu)分析
main.c // 主函數(shù),游戲的測(cè)試邏輯
game.c // 游戲功能的實(shí)現(xiàn)
game.h // 頭文件,游戲需要的宏定義、數(shù)據(jù)類型與函數(shù)聲明等
2.掃雷游戲代碼實(shí)現(xiàn)
// 1.game.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 8
#define COL 8
#define ROWS ROW+2
#define COLS COL+2
#define EASY_LEVEL 10
// 初始化棋盤(pán)
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
// 打印棋盤(pán)
void DisplayBoard(char board[ROWS][COLS], int row, int col);
// 布置雷區(qū)
void SetMine(char mine[ROWS][COLS], int row, int col);
// 排查雷區(qū)
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
// 2. game.c
#include "game.h"
// 初始化棋盤(pán)
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) {
int i = 0, j = 0;
for (i = 0; i < rows; i++){
for (j = 0; j < cols; j++) {
board[i][j] = set;
}
}
}
// 打印棋盤(pán)
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
int i = 0, j = 0;
printf("-------Dig Mines-------\n");
for (i = 0; i <= col; i++) { // 打印列標(biāo)
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i); // 打印行標(biāo)
for (j = 1; j <= col; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
}
// 布置雷區(qū),本質(zhì)上就是從mine數(shù)組中隨機(jī)選EASY_LEVEL個(gè)位置,將此處的值由0修改為1
void SetMine(char mine[ROWS][COLS], int row, int col) {
int x = 0, y = 0;
int count = EASY_LEVEL;
while (count) {
x = rand() % row + 1; // 隨機(jī)生成行標(biāo)
y = rand() % col + 1; // 隨機(jī)生成列標(biāo)
if (mine[x][y] != '1') {
mine[x][y] = '1'; // 布置一個(gè)雷
count--;
}
}
}
// 計(jì)算(x, y)周圍的8個(gè)位置值的總和
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
int i = 0, j = 0;
int count = 0;
for (i = -1; i <= 1; i++) {
for (j = -1; j <= 1; j++) {
count += (mine[x + i][y + j] - '0');
}
}
return count;
}
// 排查雷區(qū),由用戶輸入合法的(x, y),讀取mine中x行y列的數(shù)據(jù),若為1則踩雷;否則統(tǒng)計(jì)x行y列四周8個(gè)位置的數(shù)據(jù)
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0, y = 0;
int count = 0; // 某位置四周雷的個(gè)數(shù)
int dst = 0; // 非雷的個(gè)數(shù),即成功排查的非雷的位置
// 當(dāng) "成功排查的非雷位置累計(jì)數(shù)量 < 雷區(qū)位置總數(shù) - 雷的個(gè)數(shù)" 時(shí)循環(huán)繼續(xù)
while (dst < row * col - EASY_LEVEL) {
printf("輸入 x(1~%d) 和 y(1~%d):", row, col);
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {
if (mine[x][y] == '1') {
printf("踩雷了...Boom!\n");
printf("雷區(qū)信息如下:\n");
DisplayBoard(mine, row, col);
break;
}
else {
count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
dst++;
printf("Dig Mine info be like:\n");
DisplayBoard(show, row, col);
}
}
else {
printf("illegal input!\n");
}
}
if (dst == row * col - EASY_LEVEL) {
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, row, col);
}
}
// main.c
#include "game.h"
void menu(void) {
printf("************************\n");
printf("******** 1.play ********\n");
printf("******** 0.over ********\n");
printf("************************\n");
}
void game(void) {
char mine[ROWS][COLS] = { 0 }; // 存放棋盤(pán)中設(shè)置的雷的信息
char show[ROWS][COLS] = { 0 }; // 存放從棋盤(pán)中排查出的雷的信息
// 初始化棋盤(pán),mine最初全為0,show最初全為*,行列均為ROWS和COLS
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 打印棋盤(pán),行列為ROW和COL,鑒于掃雷期間要統(tǒng)計(jì)某位置四周8個(gè)位置雷的數(shù)量,
// 為防止最上下左右行列的位置越界,因此空出了最上行、最下行和最左列、最右列
DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL); // 有雷區(qū)安排的棋盤(pán)不用打印
// 布置雷區(qū)
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
// 排查雷區(qū)
FindMine(mine, show, ROW, COL);
}
void test(void) {
int input = 0;
srand((unsigned int)time(NULL)); // 以當(dāng)前時(shí)間作為種子,生成不同的隨機(jī)數(shù)
do {
menu();
printf("請(qǐng)選擇(1 or 0):");
scanf("%d", &input);
switch (input) {
case 1:
game(); // 掃雷的全部邏輯
break;
case 0:
printf("結(jié)束游戲\n");
break;
default:
printf("輸入錯(cuò)誤!\n");
break;
}
} while (input);
}
int main(int argc, const char * argv[]) {
// insert code here...
test();
return 0;
}











浙公網(wǎng)安備 33010602011771號(hào)