C语言实现经典扫雷游戏:核心逻辑与代码详解
本文详细解析利用C语言开发9x9扫雷游戏的完整过程,涵盖棋盘数据结构设计(规避越界)、地雷随机生成以及基于递归思想的区域展开算法。
项目结构与核心数据
在实现扫雷游戏前,首先需要确定底层的数据存储方式。通常扫雷界面是 9x9 的格子,为了方便处理边缘坐标的邻居判断(避免每次都要判断是否越界),我们在实际编码中常采用 11x11 的二维数组。
整个程序分为三个主要文件:
test.c:包含游戏主控流程,负责菜单显示和游戏循环。game.h:存放头文件引入、宏定义(如行列数、雷数)以及函数声明。game.c:实现具体的游戏逻辑,包括初始化、打印、埋雷和排雷等。
准备工作:头文件与常量定义
在开始编码前,需在 game.h 中统一定义常量,确保后续逻辑中的行列数和雷数保持统一。此外,需要引入 time.h 用于生成随机数种子。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 实际玩家视角的棋盘大小
#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 SetMine(char mine[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);第1步:初始化棋盘
扫雷游戏有两个核心矩阵:mine 用于存放真实的地雷信息('1'代表有雷,'0'代表无雷),show 用于显示给玩家的排查信息(默认用 '*' 表示未翻开)。
初始化时,我们需要将两个数组分别填入对应的初始字符。为了提高复用性,我们将待填充的字符作为参数传入。
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0, j = 0;
for(i=0; i<rows; i++)
{
for(j=0; j<cols; j++)
{
board[i][j] = set;
}
}
}第2步:打印并显示棋盘
为了让玩家能直观看到游戏状态,需编写一个按格式输出二维数组的函数。输出时需带上行号和列号的提示。
在测试阶段,我们可以选择性地打印 mine 数组来验证布雷效果;但在正式发布给玩家时,只应展示 show 数组的内容。 
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
printf("--------扫雷游戏--------\n");
// 打印表头(列号)
for(j=0; j<=col; j++)
{
printf("%d ", j);
}
printf("\n");
// 逐行输出数据,注意起始下标通常为1
for(i=1; i<=row; i++)
{
printf("%d ", i);
for(j=1; j<=col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("----------------------\n");
}第3步:设置地雷
地雷的位置必须是随机的。我们需要调用 srand((unsigned int)time(NULL)) 设置一次随机数种子,然后在指定区域内多次生成随机坐标放置地雷。
由于数组定义为 11x11,但有效玩家区域为 9x9,所以在布雷时只需在 [1~9] 范围内生成坐标即可。
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while(count)
{
// 生成 1-9 之间的随机坐标
int x = rand() % row + 1;
int y = rand() % col + 1;
// 如果该位置尚未布置雷,则置为 '1'
if(mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}第4步:排雷核心逻辑
这是最复杂的部分,涉及用户交互、坐标合法性检查、胜负判定以及递归展开算法。
统计周围雷数
当玩家翻开一个格子后,需要计算其周围8个格子的雷总数,并在 show 盘面上显示该数字。通过累加周围8个字符的ASCII值并减去 8 * '0' 可以快速得到结果。
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
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';
}实现递归展开(UnfoldMine)
当玩家踩到的格子周围雷数为0时,为了提升游戏体验,通常会自动翻开周围一圈。这就需要用递归实现。递归的三个关键点如下:
- 终止条件(不继续递归的情况):如果当前坐标
x,y已经被翻开过,或者已经被标记为旗帜,则停止递归。 - 非雷分支:如果周围雷数不为0,则在盘面显示数字,并将剩余可获胜位置减1。
- 零雷分支:如果周围雷数为0,则将当前位置设为空格,并对周围8个相邻坐标再次调用自身函数进行展开。
void UnfoldMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col, int* win)
{
int count = GetMineCount(mine, x, y);
// 如果是第一次展开且不是雷
if(show[x][y] == '*')
{
(*win)--; // 更新未排查位置计数器
if(count > 0)
{
show[x][y] = count + '0'; // 显示周围雷数
}
else
{
show[x][y] = ' '; // 清空显示
// 向周围八个方向继续递归
for(int i=-1; i<=1; i++)
{
for(int j=-1; j<=1; j++)
{
if(x+i<=row && x+i>=1 && y+j<=col && y+j>=1)
{
UnfoldMine(mine, show, x+i, y+j, row, col, win);
}
}
}
}
}
}主排雷循环与标记功能
完整的 FindMine 函数还需要整合用户的输入流程,支持两种操作:0 表示直接排雷,1 表示插旗标记(通常使用 # 或 * 符号区分已翻开和已标记)。
当所有非雷格子都被成功翻开时,游戏胜利;一旦踩中 '1',则游戏结束并显示全图。
总结
通过以上步骤,我们完成了一个具备基础功能的命令行版扫雷。该版本的核心在于理解二维数组索引与视觉界面的映射,以及如何利用递归优雅地处理大面积空白区域的自动展开。以上就是C语言实现扫雷排雷小游戏的详细内容,更多关于C语言小游戏开发的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









