扫雷游戏
运行环境以Dev-C++、Visual Studio 2022、MacOS的命令行和Xcode为主
1.扫雷游戏分析和设计
-
1.1 功能说明
-
在控制台通过菜单选项(继续 or 结束)完成扫雷
-
扫雷区域为
8 * 8,默认布置雷的个数为10个 -
排查雷区的逻辑
-
如果当前位置恰好是雷区,表示踩雷,结束游戏
-
如果当前位置不是雷区,则显示该位置外围绕一圈的
8个位置中雷的数目 -
如果所有非雷区的位置都找到了,则排雷成功,结束游戏
-
-
-
1.2 游戏界面(以
4 * 4雷区,3个雷为例)- 初始界面
![image]()
- 排雷界面
![image]()
- 踩雷界面
![image]()
- 成功界面
![image]()
![image]()
-
1.3 数据结构分析
- 扫雷过程中,雷区的布置和排查掉的雷区信息(即哪个位置没有埋雷)均需要存储。由于扫雷区域为
8 * 8,比较容易想到8 * 8的二维数组,如下图。假设设雷位置存储 1,非雷位置存储 0
![image]()
- 以排查位置
(3, 4)是否有雷为例,遍历它周围一圈的蓝色区域,统计周围雷的个数为2
![image]()
- 以排查位置
(8, 3)是否有雷为例,依然遍历它周围一圈的蓝色区域,统计周围雷的个数。此时发现最下方的3个区域已越界,若其中存在随机数,会对雷数统计产生影响
![image]()
- 为此,将原有扫雷区域增加 2 行 2 列,扩为 ````10 * 10```,最外圈(绿底区域)都初始化为0但不计入布雷圈
![image]()
-
再回到位置
(3, 4),它周边雷的个数2应当被记录下来,作为后续排雷的参考。倘若也记录在"雷区信息数组"中,雷的布置信息0 与 1与雷的个数信息0 1 2 ...容易混淆 -
综上,使用
10 * 10数组mine来存储雷区信息的同时,再引入一个10 * 10数组show用于存储排查出的雷的信息。如上图中mine[4][5] = 0,show[4][5] = 2;
char min[10][10] = {0}; // 存储雷的信息 char show[10][10] = {0}; // 存储排查出的非雷位置周围的雷的个数信息![image]()
- 扫雷过程中,雷区的布置和排查掉的雷区信息(即哪个位置没有埋雷)均需要存储。由于扫雷区域为
-
1.4 文件结构分析
main.c // 主函数,游戏的测试逻辑
game.c // 游戏功能的实现
game.h // 头文件,游戏需要的宏定义、数据类型与函数声明等
2.扫雷游戏代码实现
// 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
// 初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
// 布置雷区
void SetMine(char mine[ROWS][COLS], int row, int col);
// 排查雷区
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
// 2. game.c
#include "game.h"
// 初始化棋盘
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;
}
}
}
// 打印棋盘
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++) { // 打印列标
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i); // 打印行标
for (j = 1; j <= col; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
}
// 布置雷区,本质上就是从mine数组中随机选EASY_LEVEL个位置,将此处的值由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; // 随机生成行标
y = rand() % col + 1; // 随机生成列标
if (mine[x][y] != '1') {
mine[x][y] = '1'; // 布置一个雷
count--;
}
}
}
// 计算(x, y)周围的8个位置值的总和
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;
}
// 排查雷区,由用户输入合法的(x, y),读取mine中x行y列的数据,若为1则踩雷;否则统计x行y列四周8个位置的数据
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0, y = 0;
int count = 0; // 某位置四周雷的个数
int dst = 0; // 非雷的个数,即成功排查的非雷的位置
// 当 "成功排查的非雷位置累计数量 < 雷区位置总数 - 雷的个数" 时循环继续
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("雷区信息如下:\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 }; // 存放棋盘中设置的雷的信息
char show[ROWS][COLS] = { 0 }; // 存放从棋盘中排查出的雷的信息
// 初始化棋盘,mine最初全为0,show最初全为*,行列均为ROWS和COLS
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 打印棋盘,行列为ROW和COL,鉴于扫雷期间要统计某位置四周8个位置雷的数量,
// 为防止最上下左右行列的位置越界,因此空出了最上行、最下行和最左列、最右列
DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL); // 有雷区安排的棋盘不用打印
// 布置雷区
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
// 排查雷区
FindMine(mine, show, ROW, COL);
}
void test(void) {
int input = 0;
srand((unsigned int)time(NULL)); // 以当前时间作为种子,生成不同的随机数
do {
menu();
printf("请选择(1 or 0):");
scanf("%d", &input);
switch (input) {
case 1:
game(); // 扫雷的全部逻辑
break;
case 0:
printf("结束游戏\n");
break;
default:
printf("输入错误!\n");
break;
}
} while (input);
}
int main(int argc, const char * argv[]) {
// insert code here...
test();
return 0;
}











浙公网安备 33010602011771号