C语言扫雷:递归展开逻辑详解与代码实现
深入解析 C 语言扫雷程序中“棋盘展开”这一核心算法。通过双棋盘设计区分信息隐藏,利用递归逻辑实现安全区域的自动连通展示,并附带完整的头文件、测试主程序及游戏逻辑源码。
项目结构与棋盘策略
在构建扫雷游戏时,首先需要解决两个核心需求:一是隐藏雷的位置,防止玩家作弊;二是展示已翻开的区域及其周围的雷数。为了实现这一点,代码通常采用两个二维数组来模拟:board 用于记录真实的雷分布(底层逻辑),show 用于展示给玩家的界面(显示层)。
为了避免玩家在输入坐标时触碰数组非法边缘导致的越界访问,棋盘的物理尺寸通常会比视觉尺寸大两圈。例如,视觉上是 9x9 的格子,实际开辟的是 11x11 的空间(即 ROWS = ROW + 2),中间 9x9 的区域才是有效游戏区。
第1步:定义宏与头文件
入口: game.h
参数说明: 定义行/列大小以及雷的数量。
动作: 声明游戏相关函数的原型。
#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 board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
初始化与布雷逻辑
游戏开始前,必须将内部棋盘初始化为字符 '0'(表示无雷),外部展示棋盘初始化为 '*'(表示未翻开)。随后在内部棋盘中随机撒下指定数量的雷(用字符 '1' 表示)。
第2步:布置地雷
目的: 确保地雷分布在有效区域内且不重叠。
操作: 使用 rand() 生成 1~9 的随机数作为横纵坐标,若该位置未被占用则标记为 '1'。
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = Easy_count;
while(count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if(board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
玩家交互与胜负判定
玩家输入坐标后,系统需要判断是否踩雷或排雷成功。踩雷直接结束游戏;排雷成功后调用展开函数。胜利条件是:未被翻开的格子数等于地雷总数。
void Player(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int win = 0;
while(win < row*col - Easy_count)
{
printf(">>>请输入要排查的坐标:>>>");
scanf("%d %d", &x, &y);
if(x >=1 && x <=row && y >=1 && y <=col)
{
if(show[x][y] == '*')
{
if(mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
else
{
// 如果没踩雷,进入展开逻辑
OpenBoard(mine, show, row, col, x, y);
DisplayBoard(show, row, col);
win = GetWin(show, row, col);
}
}
else
{
printf("该坐标已经被排查过了\n");
}
}
else
{
printf("输入非法,请重新输入\n");
}
}
}
核心算法:递归自动展开
扫雷最精妙的部分在于“自动展开”。当玩家点击一个位置,如果它周围没有雷,那么它周围的所有相邻空格也应该同时翻开。这种连锁反应最适合用**递归**来实现。
第3步:统计周围雷数
入口: 获取坐标 (x, y)。
逻辑: 遍历该点周围 8 个邻居,统计其中 '1' 的个数。
技巧: 由于数组存储的是字符 '0' 和 '1',可以通过求和再减去 '0' 的 ASCII 值来计算整数数量。
int GetSum(char mine[ROWS][COLS], int x, int y)
{
return (mine[x-1][y-1]+mine[x-1][y]+mine[x-1][y+1]+
mine[x][y-1]+ mine[x][y+1]+ \
mine[x+1][y-1]+mine[x+1][y]+mine[x+1][y+1]-8*'0');
}
第4步:递归展开逻辑
目的: 展开当前格子及周围连通的无雷区域。
操作: 调用 OpenBoard。
图片辅助说明:
展开规则详解:
- 命中地雷: 不处理(此分支由玩家交互层拦截)。
- 周围有雷: 将该位置在 show 板中更新为具体的数字(周围雷的数量),递归终止,不再向四周扩散。
- 周围无雷(数字为0): 将其显示为空格 ' '。然后对该点周围的 8 个邻居分别调用
OpenBoard进行递归。
void OpenBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
int sum = GetSum(mine, x, y);
if(sum == 0)
{ // 周围无雷
show[x][y] = ' ';
// 遍历周围8个点进行递归
for(int i=x-1; i<=x+1; i++)
{
for(int j=y-1; j<=y+1; j++)
{
// 边界检查与未翻开检查
if(i >=1 && i<=row && j >=1 && j<=col && show[i][j]=='*')
{
OpenBoard(mine, show, row, col, i, j);
}
}
}
}
else
{ // 周围有雷
show[x][y] = sum+'0'; // 转换为字符显示
}
}
注意: 上述代码简化了针对已翻开位置的重复调用判断(即防止无限递归或重复计算)。在实际工程中,通常在进入递归前增加对 show[i][j] 是否为 '*' 的判断,以确保每只翻开一次。
总结
C语言扫雷的实现关键在于双内存管理与递归展开逻辑。通过巧妙利用 ASCII 码差值计算雷数,能够高效统计周围风险;而递归机制则完美解决了“一触连发”的连锁反应问题。掌握该案例,有助于深刻理解递归栈的工作方式及数组越界的防御性编程技巧。
以上就是C语言通过递归实现扫雷展开的详细内容,更多关于C语言扫雷递归算法的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













