第十篇 扫雷游戏 下(末版·精简)

主题:扫雷游戏开发,包括排雷阶段的两个核心问题及解决方案(方阵拓展和双数组设计),以及游戏主体的组装和完整代码实现

本文Gitee链接:2025.11.13https://gitee.com/donghua-wanli/blog-code/tree/master/2025.11.13https://gitee.com/donghua-wanli/blog-code/tree/master/2025.11.13

书接上回 :我们已完成雷区布置(10个雷)并打印布雷方阵,本章将聚焦排雷功能实现,解决开发中的核心问题,并整合完整代码。

目录

  1. 排雷阶段的两个核心问题

  2. 解决方案1:方阵拓展(解决边界排查差异)

    1. 2.1 方阵拓展的核心思路

    2. 2.2 头文件(game.h)宏定义修改

    3. 2.3 核心函数适配(初始化、打印、布雷)

  3. 解决方案2:双数组设计(解决数值歧义)

    1. 3.1 双数组的核心逻辑

    2. 3.2 字符数组初始化函数(chushihua2)

    3. 3.3 排雷函数(paicha)实现(含坐标校验、雷数计算)

  4. 游戏主体(game函数)组装与 extern 关键字应用

  5. 扫雷游戏完整代码(game.h / game.c / 11.13.c)

  6. 结尾

一、排雷阶段的两个核心问题

排雷的基础逻辑是两步:

  1. 判断目标坐标是否为雷(是则游戏结束,否则进入下一步);

  2. 计算目标坐标周围的雷数并展示。

但实际开发中遇到两个致命问题:

  • 问题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 问题:数组跨文件访问

landmineshowmine数组在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;
}

六、结尾

嗯,希望能够得到你的关注,希望我的内容能够给你带来帮助,希望有幸能够和你一起成长。

写这篇博客的时候天气有点凉,冷风吹过阳台,裹着深夜的孤独。我走到阳台拍下了一张宿舍对面的照片作为本文的封面。

相关推荐
Sui_Network5 小时前
21shares 在纳斯达克推出 2 倍 SUI 杠杆 ETF(TXXS)
大数据·人工智能·游戏·金融·区块链
点金石游戏出海6 小时前
模拟品类洞察:全球市场格局稳健,何为引领未来增长的路径?
游戏·模拟游戏·海外市场·手游市场·游戏市场·模拟手游·市场分析
培根芝士11 小时前
使用Scripting API获取CS2游戏数据
python·游戏
vx_bisheyuange11 小时前
基于SpringBoot的游戏交易系统
spring boot·后端·游戏·毕业设计
da_vinci_x14 小时前
PS 3D Viewer:3D模型直接拖进画布?宣发美工的“降维打击”
游戏·3d·prompt·aigc·技术美术·建模·游戏美术
wanhengidc15 小时前
云手机 巨 椰 三角洲行动
运维·服务器·科技·游戏·智能手机
wanhengidc16 小时前
云手机 高算力 快速便捷
运维·服务器·科技·游戏·智能手机
我是程序汪16 小时前
程序汪3万18天接的塔防射击游戏小程序
游戏·小程序
春卷同学1 天前
足球游戏 - Electron for 鸿蒙PC项目实战案例
游戏·electron·harmonyos
码界奇点1 天前
基于Python与Pygame的多功能游戏系统设计与实现
python·游戏·毕业设计·pygame·源代码管理