C语言实现简易版扫雷游戏详解与代码
本文使用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语言扫雷模块开发的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













