第十篇 扫雷游戏 下(末版·精简)
主题:扫雷游戏开发,包括排雷阶段的两个核心问题及解决方案(方阵拓展和双数组设计),以及游戏主体的组装和完整代码实现
本文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)的宏定义,其他声明暂不变:
#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,完全符合需求:
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”(即跳过外圈):
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”,避免雷布到外圈,修改随机数生成逻辑:
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 补充声明
// 新增:字符数组初始化(存排查结果)
void chushihua2(char landmine[Hang][Lie]);
// 修改:打印函数参数改为字符数组(后续仅展示排查结果)
void zhanshi(char showmine[Hang][Lie]);
(2)chushihua2 函数实现
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 声明排雷函数
// 排雷函数:参数为“雷数组”和“排查结果数组”
void paicha(int landmine[Hang][Lie], char showmine[Hang][Lie]);
(2)paicha 函数实现(关键逻辑标注)
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 函数
// 游戏主体函数
void game();
(2)game.c 中实现 game 函数
// 关键:用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(头文件:宏定义+函数声明)
#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(函数实现)
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(主函数:菜单+入口)
#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;
}
六、结尾
嗯,希望能够得到你的关注,希望我的内容能够给你带来帮助,希望有幸能够和你一起成长。
写这篇博客的时候天气有点凉,冷风吹过阳台,裹着深夜的孤独。我走到阳台拍下了一张宿舍对面的照片作为本文的封面。
