资讯详情

C语言实现经典扫雷游戏:从二维数组到递归算法

📅 2026/10/31 14:04:22 | 华诺云谱 👁 阅读
C语言实现经典扫雷游戏:从二维数组到递归算法
1. 项目概述用C语言复刻经典扫雷游戏是一个绝佳的编程练习项目。它不仅涵盖了二维数组、随机数生成、递归算法等核心编程概念还能让你深入理解游戏逻辑的实现过程。我当年在大学第一次完成这个项目时那种看到自己编写的程序能像Windows原生扫雷一样运行的成就感至今记忆犹新。这个教程将带你从零开始完整实现一个控制台版的扫雷游戏。我们会采用模块化编程思想使用多文件结构组织代码最终实现包括雷区生成、数字提示、递归展开和胜负判断等完整功能。特别适合已经掌握C语言基础语法想通过实际项目提升编程能力的学习者。2. 核心设计思路2.1 游戏数据结构设计扫雷的核心是一个二维的游戏棋盘。在C语言中我们自然想到用二维数组来表示。但这里有个关键问题实际游戏棋盘大小与数组大小的关系。假设我们要实现9×9的标准棋盘如果直接定义9×9的数组在检查边缘格子时会面临数组越界风险。更合理的做法是定义11×11的数组但只使用中间的9×9区域。这样边缘格子周围也有完整的8个相邻格子简化了边界条件处理。#define ROW 9 #define COL 9 #define ROWS ROW2 #define COLS COL2 char mine[ROWS][COLS]; // 存储地雷位置 char show[ROWS][COLS]; // 显示给玩家的棋盘2.2 多文件工程结构良好的项目应该分模块组织代码。我们采用三个文件game.h声明函数和常量game.c游戏逻辑实现test.c主程序入口和测试代码这种结构不仅使代码更清晰也便于团队协作和后期维护。3. 关键功能实现3.1 初始化棋盘我们需要初始化两个棋盘一个记录地雷位置(mine)一个显示给玩家(show)。mine棋盘初始全为0之后随机布置地雷show棋盘初始全为*表示未探索区域。void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { for (int i 0; i rows; i) { for (int j 0; j cols; j) { board[i][j] set; } } }3.2 随机布置地雷使用rand()函数在mine棋盘中随机布置地雷。注意要设置随机种子避免每次运行地雷位置相同。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--; } } }3.3 计算周围地雷数当玩家点击一个安全格子时需要显示周围8个格子中的地雷总数。这个功能很好地练习了二维数组的遍历。static int GetMineCount(char mine[ROWS][COLS], int x, int y) { return (mine[x-1][y-1] mine[x-1][y] mine[x-1][y1] mine[x][y-1] mine[x][y1] mine[x1][y-1] mine[x1][y] mine[x1][y1] - 8*0); }3.4 递归展开空白区域经典扫雷中点击一个空白区域会自动展开所有相邻的空白区域这需要递归算法实现。void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { if (x 1 || x ROW || y 1 || y COL) return; if (show[x][y] ! *) return; int count GetMineCount(mine, x, y); if (count 0) { show[x][y] count 0; return; } show[x][y] ; // 递归检查周围8个方向 ExpandBoard(mine, show, x-1, y-1); ExpandBoard(mine, show, x-1, y); ExpandBoard(mine, show, x-1, y1); ExpandBoard(mine, show, x, y-1); ExpandBoard(mine, show, x, y1); ExpandBoard(mine, show, x1, y-1); ExpandBoard(mine, show, x1, y); ExpandBoard(mine, show, x1, y1); }4. 游戏主循环实现4.1 玩家输入处理游戏需要不断接收玩家输入坐标并做出相应处理。要注意输入的合法性检查。void PlayerMove(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x, y; while (1) { 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 { ExpandBoard(mine, show, x, y); DisplayBoard(show, ROW, COL); break; } } else { printf(该坐标已被排查请重新输入\n); } } else { printf(坐标非法请重新输入\n); } } }4.2 胜负判断每次移动后需要检查游戏是否胜利所有非雷格子都被翻开。int IsWin(char show[ROWS][COLS], int row, int col) { int count 0; for (int i 1; i row; i) { for (int j 1; j col; j) { if (show[i][j] *) { count; } } } return count EASY_COUNT; }5. 完整代码结构5.1 game.h 头文件#ifndef __GAME_H__ #define __GAME_H__ #include stdio.h #include stdlib.h #include time.h #define ROW 9 #define COL 9 #define ROWS ROW2 #define COLS COL2 #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); #endif5.2 game.c 实现文件#include game.h 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) { // 实现同上 } static int GetMineCount(char mine[ROWS][COLS], int x, int y) { // 实现同上 } void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { // 实现同上 } void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { // 游戏主逻辑 }5.3 test.c 测试文件#include game.h void menu() { printf(***********************\n); printf(***** 1. play *****\n); printf(***** 0. exit *****\n); printf(***********************\n); } void game() { char mine[ROWS][COLS] {0}; char show[ROWS][COLS] {0}; InitBoard(mine, ROWS, COLS, 0); InitBoard(show, ROWS, COLS, *); SetMine(mine, ROW, COL); DisplayBoard(show, ROW, COL); FindMine(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; }6. 进阶优化方向6.1 添加标记功能实际扫雷游戏允许玩家标记可能的地雷位置。我们可以扩展功能让玩家输入特定格式如x y f来标记格子。void MarkMine(char show[ROWS][COLS], int row, int col) { int x, y; printf(请输入要标记的坐标(行 列):); scanf(%d %d, x, y); if (x 1 x row y 1 y col) { if (show[x][y] *) { show[x][y] F; } else if (show[x][y] F) { show[x][y] *; } } }6.2 难度选择通过宏定义不同难度级别的棋盘大小和地雷数量让玩家可以选择游戏难度。#define EASY_ROW 9 #define EASY_COL 9 #define EASY_COUNT 10 #define MEDIUM_ROW 16 #define MEDIUM_COL 16 #define MEDIUM_COUNT 40 #define HARD_ROW 16 #define HARD_COL 30 #define HARD_COUNT 996.3 计时和排行榜添加计时功能记录玩家完成游戏所用时间并保存最佳成绩。#include time.h time_t start, end; double duration; start time(NULL); // ...游戏过程... end time(NULL); duration difftime(end, start); printf(用时: %.0f秒\n, duration);7. 常见问题与调试技巧7.1 递归展开导致栈溢出当遇到大面积空白区域时深度递归可能导致栈溢出。解决方法限制递归深度改用非递归方式实现如使用栈数据结构7.2 地雷分布不均匀rand()函数生成的随机数质量不高可能导致地雷聚集。改进方法使用更好的随机数生成器在布置地雷后检查分布情况必要时重新布置7.3 内存越界访问这是C语言常见问题特别是在处理数组边界时。建议使用assert断言检查数组索引在调试时打印整个数组内容检查#include assert.h assert(x 1 x row); assert(y 1 y col);7.4 跨平台兼容性问题不同平台对控制台的支持不同可能导致显示效果不一致。解决方案使用跨平台库如ncurses为不同平台编写特定的显示代码8. 项目总结与扩展思考通过这个项目我们不仅实现了经典扫雷游戏的核心功能还实践了以下编程技能二维数组的应用模块化编程思想递归算法实现用户输入处理游戏状态管理这个项目可以进一步扩展为图形界面版本使用GTK、Qt等库网络对战版本人工智能自动解雷算法手机端移植使用C语言交叉编译我在实际开发中发现良好的代码组织和充分的边界条件检查是项目成功的关键。特别是在游戏逻辑中对用户输入的严格验证可以避免很多奇怪的bug。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑