C语言实现简易版扫雷游戏详解与代码

方予
方予
发布时间:2026-10-16 | 6人浏览过
原创

本文使用C语言实现一个简易版的扫雷游戏,涵盖菜单制作、二维数组操作、随机数生成及多文件模块化设计。

菜单设计与主循环控制

游戏启动时,首先需要通过文字界面提供交互选项。使用 printf 函数输出清晰的菜单引导玩家选择“开始游戏”或“退出游戏”。为了实现多次游玩而不需重复启动程序,在 main 函数中引入 do-while 循环,配合 switch-case 语句对用户的输入进行分流:若输入 1 则调用核心游戏函数;若输入 0 则正常退出;输入其他非法值则提示错误。

这种结构保证了程序的稳健性,确保只有在用户明确选择退出或游戏结束后才会停止运行。

棋盘的创建与内存布局

为了让游戏具备灵活性,行列数不宜写死。建议采用预处理指令 #define 定义逻辑行/列数(如9x9),同时为了规避数组访问时的越界问题,物理存储时会创建一个比逻辑尺寸大一圈的“保护圈”棋盘(如11x11)。这样,玩家在边缘点击坐标时,逻辑判断不会触碰无效的下标位置。

扫雷游戏涉及两套信息:一套是底层真实的雷分布图(mine),另一套是呈现给玩家的排查视图(show)。将两者分离能有效防止作弊,因为直接读取同一个数组会让玩家一眼看穿雷的位置。

二维数组的初始化

在开始之前,必须把棋盘内存清空。由于需要针对不同的显示需求(比如雷盘填 '0' 表示无雷,展示盘填 '*' 表示未翻开),可以封装一个通用的初始化函数。该函数接收二维数组指针、行列总数以及初始化的目标字符,利用嵌套的 for 循环遍历整个矩阵完成赋值。

棋盘信息的渲染输出

控制台界面需要直观易懂。打印函数除了展示矩阵内容外,还应附带行号和列号的提示信息,帮助玩家快速定位坐标。为了避免视觉干扰,通常在输出的第一行和最后一行各加一条分隔线。

注意打印循环应从下标 1 开始遍历,跳过作为“保护圈”的第 0 行和第 N+1 行,从而保证输出内容与实际游戏盘面一致。

安全布置地雷逻辑

布雷操作同样应当支持自定义数量。设定一个宏常量记录雷的数量后,借助 while 循环直到雷数达标。核心难点在于随机坐标的生成:
1. 调用 srand((unsigned int)time(NULL)) 播种,否则每次运行的随机序列将完全相同。
2. 利用 rand() % ROW + 1 将生成的随机大数映射到合法的行列区间内(假设从1计数)。
3. 检查该位置是否已被标记为雷(即是否为 '0'),如果不是 '0' 则将其赋值为 '1' 并累加计数器。

核心玩法:排查地雷与统计逻辑

进入排雷阶段后,程序处于持续监听用户输入的状态。首先需要校验输入的坐标是否在合法的游戏区域 [1, 9] × [1, 9] 内,若不合法则抛出警告。

如果命中地雷(mine[x][y] == '1'),则宣布失败,并全盘揭示所有地雷位置让玩家明白输在了哪里,随即中断循环。若点开了空白处,则需要计算该坐标周围八个相邻格子内共有多少颗雷。

这里涉及到一种巧妙的 ASCII 码运算技巧:如果网格里的数据是字符 '0'-'1',而你想得到整型数字用于统计,可以通过 `当前字符 - '0'` 的方式来强制转换。将周围八个格子的结果求和后,再加上自身字符减去 '0',最终得到的就是一个纯数字。

将该数字转化回字符形式(加上 '0')填入展示盘,更新界面,同时将胜利计数变量加一。当成功排除的格子数达到总非雷区数量时,触发胜利条件,宣告游戏结束。

全部源码示例

以下代码采用了模块化编写的思路,包含接口声明、功能实现及测试入口,可直接保存编译运行。

// test.c 测试入口文件
#include "game.h"

void menu()
{
	printf("**************************\n");
	printf("****    1.play    ********\n");
	printf("****    0.exit    ********\n");
	printf("**************************\n");
}

// 游戏核心逻辑
void game()
{
	// 定义两个11x11的二维字符数组
	char mine[ROWS][COLS]; 	 // 存放雷的信息 (1代表雷, 0代表安全)
	char show[ROWS][COLS]; 	 // 展示给玩家的信息 (*代表未展开, 数字代表周围雷数)

	// 1. 初始化两个棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	// 2. 布置预设数量的雷
	SetMineBoard(mine, ROW, COL);

	// 3. 打印初始状态
	DisplayBoard(show, ROW, COL);

	// 4. 执行扫雷逻辑
	FindBoard(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	// 使用时间戳对随机数种子进行初始化,确保随机性
	srand((unsigned int)time(NULL));

	do
	{
		menu();
		printf("请选择: ");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			game(); // 进入游戏流程
			break;
		case 0:
			printf("正在退出游戏...\n");
			break;
		default:
			printf("输入有误,请重新选择!\n");
			break;
		}
	} while (input);

	return 0;
}

// ==============================
// game.h 接口声明文件
// ==============================
#pragma once
#include 
#include 
#include 

// 逻辑棋盘尺寸
#define ROW 9
#define COL 9
// 物理存储尺寸(四周多一圈用于辅助判断边界)
#define ROWS (ROW + 2)
#define COLS (COL + 2)
// 雷的总数
#define EASY_COUNT 10

// 函数原型声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMineBoard(char board[ROWS][COLS], int row, int col);
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

// ==============================
// game.c 功能实现文件
// ==============================
#include "game.h"

// 填充初始化函数:按指定字符填满二维数组
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

// 绘制棋盘函数:输出带坐标索引的控制台画面
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-----------------------------\n");
	// 打印顶部列号表头
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

	// 逐行打印表格内容
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i); // 左侧打印行号
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-----------------------------\n");
}

// 布雷函数:随机放置指定数量的雷
void SetMineBoard(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = EASY_COUNT; // 剩余需要布置的雷数

	while (count > 0)
	{
		// rand产生[0, MAX]之间的随机数,取模得到[0, row-1]范围,再+1变成[1, row]
		x = rand() % row + 1;
		y = rand() % col + 1;

		// 避免在同一位置重复布雷
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

// 获取某一点周围八格的雷数总和
int GetMineCount(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1]
		+ board[x][y - 1] + board[x][y + 1]
		+ board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0');
}

// 扫雷主逻辑函数
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0; // 已排查的安全格子数量

	// 只要未翻开的格子还大于雷数,就继续循环
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入要排查的坐标(如 1 1): ");
		scanf("%d %d", &x, &y);

		// 坐标合法性校验
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			// 踩雷判定
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你踩到了雷!游戏结束。
");
				DisplayBoard(mine, ROW, COL); // 显示真实雷区布局
				break;
			}
			else
			{
				// 安全,计算周围雷数并更新展示盘
				show[x][y] = GetMineCount(mine, x, y) + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("您输入的坐标不在有效范围内,请重新输入!\n");
		}
	}

	// 胜利判定:翻出的格子数量等于总格子减去雷的数量
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜您,排雷成功!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

以上就是C语言实现简易版扫雷游戏的详细内容,更多关于C语言扫雷模块开发的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表