这是一款9*9的扫雷游戏
扫雷游戏
1.需求分析
我们需要编写一款C语言扫雷游戏 ,包含
9*9
自定义格式
等游戏格式
注意: (由于创作者水平有限)**!!**本款游戏未能实现UI设计前端知识
。
-
游戏可以通过菜单实现继续玩或者退出游戏
-
游戏的棋盘格式是
9x9
-
默认随机布置10颗雷
-
可以排查雷
--如果不是雷,则显示周边有几颗雷
--如果是雷,则炸死游戏结束
--把除了雷之外的所有非雷格子都找出来,则排雷成功,游戏结束
--游戏可以反复玩 -
甚至我们可以拓展多种玩法,例如:
11x11
、15x15
、计时模式
-
游戏界面
2.程序框架设计
- 扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构(C语言讲完之后会讲到)来存储这些信息。
- 因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们首先想到的就是创建一个
9*9
的数组来存放布置雷的信息 - 那如果这个位置布置雷,我们就存放1,没有布置雷就存放0.
我们先看上面这个9x9的格子
我们访问(8,9)这个坐标的时候,周围的⼀圈8个位置,统计周围雷的个数时,最下面的三个坐标就会越界,为了防止越界,我们在设计的时候,给数组扩大⼀圈,雷还是布置在中间的9*9的坐标上,周围⼀圈不去布置雷就行,这样就解决了越界的问题。所以我们将存放数据的数组创建成
11*11
是⽐较合适。
- 再定义一个符号数组用来存放排查出的雷的信息
我们可以将数组初始化为
*
3.分函数实现
打印游戏菜单界面
c
void menu() {
printf("***********************\n");
printf("***** 1. play *****\n"); // 选择1开始游戏
printf("***** 0. exit *****\n"); // 选择0退出游戏
printf("***********************\n");
}
游戏主逻辑函数
c
void game() {
// 定义两个二维数组:
// mine数组:存储地雷的真实分布('0'无雷,'1'有雷)
// show数组:存储玩家可见的信息('*'未排查,数字表示周围雷数)
char mine[ROWS][COLS]; // 实际地雷分布棋盘
char show[ROWS][COLS]; // 玩家可见棋盘
// 初始化棋盘:
// mine数组全部初始化为'0'(表示初始时没有地雷)
// show数组全部初始化为'*'(表示所有位置都未排查)
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 在mine数组中随机布置地雷(数量由EASY_COUNT决定)
SetMine(mine, ROW, COL);
// 打印玩家可见的棋盘(调试时可取消注释查看地雷分布)
// DisplayBoard(mine, ROW, COL); // 打印真实地雷分布(用于调试)
DisplayBoard(show, ROW, COL); // 打印玩家可见棋盘
// 开始玩家排雷过程
FindMine(mine, show, ROW, COL);
}
程序主入口
c
int main() {
int input = 0; // 存储用户输入的菜单选择
// 设置随机数种子(使用当前时间确保每次运行随机性不同)
srand((unsigned int)time(NULL));
// 主游戏循环(至少执行一次)
do {
menu(); // 打印菜单
printf("请选择:>"); // 提示用户输入
scanf_s("%d", &input); // 读取用户选择
// 根据用户选择执行不同操作
switch (input) {
case 1: // 选择1:开始游戏
game(); // 调用游戏主函数
break;
case 0: // 选择0:退出游戏
printf("退出游戏\n");
break;
default: // 其他输入:提示错误
printf("选择错误,请重新选择\n");
break;
}
} while (input); // 当input不为0时继续循环
return 0; // 程序正常退出
}
初始化游戏棋盘
c
// board: 二维数组表示的棋盘
// rows: 棋盘的行数
// cols: 棋盘的列数
// set: 初始化时填充的字符(通常'0'表示无雷,'1'表示有雷)
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++) // 遍历每一行
{
int j = 0;
for (j = 0; j < cols; j++) // 遍历每一列
{
board[i][j] = set; // 将当前格子设置为指定字符
}
}
}
随机布置地雷
c
// mine: 表示雷区的二维数组
// row: 有效行数
// col: 有效列数
void SetMine(char mine[ROWS][COLS], int row, int col)
{
// 需要布置的地雷数量(EASY_COUNT可能是头文件中定义的常量,如10)
int count = EASY_COUNT;
while (count) { // 循环直到布置完所有地雷
// 生成随机坐标(1-row和1-col范围内)
int x = rand() % row + 1; // 1到row的随机数
int y = rand() % col + 1; // 1到col的随机数
// 检查该位置是否已经有雷
if (mine[x][y] == '0') // '0'表示无雷
{
mine[x][y] = '1'; // '1'表示有雷
count--; // 成功布置一个雷,计数器减1
}
}
显示当前棋盘状态
c
// board: 要显示的棋盘(可能是雷区或玩家可见区)
// row: 棋盘的有效行数(通常从1开始)
// col: 棋盘的有效列数(通常从1开始)
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
printf("-------扫雷-------\n");
// 打印列号(0-col)
for (int j = 0; j <= col; j++) {
printf("%d ", j); // 打印列索引(0到col)
}
printf("\n");
// 打印每一行内容(行号+棋盘内容)
for (int i = 1; i <= row; i++) {
printf("%d ", i); // 打印行号(1到row)
// 打印该行每个格子的内容
for (int j = 1; j <= col; j++) {
printf("%c ", board[i][j]); // 打印棋盘内容
}
printf("\n"); // 换行到下一行
}
}
计算指定位置周围的地雷数量
c
// mine: 雷区数组
// x: 行坐标
// y: 列坐标
// 返回:周围8个格子的地雷总数
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
// 将周围8个格子的字符值相加('0'=48,'1'=49),然后减去8*'0'得到实际数字
return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
}
玩家排雷主逻辑
c
// mine: 真实的雷区数组
// show: 玩家看到的棋盘(显示已排查区域)
// row: 有效行数
// col: 有效列数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0, y = 0; // 玩家输入的坐标
int win = 0; // 已安全排查的格子计数
// 游戏循环:当已排查的安全格子数 < 总格子数-地雷数时继续
while (win < row * col - EASY_COUNT) {
printf("请输入要排查的坐标:");
scanf_s("%d %d", &x, &y); // 读取玩家输入
// 检查坐标是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col) {
// 如果踩中地雷
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col); // 显示全部地雷位置
break; // 结束游戏
}
else {
// 计算并显示周围地雷数量
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0'; // 将数字转换为ASCII字符(如1→'1')
DisplayBoard(show, row, col);
win++; // 成功排查一个安全格子,计数器加1
}
}
else {
printf("输入的坐标非法,请重新输入\n"); // 坐标超出范围提示
}
}
// 胜利条件判断:当所有安全格子都被排查
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, row, col); // 显示地雷位置
}
}
4.分文件实现
这一次我们分三个文件来讲解
test.c 文件中写文件的测试逻辑 game.c 文件中写函数的实现 game.h 文件中写程序需要的数据类型和函数声明
(1)test.c
c
#include <stdio.h> // 标准输入输出库
#include "game.h" // 包含自定义的游戏头文件(定义常量、函数声明等)
#include <stdlib.h> // 包含rand()和srand()函数
#include <time.h> // 包含time()函数用于生成随机数种子
// 打印游戏菜单界面
void menu() {
printf("***********************\n");
printf("***** 1. play *****\n"); // 选择1开始游戏
printf("***** 0. exit *****\n"); // 选择0退出游戏
printf("***********************\n");
}
// 游戏主逻辑函数
void game() {
// 定义两个二维数组:
// mine数组:存储地雷的真实分布('0'无雷,'1'有雷)
// show数组:存储玩家可见的信息('*'未排查,数字表示周围雷数)
char mine[ROWS][COLS]; // 实际地雷分布棋盘
char show[ROWS][COLS]; // 玩家可见棋盘
// 初始化棋盘:
// mine数组全部初始化为'0'(表示初始时没有地雷)
// show数组全部初始化为'*'(表示所有位置都未排查)
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 在mine数组中随机布置地雷(数量由EASY_COUNT决定)
SetMine(mine, ROW, COL);
// 打印玩家可见的棋盘(调试时可取消注释查看地雷分布)
// DisplayBoard(mine, ROW, COL); // 打印真实地雷分布(用于调试)
DisplayBoard(show, ROW, COL); // 打印玩家可见棋盘
// 开始玩家排雷过程
FindMine(mine, show, ROW, COL);
}
// 程序主入口
int main() {
int input = 0; // 存储用户输入的菜单选择
// 设置随机数种子(使用当前时间确保每次运行随机性不同)
srand((unsigned int)time(NULL));
// 主游戏循环(至少执行一次)
do {
menu(); // 打印菜单
printf("请选择:>"); // 提示用户输入
scanf_s("%d", &input); // 读取用户选择
// 根据用户选择执行不同操作
switch (input) {
case 1: // 选择1:开始游戏
game(); // 调用游戏主函数
break;
case 0: // 选择0:退出游戏
printf("退出游戏\n");
break;
default: // 其他输入:提示错误
printf("选择错误,请重新选择\n");
break;
}
} while (input); // 当input不为0时继续循环
return 0; // 程序正常退出
}
(2)game.c
c
#include "game.h" // 包含自定义的游戏头文件,可能定义了ROWS、COLS、EASY_COUNT等常量
// 初始化游戏棋盘
// board: 二维数组表示的棋盘
// rows: 棋盘的行数
// cols: 棋盘的列数
// set: 初始化时填充的字符(通常'0'表示无雷,'1'表示有雷)
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++) // 遍历每一行
{
int j = 0;
for (j = 0; j < cols; j++) // 遍历每一列
{
board[i][j] = set; // 将当前格子设置为指定字符
}
}
}
// 显示当前棋盘状态
// board: 要显示的棋盘(可能是雷区或玩家可见区)
// row: 棋盘的有效行数(通常从1开始)
// col: 棋盘的有效列数(通常从1开始)
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
printf("-------扫雷-------\n");
// 打印列号(0-col)
for (int j = 0; j <= col; j++) {
printf("%d ", j); // 打印列索引(0到col)
}
printf("\n");
// 打印每一行内容(行号+棋盘内容)
for (int i = 1; i <= row; i++) {
printf("%d ", i); // 打印行号(1到row)
// 打印该行每个格子的内容
for (int j = 1; j <= col; j++) {
printf("%c ", board[i][j]); // 打印棋盘内容
}
printf("\n"); // 换行到下一行
}
}
// 随机布置地雷
// mine: 表示雷区的二维数组
// row: 有效行数
// col: 有效列数
void SetMine(char mine[ROWS][COLS], int row, int col)
{
// 需要布置的地雷数量(EASY_COUNT可能是头文件中定义的常量,如10)
int count = EASY_COUNT;
while (count) { // 循环直到布置完所有地雷
// 生成随机坐标(1-row和1-col范围内)
int x = rand() % row + 1; // 1到row的随机数
int y = rand() % col + 1; // 1到col的随机数
// 检查该位置是否已经有雷
if (mine[x][y] == '0') // '0'表示无雷
{
mine[x][y] = '1'; // '1'表示有雷
count--; // 成功布置一个雷,计数器减1
}
}
}
// 计算指定位置周围的地雷数量
// mine: 雷区数组
// x: 行坐标
// y: 列坐标
// 返回:周围8个格子的地雷总数
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
// 将周围8个格子的字符值相加('0'=48,'1'=49),然后减去8*'0'得到实际数字
return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
}
// 玩家排雷主逻辑
// mine: 真实的雷区数组
// show: 玩家看到的棋盘(显示已排查区域)
// row: 有效行数
// col: 有效列数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0, y = 0; // 玩家输入的坐标
int win = 0; // 已安全排查的格子计数
// 游戏循环:当已排查的安全格子数 < 总格子数-地雷数时继续
while (win < row * col - EASY_COUNT) {
printf("请输入要排查的坐标:");
scanf_s("%d %d", &x, &y); // 读取玩家输入
// 检查坐标是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col) {
// 如果踩中地雷
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col); // 显示全部地雷位置
break; // 结束游戏
}
else {
// 计算并显示周围地雷数量
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0'; // 将数字转换为ASCII字符(如1→'1')
DisplayBoard(show, row, col);
win++; // 成功排查一个安全格子,计数器加1
}
}
else {
printf("输入的坐标非法,请重新输入\n"); // 坐标超出范围提示
}
}
// 胜利条件判断:当所有安全格子都被排查
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, row, col); // 显示地雷位置
}
}
(3)game.h
c
#pragma once // 防止头文件被重复包含
// 标准库头文件
#include <stdio.h> // 提供输入输出函数(如printf、scanf)
#include <stdlib.h> // 提供内存分配、随机数等函数(如rand、srand)
#include <time.h> // 提供时间相关函数(如time用于随机数种子)
/***********************
* 游戏难度配置宏定义
* 通过地雷数量控制难度级别
***********************/
#define EASY_COUNT 10 // 简单难度 - 10个地雷
#define MIDIEA_COUNT 30 // 中等难度 - 30个地雷(注意拼写应为MEDIUM)
#define HIGH_COUNT 50 // 困难难度 - 50个地雷
/***********************
* 棋盘尺寸宏定义
* 采用"大一圈"的数组设计便于边界处理
***********************/
#define ROW 9 // 游戏可见区域的行数(玩家实际操作区域)
#define COL 9 // 游戏可见区域的列数
#define ROWS ROW+2 // 实际数组行数 = 可见行 + 2(上下各多一行边界)
#define COLS COL+2 // 实际数组列数 = 可见列 + 2(左右各多一列边界)
/***********************
* 函数声明部分
* 描述各模块的核心功能
***********************/
/**
* @brief 初始化游戏棋盘
* @param board 目标二维数组
* @param rows 数组总行数(应使用ROWS)
* @param cols 数组总列数(应使用COLS)
* @param set 初始化填充字符
* @note 将整个数组(包括边界)初始化为set字符
* 通常:mine数组用'0',show数组用'*'
*/
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
/**
* @brief 打印游戏棋盘
* @param board 要打印的二维数组
* @param row 可见区域行数(应使用ROW)
* @param col 可见区域列数(应使用COL)
* @note 会显示行列坐标(1-9)
* 只打印内部9x9区域(忽略最外圈边界)
*/
void DisplayBoard(char board[ROWS][COLS], int row, int col);
/**
* @brief 随机布置地雷
* @param board 地雷分布数组(通常为mine数组)
* @param row 可见区域行数(应使用ROW)
* @param col 可见区域列数(应使用COL)
* @note 在1-9行/列范围内随机布置地雷
* 地雷用'1'表示,安全格用'0'
* 实际使用时应配合EASY_COUNT等难度常量
*/
void SetMine(char board[ROWS][COLS], int row, int col);
/**
* @brief 玩家排雷核心逻辑
* @param mine 真实地雷分布数组
* @param show 玩家可见的棋盘数组
* @param row 可见区域行数(应使用ROW)
* @param col 可见区域列数(应使用COL)
* @note 处理玩家输入、胜负判断、棋盘更新
* 包含游戏主循环,直到胜利或踩雷
*/
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
5.运行效果展示

如果你觉得这篇文章对你有帮助
请给个三连支持一下哦
