
🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言
🔥专栏:《C语言从零开始到精通》
《C语言编程实战》
《数据结构与算法》
《小游戏与项目》
💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。
前言:
之前发过一个简易版本的扫雷了,最近又写了一个拓展版本( 包括周围无雷会自动检查四周的坐标以及手动标记雷区 ),如果对拓展版扫雷感兴趣的话,欢迎大家阅读这篇文章!
文章目录
拓展版扫雷游戏:经典玩法的升级与实现细节
扫雷作为一款经典的单人益智游戏,自诞生以来就深受玩家喜爱。本文将介绍一款基于C语言实现的拓展版扫雷游戏,它不仅保留了经典扫雷的核心玩法,还增加了更灵活的操作方式和人性化的交互设计。我们将从游戏功能、代码实现和玩法说明三个方面进行详细介绍。
游戏功能与特色
这款拓展版扫雷在传统扫雷基础上进行了多处优化,主要特点包括:
- 标准9x9游戏棋盘:经典的9×9格子布局,包含10个地雷,符合大多数玩家的操作习惯
- 完整的游戏流程:从游戏启动、棋盘展示到胜负判定,形成完整的游戏闭环
- 丰富的操作选项:提供扫雷、标记地雷、取消标记和中途退出四种核心操作
- 智能扩展功能:当玩家点击到周围无地雷的格子时,会自动扩展显示周围安全区域
- 清晰的视觉反馈:通过符号区分未探索区域(*)、标记区域(#)和已探索区域(数字)
- 完善的边界检查:对玩家输入的坐标进行有效性验证,防止误操作
核心代码解析
1. 头文件与宏定义
首先定义了游戏所需的常量和函数声明,通过宏定义可以方便地修改游戏难度(棋盘大小和地雷数量):
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 游戏常量定义
#define MINE 10 // 地雷数量
#define ROW 9 // 棋盘行数
#define COL 9 // 棋盘列数
#define ROWS ROW+2 // 含边界的行数(便于边界检查)
#define COLS COL+2 // 含边界的列数(便于边界检查)
// 函数声明
void game();
void menu();
void Init_Board(char arr[ROWS][COLS], int row, int col, char set);
void Set(char arr[ROWS][COLS], int row, int col, int mine);
void Print_Board(char arr[ROWS][COLS], int row, int col);
void Check_mine_a(char arr[ROWS][COLS], char arr1[ROWS][COLS], int x, int y, int row, int col, int mine);
void Check_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine, int* win);
void Sign_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine);
void unSign_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine);
void Option(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine);
2. 菜单与初始化功能
菜单函数用于展示游戏入口,初始化函数则负责设置棋盘初始状态:
// 游戏菜单
void menu()
{
printf("************************\n");
printf("******* 扫雷 *******\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("************************\n");
}
// 初始化棋盘
void Init_Board(char arr[ROWS][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
arr[i][j] = set; // 用指定字符填充棋盘
}
}
}
这里我们使用了两个棋盘:
mine数组:存储地雷位置信息('1’表示地雷,'0’表示安全)show数组:展示给玩家的棋盘('*‘表示未探索,’#'表示标记,数字表示周围地雷数)
3. 地雷布置算法
地雷布置采用随机数生成算法,确保地雷位置随机且不重复:
// 埋雷
void Set(char arr[ROWS][COLS], int row, int col, int mine)
{
srand((unsigned int)time(NULL)); // 初始化随机数种子
while (mine > 0) // 直到布置完所有地雷
{
// 生成1~row和1~col范围内的随机坐标
int x = 1 + rand() % row;
int y = 1 + rand() % col;
// 确保该位置还没有地雷
if (arr[x][y] != '1')
{
arr[x][y] = '1'; // 标记地雷
mine--; // 剩余地雷数减1
}
}
}
4. 棋盘打印功能
打印函数负责将当前游戏状态展示给玩家,包含坐标提示以便玩家输入:
// 打印棋盘
void Print_Board(char arr[ROWS][COLS], int row, int col)
{
// 打印列坐标
for (int i = 0; i <= ROW; i++)
{
printf("%d ", i);
}
printf("\n");
// 打印行坐标和棋盘内容
for (int i = 1; i <= row; i++)
{
printf("%d ", i); // 打印行坐标
for (int j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]); // 打印格子内容
}
printf("\n");
}
printf("\n");
}
5. 自动扩展功能实现
这是本游戏的核心特色之一,当玩家点击到周围无地雷的格子时,会自动递归扩展显示周围区域:
// 自动检查(递归扩展)
void Check_mine_a(char arr[ROWS][COLS], char arr1[ROWS][COLS], int x, int y, int row, int col, int mine)
{
// 检查坐标是否在有效范围内
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
// 计算周围8个格子的地雷总数
int ret = arr[x - 1][y - 1] + arr[x - 1][y]
+ arr[x - 1][y + 1] + arr[x][y - 1]
+ arr[x][y + 1] + arr[x + 1][y - 1]
+ arr[x + 1][y] + arr[x + 1][y + 1]
- 8 * '0'; // 转换为数字
arr1[x][y] = ret + '0'; // 转换为字符存储
// 如果周围没有地雷,则递归检查周围8个方向
if (arr1[x][y] == '0')
{
if (arr1[x - 1][y - 1] == '*')
Check_mine_a(arr, arr1, x - 1, y - 1, row, col, mine);
if (arr1[x - 1][y] == '*')
Check_mine_a(arr, arr1, x - 1, y, row, col, mine);
if (arr1[x - 1][y + 1] == '*')
Check_mine_a(arr, arr1, x - 1, y + 1, row, col, mine);
if (arr1[x][y - 1] == '*')
Check_mine_a(arr, arr1, x, y - 1, row, col, mine);
if (arr1[x][y + 1] == '*')
Check_mine_a(arr, arr1, x, y + 1, row, col, mine);
if (arr1[x + 1][y - 1] == '*')
Check_mine_a(arr, arr1, x + 1, y - 1, row, col, mine);
if (arr1[x + 1][y] == '*')
Check_mine_a(arr, arr1, x + 1, y, row, col, mine);
if (arr1[x + 1][y + 1] == '*')
Check_mine_a(arr, arr1, x + 1, y + 1, row, col, mine);
}
}
else
{
return; // 坐标无效则返回
}
}
6. 扫雷核心逻辑
处理玩家的扫雷操作,包括判断是否踩雷、计算周围地雷数和判定胜利条件:
// 检查雷
void Check_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine, int* win)
{
int x, y;
printf("请输入您要检查的坐标(例如:x,y):\n");
scanf("%d,%d", &x, &y);
// 检查坐标有效性
if (1 <= x && x <= row && 1 <= y && y <= col)
{
// 判断是否踩雷
if (arr[x][y] == '1')
{
printf("此位置有雷,游戏结束\n");
// 展示所有地雷位置
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (arr[i][j] == '1')
arr1[i][j] = '1'; // 显示地雷
}
}
Print_Board(arr1, ROW, COL);
*win = row * col - mine; // 结束游戏循环
}
else // 未踩雷
{
// 检查是否是未探索区域
if (arr1[x][y] == '*')
{
// 计算周围地雷数
int ret = arr[x - 1][y - 1] + arr[x - 1][y]
+ arr[x - 1][y + 1] + arr[x][y - 1]
+ arr[x][y + 1] + arr[x + 1][y - 1]
+ arr[x + 1][y] + arr[x + 1][y + 1]
- 8 * '0';
arr1[x][y] = ret + '0';
// 如果周围没有地雷,触发自动扩展
if (arr1[x][y] == '0')
{
Check_mine_a(arr, arr1, x, y, row, col, mine);
}
// 打印更新后的棋盘
Print_Board(arr1, ROW, COL);
// 计算已探索区域数量,判断是否胜利
*win = 0;
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (arr1[i][j] != '*' && arr1[i][j] != '#')
{
(*win)++;
}
}
}
printf("还剩下%d处未检查\n", row * col - mine - *win);
// 判断是否胜利
if (*win == row * col - mine)
{
printf("恭喜您,排雷成功!\n");
}
}
else
{
printf("输入重复或者已经标记,请重新输入:\n");
}
}
}
else
{
printf("输入错误,重新输入\n");
}
}
7. 标记与取消标记功能
实现地雷标记功能,帮助玩家记录怀疑有地雷的位置:
// 标记雷
void Sign_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine)
{
int x, y;
printf("请输入您要标记的坐标(例如:x,y):\n");
scanf("%d,%d", &x, &y);
// 检查坐标有效性
if (1 <= x && x <= row && 1 <= y && y <= col)
{
// 只能标记未探索区域
if (arr1[x][y] == '*')
{
arr1[x][y] = '#'; // 用#表示标记
Print_Board(arr1, ROW, COL);
}
else
{
printf("输入错误,该位置无法标记,请重新输入\n");
}
}
else
{
printf("输入错误,坐标超出范围,请重新输入\n");
}
}
// 取消标记
void unSign_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine)
{
int x, y;
printf("请输入您要取消标记的坐标(例如:x,y):\n");
scanf("%d,%d", &x, &y);
// 检查坐标有效性
if (1 <= x && x <= row && 1 <= y && y <= col)
{
// 只能取消已标记的区域
if (arr1[x][y] == '#')
{
arr1[x][y] = '*'; // 恢复为未探索状态
Print_Board(arr1, ROW, COL);
}
else
{
printf("输入错误,该位置没有标记,请重新输入\n");
}
}
else
{
printf("输入错误,坐标超出范围,请重新输入\n");
}
}
8. 游戏主循环
处理玩家的操作选择,控制游戏流程:
// 操作选择
void Option(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col, int mine)
{
int win = 0;
// 游戏循环,直到胜利或失败
do
{
int opp = 0;
printf("请输入您的选择\n1:扫雷\n2:标记\n3:取消标记\n4:退出\n");
scanf("%d", &opp);
switch (opp)
{
case 1: // 扫雷操作
Check_mine(arr, arr1, ROW, COL, MINE, &win);
break;
case 2: // 标记地雷
Sign_mine(arr, arr1, ROW, COL, MINE);
break;
case 3: // 取消标记
unSign_mine(arr, arr1, ROW, COL, MINE);
break;
case 4: // 退出游戏
printf("已退出游戏\n");
win = row * col - mine; // 结束循环
break;
default:
printf("输入错误,请输入1-4之间的数字!\n");
break;
}
} while (win < row * col - mine); // 胜利条件:已探索区域=总区域-地雷数
}
// 游戏主函数
void game()
{
printf("游戏开始\n");
char mine[ROWS][COLS]; // 存储地雷信息
char show[ROWS][COLS]; // 展示给玩家的棋盘
// 初始化棋盘:mine初始化为'0',show初始化为'*'
Init_Board(mine, ROWS, COLS, '0');
Init_Board(show, ROWS, COLS, '*');
// 打印初始棋盘
Print_Board(show, ROW, COL);
// 布置地雷
Set(mine, ROW, COL, MINE);
// 进入游戏操作循环
Option(mine, show, ROW, COL, MINE);
}
9. 程序入口
int main()
{
int op;
do
{
menu(); // 显示菜单
printf("请输入您的选择\n1:开始游戏\n0:退出程序\n");
scanf("%d", &op);
switch (op)
{
case 1: // 开始游戏
game();
break;
case 0: // 退出程序
printf("退出游戏,祝您生活愉快!\n");
break;
default:
printf("输入错误,请输入0或1!\n");
break;
}
} while (op != 0); // 输入0则退出程序
return 0;
}
游戏玩法说明
-
启动与退出:运行程序后,输入1开始游戏,输入0退出程序
-
棋盘元素:
*:未探索的格子#:标记为地雷的格子- 数字(0-8):表示周围8个格子中的地雷数量
1:游戏结束时显示的地雷位置
-
操作指南:
- 选择1:进行扫雷操作,输入格式为"x,y"(如3,5表示第3行第5列)
- 选择2:标记疑似地雷的位置,帮助记忆
- 选择3:取消之前的标记
- 选择4:中途退出当前游戏
-
胜负判定:
- 胜利:成功探索所有非地雷区域(即已探索区域数量 = 总格子数 - 地雷数)
- 失败:不小心点击到地雷位置
-
技巧提示:
- 优先点击角落或边缘格子,更容易触发自动扩展
- 善用标记功能,明确记录怀疑有地雷的位置
- 根据数字提示判断周围地雷分布,例如"1"表示周围8格中只有1个地雷
总结与扩展方向
这款拓展版扫雷游戏通过模块化的代码设计,实现了经典扫雷的全部核心功能,并增加了更友好的交互体验。代码中使用了数组、函数、循环、条件判断和递归等多种C语言基础知识点,非常适合作为C语言初学者的实践项目。
未来可以从以下几个方向进行扩展:
- 增加难度选择(初级、中级、高级)
- 实现计时功能,记录玩家完成时间
- 添加剩余地雷数量显示
- 增加右键标记的鼠标操作支持
- 实现游戏存档与读档功能
通过这个项目,不仅可以巩固C语言基础知识,还能学习到游戏开发的基本思路和逻辑设计方法。希望这款拓展版扫雷能给你带来编程和游戏的双重乐趣!
《扫雷拓展版》源代码
- 如果想要源代码的话,欢迎点击:《拓展版扫雷》
- 本节完…
95





