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。
企业数字化 ERP 产品动态
相关推荐
JMS与ActiveMQ:消息队列原理与实战指南 1. 从零开始理解JMS与ActiveMQ的关系 第一次接触JMS和ActiveMQ时,我也曾被它们的关系搞糊涂。这就像有人问"汽车和交通规则有什么区别"——JMS是Java消息服务的规范(相当于交通规则),而ActiveMQ则是实现这个规范的具体产… · 2026/11/6 6:58:46
人体感知机制与多感官信息处理解析 1. 人体感知的底层逻辑解析当我们谈论"人体感知"时,实际上是在探讨一个从物理世界到精神世界的复杂转换过程。这个过程始于各种物理信号(如光波、声波、压力等)被我们的感觉器官接收,经过神经系统的编码和传递ÿ… · 2026/11/8 15:01:40
AI云账户系统后端源码拆解:银行科技岗Java实战 简介:这套《基于Java的银行科技岗AI云账户系统后端设计源码》面向银行科技岗开发者与金融科技学习者,围绕AI云账户系统的设计落地,完整覆盖转账、对账、充值、提现等关键业务,并保留了后续接入AI能力的扩展空间。资源包共139个文件… · 2026/11/6 9:03:46
禹城在哪里学电工证 考前押题怎么找?工地太忙根本没时间复习考试 禹城在哪里学电工证 考前押题怎么找?工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,这种困扰你不是一个人。电工证作为特种作业操作证的一种,直接关系到你的工作资质和安全责任。特别是在禹城,工地作业频繁,很多电工朋友都在为“禹城在哪里学… · 2026/11/8 15:01:24
宿迁电工证报名在哪里 电工证值不值得考 宿迁电工证报名在哪里 电工证值不值得考 证书过期了不知道怎么复审延期?别慌,现在就告诉你怎么处理,还有电工证到底值不值得考,看完这篇你就明白了。… · 2026/11/8 15:01:24
锦州低压电工证考试怎么考?实操考试心里没底怕挂科?全国通用证书这样拿 锦州低压电工证考试怎么考?实操考试心里没底怕挂科?全国通用证书这样拿 实操考试心里没底怕挂科?你不是一个人。尤其是锦州低压电工证考试,很多刚接触这个行业的朋友,最担心的就是实操环节会不会挂科,怕耽误工期,更怕拿不到全国通用的证书。今天就从一… · 2026/11/8 15:01:24
工地太忙根本没时间复习考试?蛋仔派对特种工蛋网上查询全攻略 工地太忙根本没时间复习考试?蛋仔派对特种工蛋网上查询全攻略 工地太忙根本没时间复习考试,这几乎是每个特种作业人员的心声。特别是在河南信阳这样的建筑大市,工地上每天都有十几个项目同时推进,作业人员往往被安排在不同的施工环节,根本没有整块时间来… · 2026/11/8 15:01:24
工地太忙根本没时间复习考试?电工证还有必要考嘛的真实经历告诉你 工地太忙根本没时间复习考试?电工证还有必要考嘛的真实经历告诉你 工地太忙根本没时间复习考试,这是很多一线电工朋友的真实写照。我身边就有人因为赶工期,把电工证考试的事一拖再拖,结果等到要上岗时才发现,没有证根本不能操作。这不是危言耸听,而是… · 2026/11/8 15:01:24
特种工历年真题下载怎么找?电子证书能全国通用吗? 特种工历年真题下载怎么找?电子证书能全国通用吗? 之前考的证在外省能不能转过来?这个问题,我遇到过太多人问了,特别是那些在外地打工、换了城市的特种作业人员,生怕以前考的证成了“废纸”。其实,只要你是通过正规渠道取得的 电子证书… · 2026/11/8 15:00:24
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/8 0:00:20
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/8 0:00:20
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/8 0:00:20
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/8 0:00:20
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/8 0:00:20
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/8 0:00:20
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 11:25:08
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 12:02:18
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/8 1:18:25