目录:
引言
扫雷是一款经典的单人益智游戏,玩家通过点击格子并避免踩雷来获胜。本文将用 C语言 实现一个简易的 9 × 9 扫雷游戏,重点讲解如何设计数据结构、生成雷区、计算周围雷数,以及交互逻辑。
正文开始
1.扫雷游戏分析和设计
1.1 扫雷游戏的功能说明
- 使用控制台实现经典的扫雷游戏
- 游戏可以通过菜单实现继续玩或者退出游戏
- 扫雷的棋盘是9*9的格子
- 默认随机布置10个雷
- 可以排查雷
- 如果位置不是雷,就显示周围有几个雷
- 如果位置是雷,就炸死游戏结束
- 把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束



1.2 游戏的分析与设计
1.2.1 数据结构分析



char mine[11][11] = {0};//⽤来存放布置好的雷的信息
char show[11][11] = {0};//⽤来存放排查出的雷的个数信息
1.2.2 文件结构设计
为了让整体程序有更好的可维护性、可读性、可扩展性,我们可以采取多文件的方式:
test.c //⽂件中写游戏的测试逻辑
game.c //⽂件中写游戏中函数的实现等
game.h //⽂件中写游戏需要的数据类型和函数声明等
2. 扫雷游戏的代码实现
2.1 test.c
2.1.1 基础架构
首先我们知道程序运行后,需要什么要求
1.在菜单上选择,如果选择开始,那么我们开始游戏,选择结束,退出游戏
2. 只要不选择退出游戏,游戏就会一直保持运行
那么我们就可以使用while + switch函数来实现,但是使用while的话我们首先得知道条件是什么,可我们的需求是先运行出菜单页面,先让我们选择,再进行判断条件,那么我们就可以使用do while。
void test()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戏结束,退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
2.1.2 菜单功能
在上面代码中,我们需要一个菜单函数menu()进行打印可视化的界面
void menu()
{
printf("***********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("***********************\n");
}
2.1.3 游戏流程
如果选择开始时,我们需要运行游戏,那么我们就可以写出一个game()函数来实现
在上面分析中,我们知道首先需要设计出两个11 * 11的棋盘mine和show
//可操作棋盘范围
#define ROW 9
#define COL 9
//实际棋盘范围(包含行 列)
#define ROWS ROW+2
#define COLS COL+2
char mine[ROWS][COLS] = { 0 }; //源棋盘 用于找周围有几颗雷 初始化为'0'
char show[ROWS][COLS] = { 0 }; //最终棋盘 由源棋盘得出的雷数改写最后的棋盘 初始化为'*'
下一步,我们需要初始化棋盘,将两个棋盘都初始化,可以先写上需要使用的函数,在后续game.c中再详细编写函数功能。
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0'); //将源棋盘全部初始化为'0'
InitBoard(show, ROWS, COLS, '*'); //将终棋盘全部初始化为'*'
下一步就要设置雷的位置,同样先写上
//安放雷
SetMine(mine, ROW, COL);
因为在开始游戏后,我们首先先得看一眼棋盘,然后选择坐标,所以我们先打印一次棋盘,供玩家更直观地查看棋盘
//打印棋盘
DisplayBoard(show, ROW, COL);
最后我们要做的就是排查雷了,要写出一个排查雷的函数,排查完后还需要让玩家看到排查后的棋盘状态所以需要再打印一次棋盘
//排查雷
FindMine(mine, show, ROW, COL);
DisplayBoard(show, ROW, COL);
所以最后总体是这样的
void menu()
{
printf("***********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("***********************\n");
}
void game()
{
//完成扫雷游戏
char mine[ROWS][COLS] = { 0 }; //源棋盘 用于找周围有几颗雷 初始化为'0'
char show[ROWS][COLS] = { 0 }; //最终棋盘 由源棋盘得出的雷数改写最后的棋盘 初始化为'*'
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0'); //将源棋盘全部初始化为'0'
InitBoard(show, ROWS, COLS, '*'); //将终棋盘全部初始化为'*'
//安放雷
SetMine(mine, ROW, COL);
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
DisplayBoard(show, ROW, COL);
}
void test()
{
srand(time(NULL)); // 2.2.3函数需要
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戏结束,退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
2.2 game.c
2.2.1 棋盘初始化
首先接收的参数需要1.行 2.列 3.初始化的目标,然后遍历整个数组,完成初始化
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
arr[i][j] = set; //将数组内容全部初始化为set
}
2.2.2 打印棋盘
为了方便玩家能清晰地选出坐标,所以在打印棋盘时,我们还需要先将行号和列号打印出来,然后再按序打印棋盘
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
for (int i = 0; i <= row; ++i)
{
printf("%2d ", i); // 行号右对齐
if (i == 0)
// 打印列号(1, 2, ..., col)
for (int j = 1; j <= col; ++j) printf("%2d ", j);
else
{
// 打印内容
for (int j = 1; j <= col; ++j)
printf("%2c ", arr[i][j]); // 内容右对齐
}
printf("\n");
}
}
2.2.3 放置雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
//随机生成雷的坐标
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
2.2.4 查找雷
游戏胜利的条件就是找到所有的雷,那也就是将除了雷以外的所有棋盘全部都探查完毕。所以我们最多可以查找棋盘的大小 - 雷的数量次。然后要让玩家输入需要排查的坐标并判定合法性(有没有越界),如果是雷,打印出相应提示语并结束游戏,如果不是雷则查找此坐标附近有多少雷,并标记,如果循环结束都没有踩到雷那么通关游戏。
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
//计算一共可输入的次数(棋盘大小 - 雷的数量)
int cnf = (row * col) - EASY_COUNT;
while (cnf)
{
int x = 0, y = 0;
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
//判断坐标的合法性
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (mine[x][y] == '0')
{
//该坐标不是雷 计算附近有几颗雷
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, row, col);
cnf--;
}
else
{
show[x][y] = '#';
printf("很遗憾,你被炸死了,游戏结束\n");
break;
}
}
else
printf("坐标不合法,请重新输入\n");
}
if (cnf == 0) printf("恭喜你,通关游戏\n");
}
在上述代码中,if (mine[x][y] == '0') 也就是没踩到雷时,需要将该坐标标记为附近九宫格内的雷的数量,所以我们还需要写一个查找九宫格雷的函数GetMineCount()
int GetMineCount(char arr[ROWS][COLS], int x, int y)
{
int count = 0;
//将九宫格内的所有值相加,计算有几颗雷
for (int i = x - 1; i <= x + 1; ++i)
{
for (int j = y - 1; j <= y + 1; ++j)
{
if (arr[i][j] == '1')
count++;
}
}
return count;
}
2.3 game.h
在上述所有代码中,我们还需要一个头文件来声明所有的函数
#include<stdio.h>
#include<stdlib.h>
//可操作棋盘范围
#define ROW 9
#define COL 9
//实际棋盘范围(包含行 列)
#define ROWS ROW+2
#define COLS COL+2
//放置的雷的数量
#define EASY_COUNT 10
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);
void DisplayBoard(char arr[ROWS][COLS], int row, int col);
void SetMine(char arr[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
这样就不会让其他文件显得非常繁杂,但是需要使用这个文件的话,得需要在 test.c 和 game.c 文件中引入这个文件头文件
#include "game.h"
这样一个简易扫雷游戏就完成了
完