目录 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 1.2 游戏的分析与设计️ 1.2.1 数据结构的分析 1.2.2 文件结构设计 2. 扫雷游戏的代码实现 game.h —— 头文件与声明⚙️ game.c —— 核心函数实现 test.c —— 游戏主逻辑 3. 扫雷游戏的扩展 小总结 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明扫雷是一款经典的单人益智游戏我们将使用C语言在控制台中实现它。游戏具备以下核心功能️菜单交互游戏启动时显示菜单玩家可以选择“开始游戏”或“退出游戏”。️标准棋盘游戏棋盘为9×9的格子。随机布雷系统会默认随机布置10个雷。排查雷机制如果玩家点击的位置不是雷则显示该位置周围8个格子中雷的总数。如果点击的位置是雷则游戏结束玩家被“炸死”。如果玩家成功找出所有非雷格子即排除了除10个雷之外的所有格子则游戏胜利。游戏界面示意初始界面棋盘上所有格子均显示为*代表未知区域。排雷界面玩家输入坐标后该位置会显示周围雷的数量数字1-8或显示空白周围无雷。排雷失败界面踩到雷后游戏会展示所有雷的位置并提示“很遗憾你被炸死了”。 1.2 游戏的分析与设计️ 1.2.1 数据结构的分析在扫雷过程中我们需要存储两类信息雷的布局信息和玩家排查出的雷的数量信息。因此我们需要设计合适的数据结构。1. 基础棋盘设计最直观的想法是创建一个9×9 的二维数组来存储信息。我们规定0表示该位置没有雷。1表示该位置有雷。2. 边界问题与解决方案当我们排查一个格子时需要统计其周围8个格子的雷数。例如排查坐标(2,5)时访问其周围的黄色格子是安全的。但是如果排查棋盘边缘的格子比如(8,6)访问其下方的格子时就会发生数组越界。排雷的假设周围加上一圈后的棋盘为了解决这个问题我们采用一个经典的技巧将数组扩大一圈。即创建一个11×11 的数组只在中间的9×9区域内布置雷外围一圈全部初始化为0无雷。这样在统计任何中间格子的周围雷数时都不会发生越界。3. 信息分离双数组策略如果我们将雷的信息0/1和排查出的雷的数量信息0-8都放在同一个数组中会造成数据混淆。例如数组中的数字1到底代表“这里有1个雷”还是“周围有1个雷”为了解决这个问题我们采用双数组方案mine数组专门用来存放布置好的雷的信息。初始化全为0布置雷的位置改为1。show数组专门用来存放排查出的雷的数量信息并展示给玩家。初始化全为*神秘符号排查后显示对应的数字。这样做的好处是职责单一互不干扰。两个数组类型一致都是char类型可以使用同一套函数进行处理提高代码复用性。mine数组布置雷后的状态show输出初始化的状态对应的数组定义如下charmine[11][11]{0};// 用来存放布置好的雷的信息charshow[11][11]{0};// 用来存放排查出的雷的个数信息 1.2.2 文件结构设计为了代码的模块化和清晰性我们采用多文件组织方式将游戏代码分为三个文件test.c游戏的测试逻辑包含main函数和游戏菜单。game.c游戏核心函数的实现如初始化棋盘、打印棋盘、布雷、排雷等。game.h头文件包含所需库的引入、常量的定义以及所有函数的声明。 2. 扫雷游戏的代码实现 game.h —— 头文件与声明#pragmaonce#includestdio.h#includestdlib.h#includetime.h#defineEASY_COUNT10#defineROW9#defineCOL9#defineROWSROW2#defineCOLSCOL2// 初始化棋盘voidInitBoard(charboard[ROWS][COLS],introws,intcols,charset);// 打印棋盘voidDisplayBoard(charboard[ROWS][COLS],introw,intcol);// 布置雷voidSetMine(charboard[ROWS][COLS],introw,intcol);// 排查雷voidFindMine(charmine[ROWS][COLS],charshow[ROWS][COLS],introw,intcol);⚙️ game.c —— 核心函数实现#includegame.h// 1. 初始化棋盘voidInitBoard(charboard[ROWS][COLS],introws,intcols,charset){inti0;for(i0;irows;i){intj0;for(j0;jcols;j){board[i][j]set;}}}// 2. 打印棋盘voidDisplayBoard(charboard[ROWS][COLS],introw,intcol){inti0;printf(-------- 扫雷游戏 -------\n);// 打印列号for(i0;icol;i){printf(%d ,i);}printf(\n);// 打印行号和棋盘内容for(i1;irow;i){printf(%d ,i);intj0;for(j1;jcol;j){printf(%c ,board[i][j]);}printf(\n);}}// 3. 布置雷voidSetMine(charboard[ROWS][COLS],introw,intcol){intcountEASY_COUNT;while(count){intxrand()%row1;intyrand()%col1;if(board[x][y]0){board[x][y]1;count--;}}}// 4. 统计某坐标周围雷的数量intGetMineCount(charmine[ROWS][COLS],intx,inty){return(mine[x-1][y]mine[x-1][y-1]mine[x][y-1]mine[x1][y-1]mine[x1][y]mine[x1][y1]mine[x][y1]mine[x-1][y1]-8*0);}// 5. 排查雷voidFindMine(charmine[ROWS][COLS],charshow[ROWS][COLS],introw,intcol){intx0;inty0;intwin0;// 记录已排查的非雷格子数while(winrow*col-EASY_COUNT){printf(请输入要排查的坐标:);scanf(%d %d,x,y);if(x1xrowy1ycol){if(mine[x][y]1){printf( 很遗憾你被炸死了\n);DisplayBoard(mine,ROW,COL);break;}else{// 该位置不是雷统计周围雷数intcountGetMineCount(mine,x,y);show[x][y]count0;DisplayBoard(show,ROW,COL);win;}}else{printf(❌ 坐标非法请重新输入\n);}}if(winrow*col-EASY_COUNT){printf( 恭喜你排雷成功\n);DisplayBoard(mine,ROW,COL);}} test.c —— 游戏主逻辑#includegame.hvoidmenu(){printf(***********************\n);printf(***** 1. play *****\n);printf(***** 0. exit *****\n);printf(***********************\n);}voidgame(){charmine[ROWS][COLS];// 存放布置好的雷charshow[ROWS][COLS];// 存放排查出的雷的信息// 1. 初始化棋盘InitBoard(mine,ROWS,COLS,0);InitBoard(show,ROWS,COLS,*);// 2. 打印棋盘DisplayBoard(show,ROW,COL);// 3. 布置雷SetMine(mine,ROW,COL);// 4. 排查雷FindMine(mine,show,ROW,COL);}intmain(){intinput0;srand((unsignedint)time(NULL));// 设置随机数种子do{menu();printf(请选择:);scanf(%d,input);switch(input){case1:game();break;case0:printf( 退出游戏\n);break;default:printf(❌ 选择错误请重新选择\n);break;}}while(input);return0;} 3. 扫雷游戏的扩展以上实现了一个基础的扫雷游戏但它还有很多可以优化的地方。以下是一些常见的扩展方向也是面试中可能会被问到的问题️游戏难度选择简单9×9 棋盘10个雷。中等16×16 棋盘40个雷。困难30×16 棋盘99个雷。展开功能如果排查的位置不是雷且周围也没有雷可以自动展开周围的一片区域提升游戏体验。标记雷功能允许玩家对怀疑是雷的位置进行标记例如用!或?符号。⏱️计时功能显示玩家完成游戏所用的时间增加挑战性。在线扫雷游戏参考http://www.minesweeper.cn/ 小总结通过本次扫雷游戏的实战我们深入实践了C语言中的以下核心知识点数组使用二维数组来模拟游戏棋盘并学习了通过“扩大一圈”的技巧来解决数组越界问题。函数将游戏的不同功能初始化、打印、布雷、排雷封装成独立的函数提高了代码的可读性和可维护性。多文件编程将声明、实现和测试逻辑分离到不同的文件中是大型项目开发的基础。模块化设计思想通过“双数组”策略将雷的信息和排查信息分离避免了数据混淆体现了良好的设计思路。