主题:扫雷游戏开发,包括排雷阶段的两个核心问题及解决方案(方阵拓展和双数组设计),以及游戏主体的组装和完整代码实现
本文Gitee链接:2025.11.13https://gitee.com/donghua-wanli/blog-code/tree/master/2025.11.13
https://gitee.com/donghua-wanli/blog-code/tree/master/2025.11.13
书接上回 :我们已完成雷区布置(10个雷)并打印布雷方阵,本章将聚焦排雷功能实现,解决开发中的核心问题,并整合完整代码。
目录
-
排雷阶段的两个核心问题
-
解决方案1:方阵拓展(解决边界排查差异)
-
2.1 方阵拓展的核心思路
-
2.2 头文件(game.h)宏定义修改
-
2.3 核心函数适配(初始化、打印、布雷)
-
-
解决方案2:双数组设计(解决数值歧义)
-
3.1 双数组的核心逻辑
-
3.2 字符数组初始化函数(chushihua2)
-
3.3 排雷函数(paicha)实现(含坐标校验、雷数计算)
-
-
游戏主体(game函数)组装与 extern 关键字应用
-
扫雷游戏完整代码(game.h / game.c / 11.13.c)
-
结尾
一、排雷阶段的两个核心问题
排雷的基础逻辑是两步:
-
判断目标坐标是否为雷(是则游戏结束,否则进入下一步);
-
计算目标坐标周围的雷数并展示。
但实际开发中遇到两个致命问题:
-
问题1(边界排查差异) :目标坐标在中间(8个方向)、边上(5个方向)、顶点(3个方向) 时,需写3个不同排查逻辑,程序复杂;
-
问题2(数值歧义):用同一数组存储"雷(1)/非雷(0)"和"周围雷数(0-8)"时,无法区分"非雷(0)"和"周围雷数为0",后续排查会受干扰。
二、解决方案1:方阵拓展(解决边界排查差异)
2.1 方阵拓展的核心思路
<span style="color:red; font-weight:bold;">核心逻辑:将9×9游戏方阵向外拓展1圈,形成11×11方阵</span>
-
仅在中间9×9区域布置雷,拓展的外圈全部设为"非雷(0)";
-
此时无论目标坐标在原9×9的哪个位置,都可统一按"中间区域"排查(即检查周围8个方向),无需区分边界/顶点,彻底简化逻辑。
这也是之前将方阵行、列定义为9的"笨拙之处"------用宏定义(#define)可轻松修改方阵大小。
2.2 头文件(game.h)宏定义修改
只需修改行(Hang)、列(Lie)的宏定义,其他声明暂不变:
cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 关键修改:从9改为11(拓展为11×11方阵)
#define Hang 11
#define Lie 11
// 函数声明(后续会补充)
void chushihua(int landmine[Hang][Lie]);// 整形数组初始化(存雷)
void zhanshi(int landmine[Hang][Lie]);// 打印数组
void setmine(int landmine[Hang][Lie]);// 布置雷
2.3 核心函数适配(初始化、打印、布雷)
拓展方阵后,需调整3个核心函数的逻辑(仅修改关键部分,尽量保留原代码):
(1)初始化函数(chushihua)------无需修改
因拓展的外圈需设为"非雷(0)",而原初始化逻辑是将所有元素设为0,完全符合需求:
cpp
void chushihua(int landmine[Hang][Lie])// 整形数组初始化(存雷)
{
int a;
for (a = 0; a < Hang; a++)
{
int b;
for (b = 0; b < Lie; b++)
{
landmine[a][b] = 0; // 外圈默认非雷,无需额外修改
}
}
}
(2)打印函数(zhanshi)------仅打印中间9×9区域
拓展后方阵为11×11,但玩家只需看原9×9游戏区,因此循环条件改为"1 ≤ 索引 ≤ 9"(即跳过外圈):
cpp
void zhanshi(int landmine[Hang][Lie])
{
int a;
// 关键修改:从0→Hang改为1→Hang-1(跳过外圈,打印中间9×9)
for (a = 1; a < Hang - 1; a++)
{
int b;
for (b = 1; b < Lie - 1; b++)
{
printf("%d", landmine[a][b]);
}
printf("\n");
}
}
(3)布雷函数(setmine)------仅在中间9×9布雷
随机生成的坐标需限定在"1 ≤ x/y ≤ 9",避免雷布到外圈,修改随机数生成逻辑:
cpp
void setmine(int landmine[Hang][Lie])
{
int count = 10; // 共10个雷
while (count)
{
// 关键修改:rand()%(Hang-2)+1 → 生成1~9的随机数(跳过外圈)
int x = rand() % (Hang - 2) + 1;
int y = rand() % (Lie - 2) + 1;
if (landmine[x][y] == 0) // 确保不重复布雷
{
landmine[x][y] = 1;
count--;
}
}
}
三、解决方案2:双数组设计(解决数值歧义)
3.1 双数组的核心逻辑
<span style="color:red; font-weight:bold;">核心思路:用两个数组分别存储"雷的位置"和"排查结果"</span>
-
int landmine[Hang][Lie]:整形数组,仅存"雷(1)/非雷(0)",不对外展示; -
char showmine[Hang][Lie]:字符数组,对外展示------未排查用'*',排查后用字符数字(如'2'表示周围2个雷),彻底避免歧义。
3.2 字符数组初始化函数(chushihua2)
需新增一个函数,将showmine数组全部初始化为'*'(表示未排查),并在game.h中声明:
(1)game.h 补充声明
cpp
// 新增:字符数组初始化(存排查结果)
void chushihua2(char landmine[Hang][Lie]);
// 修改:打印函数参数改为字符数组(后续仅展示排查结果)
void zhanshi(char showmine[Hang][Lie]);
(2)chushihua2 函数实现
cpp
void chushihua2(char landmine[Hang][Lie])// 字符数组初始化(存排查结果)
{
int a;
for (a = 0; a < Hang; a++)
{
int b;
for (b = 0; b < Lie; b++)
{
landmine[a][b] = '*'; // 所有位置默认未排查(*)
}
}
}
3.3 排雷函数(paicha)实现
排雷函数是核心,需完成坐标校验、雷判断、雷数计算、结果存储,步骤如下:
(1)game.h 声明排雷函数
cpp
// 排雷函数:参数为"雷数组"和"排查结果数组"
void paicha(int landmine[Hang][Lie], char showmine[Hang][Lie]);
(2)paicha 函数实现(关键逻辑标注)
cpp
void paicha(int landmine[Hang][Lie], char showmine[Hang][Lie])
{
int a;
int b;
printf("请输入排查的坐标:");
chonglai:
scanf("%d%d", &a, &b);
if (a >= 1 && a <= 9 && b >= 1 && b <= 9)
{
if (landmine[a][b] == 1)
{
printf("你踩到雷了,炸死,游戏结束");
return;
}
else
{
// 新增:判断坐标是否已排查
if (showmine[a][b] != '*')
{
printf("该坐标已排查,请重新输入:");
goto chonglai;
}
int c = 0; // 存储周围雷数
// 后续雷数计算逻辑不变...
int i;
for (i = a - 1; i <= a + 1; i++)
{
int j;
for (j = b - 1; j <= b + 1; j++)
{
c += landmine[i][j];
}
}
showmine[a][b] = c + 48;
}
}
else
{
printf("输入错误,请输入符合格式的坐标:");
goto chonglai;
}
}
(ASCII转换原理:字符'0'的ASCII值为48,因此c=2时,2+48=50对应字符'2',直接赋值给字符数组即可展示)
四、游戏主体(game函数)组装与 extern 关键字应用
4.1 问题:数组跨文件访问
landmine和showmine数组在11.13.c中定义,但game函数在game.c中实现,编译器会提示"未定义数组"。
解决方案 :用extern关键字在game.c开头声明数组(表示"数组在其他文件中定义,此处仅引用")。
4.2 game函数实现(整合所有逻辑)
(1)game.h 声明 game 函数
cpp
// 游戏主体函数
void game();
(2)game.c 中实现 game 函数
cpp
// 关键:用extern声明数组(来自11.13.c)
extern int landmine[Hang][Lie];
extern char showmine[Hang][Lie];
void game()
{
// 步骤1:初始化两个数组
chushihua(landmine); // 初始化雷数组(0=非雷)
chushihua2(showmine); // 初始化排查结果数组(*=未排查)
// 步骤2:布置10个雷
setmine(landmine);
// 步骤3:排雷逻辑(需排71次非雷区域:9×9-10=71)
int success_count = 0; // 成功排雷次数
while (success_count < 71)
{
paicha(landmine, showmine); // 一次排雷
zhanshi(showmine); // 打印当前排查结果
success_count++;
}
// 步骤4:排完所有非雷,游戏胜利
if (success_count == 71)
{
printf("恭喜你排雷成功,游戏结束");
}
}
五、扫雷游戏完整代码
5.1 game.h(头文件:宏定义+函数声明)
cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 方阵大小:11×11(中间9×9为游戏区,外圈用于简化边界判断)
#define Hang 11
#define Lie 11
// 函数声明
void chushihua(int landmine[Hang][Lie]);// 整形数组初始化(存雷)
void chushihua2(char landmine[Hang][Lie]);// 字符数组初始化(存排查结果)
void setmine(int landmine[Hang][Lie]);// 布置雷
void paicha(int landmine[Hang][Lie], char showmine[Hang][Lie]);// 排雷
void zhanshi(char showmine[Hang][Lie]);// 打印排查结果
void game();// 游戏主体
5.2 game.c(函数实现)
cpp
extern int landmine[Hang][Lie];//制作存放雷的方阵:
extern char showmine[Hang][Lie];//制作存放排查结果的方阵:
#include"game.h"
void chushihua(int landmine[Hang][Lie])//将整形数组初始化
{
int a;
for (a = 0; a < Hang; a++)
{
int b;
for (b = 0; b < Lie; b++)
{
landmine[a][b] = 0;
}
}
}
void zhanshi(char showmine[Hang][Lie])
{
int a;
for (a = 1; a < Hang - 1; a++)
{
int b;
for (b = 1; b < Lie - 1; b++)
{
printf("%c", showmine[a][b]);
}
printf("\n");
}
}
void setmine(int landmine[Hang][Lie])
{
int count = 10;
while (count)
{
int x = rand() % (Hang - 2) + 1;
int y = rand() % (Lie - 2) + 1;
if (landmine[x][y] == 0)
{
landmine[x][y] = 1;
count--;
}
}
}
void paicha(int landmine[Hang][Lie], char showmine[Hang][Lie])
{
int a;
int b;
printf("请输入排查的坐标:");
chonglai:
scanf("%d%d", &a, &b);
if (a >= 1 && a <= 9 && b >= 1 && b <= 9)
{
if (landmine[a][b] == 1)
{
printf("你踩到雷了,炸死,游戏结束");
return;
}
else
{
// 新增:判断坐标是否已排查
if (showmine[a][b] != '*')
{
printf("该坐标已排查,请重新输入:");
goto chonglai;
}
int c = 0; // 存储周围雷数
// 后续雷数计算逻辑不变...
int i;
for (i = a - 1; i <= a + 1; i++)
{
int j;
for (j = b - 1; j <= b + 1; j++)
{
c += landmine[i][j];
}
}
showmine[a][b] = c + 48;
}
}
else
{
printf("输入错误,请输入符合格式的坐标:");
goto chonglai;
}
}
void chushihua2(char landmine[Hang][Lie])//将字符数组初始化
{
int a;
for (a = 0; a < Hang; a++)
{
int b;
for (b = 0; b < Lie; b++)
{
landmine[a][b] = '*';
}
}
}
void game()
{
chushihua(landmine);
chushihua2(showmine);//初始化两方阵
setmine(landmine);//布置雷
paicha(landmine, showmine);//排雷
int a = 0;
while (a<71)
{
zhanshi(showmine);//打印排查结果
a++;
}
if (a == 71)
{
printf("恭喜你排雷成功,游戏结束");
}
}
5.3 11.13.c(主函数:菜单+入口)
cpp
#include"game.h"
// 定义全局数组(供game.c用extern引用)
int x;
int landmine[Hang][Lie];// 存雷的整形数组
char showmine[Hang][Lie];// 存排查结果的字符数组
int main()
{
// 设置随机数种子(确保每次布雷位置不同)
srand((unsigned int)time(NULL));
// 游戏菜单
printf("*******************************************\n");
printf("*************** 扫雷游戏 *****************\n");
printf("*** 请选择:1.开始游玩 2.退出游戏 ***\n");
printf("*******************************************\n");
printf("*******************************************\n");
printf("*******************************************\n");
scanf("%d", &x);
switch (x)
{
case 1:
printf("************* 开始游戏 ***************\n");
game(); // 进入游戏主体
break;
case 2:
printf("************* 退出游戏 ***************\n");
break;
default:
printf("************* 输入错误 ***************\n");
break;
}
return 0;
}
六、结尾
嗯,希望能够得到你的关注,希望我的内容能够给你带来帮助,希望有幸能够和你一起成长。
写这篇博客的时候天气有点凉,冷风吹过阳台,裹着深夜的孤独。我走到阳台拍下了一张宿舍对面的照片作为本文的封面。