尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
从零开始实现扫雷游戏:C语言数组与函数实战
前言扫雷是一款风靡全球的经典益智游戏它看似简单却蕴含着数组、循环、随机数、函数封装等许多基础编程思想。作为一个初学者在学完 C 语言的基本语法后常常会困惑这些知识到底能用来做什么。本文就以扫雷游戏为载体从零开始带你一步步分析需求、设计数据结构并最终用 C 语言在控制台中完整实现这款游戏。通过这个实战项目你不仅能巩固数组和函数的知识还能体会到把一个想法变成可运行程序的完整过程。一. 扫雷游戏分析与设计扫雷是一款经典的益智游戏玩家需要根据数字提示推断出雷的位置并避开所有雷完成排雷。本文将从零开始使用 C 语言在控制台中实现一个 9×9 棋盘、10 个雷的经典扫雷游戏帮助读者巩固数组和函数的相关知识。1.1 游戏功能说明本游戏需要实现以下功能使用控制台实现经典的扫雷游戏。游戏可以通过菜单实现继续玩或者退出游戏。扫雷的棋盘是 9×9 的格子。默认随机布置 10 个雷。可以排查雷如果位置不是雷就显示周围有几个雷如果位置是雷就炸死游戏结束把除 10 个雷之外的所有非雷都找出来排雷成功游戏结束。1.2 文件结构设计之前学习了多文件的形式对函数的声明和定义这里我们实践一下设计三个文件test.c文件中写游戏的测试逻辑。(负责游戏菜单和流程的控制)game.c文件中写游戏中函数的实现等。(存放雷初始化棋盘打印雷和排查雷game.h文件中写游戏需要的数据类型和函数声明等。(头文件常量定义和函数的声明注test.c和game.c中的所需要的头文件都可以写在game.h中之后只需要在它们自己文件中写一个#includegame.h就可以了。二.游戏菜单的制作我们可以制作一个简易菜单它包含最基础的“游玩”和“退出游戏”的选项。输入“1”表示“play”“0”则代表“exit”。void menu() { printf(***********************\n); printf(***** 1.play *****\n); printf(***** 0.exit *****\n); printf(***********************\n); }根据游戏可以重复玩的特性至少要显示一次菜单并且考虑到有分支的选项可以采用do-while循环加switch语句的结构。int main() { int input 0; do { menu(); printf(请选择:); scanf(%d, input); switch (input) { case 1: game(); break; case 0: printf(退出游戏\n); break; default: printf(选择错误请重新选择\n); } } while (input); return 0; }由代码可知switch语句中分三种情况输入1进入game()也就是游戏功能的实现部分输入0则是退出游戏除此之外则会报错。三.game()函数的实现(游戏功能的实现1.棋盘的制作与定义1.1制作的基本逻辑首先对于一个9×9的扫雷棋盘我们选择用二维数组来存储其中的信息分为两种信息1.存放布置好的雷的信息mine数组2.存放排查出雷的信息 show数组根据扫雷游戏的基本逻辑排查到某个位置时若该位置不是雷则需要展示出该位置周边的8个位置雷的个数。因此当我们要去制作一个 9×9的棋盘时这个棋盘的每条边在排查时都会出现数组越界访问的情况。想要解决这样的情况我们可以将这个棋盘扩大一圈变为 11×11 棋盘但是棋盘的信息还是存在9×9的棋盘里只是扩大一圈作为“边界保护”。1.2棋盘的定义二维数组的定义首先要看是什么类型。对于mine数组来说我们用0代表无雷1代表有雷信息为字符类型char。对于show数组来说属于玩家玩游戏时的棋盘全部用*信息也为字符类型char。并且为了后面可以灵活地改变数组的大小我们可以采用宏定义使用到了关键字define用ROWS表示行COLS表示列。char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息并将它们在game.h中声明#define ROW 9//棋盘行数 #define COL 9//棋盘列数 #define ROWS ROW2//数组行数 #define COLS COL2//数组列数2.棋盘的初始化mine数组全部初始化为0show数组: 全部初始化为*使用函数InitBoard(),向其中传入数组数组行数数组列数和要初始化的符号InitBoard(mine, ROWS, COLS, 0); InitBoard(show, ROWS, COLS, *);之后用循环嵌套的方式对二维数组进行初始化void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i 0; for (int i 0; i rows; i) { for (int j 0; j cols; j) { board[i][j] set; } } }3.棋盘的打印使用函数DisplayBoard()传入数组棋盘行数棋盘列数DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL);为了方便观察可以打印出行号和列号void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf(---------扫雷游戏--------\n); for (int i 0; i col; i) { printf(%d , i);//打印行号 } printf(\n); for (int i 1; i row; i) { printf(%d , i);//打印列号 for (int j 0; j col; j) { printf(%c , board[i][j]); } printf(\n); } }4.雷的布置使用函数SetMine()传入数组棋盘行数棋盘列数SetMine(mine, ROW, COL);4.1随机生成雷为了可以随机生成10个雷 可以在函数内部使用rand()函数来生成随机数,而rand()函数调用之前需要调用srand(),当传递给srand()的数值(类型为unsigend int)也叫做种子不断变化时rand()函数每次调用就会返回随机的值。因此可以使用time()函数来设置一个随时间变化的种子当time()函数的参数timer(类型为time_t)为NULL时就会返回当前日历的时间。因此time(NULL)就是一个随时间变化的种子所以需要在主函数里添加srand((unsigned int)time(NULL));int main() { int input 0; do { srand((unsigned int)time(NULL)); menu(); printf(请选择:gt;); scanf(%d, amp;input); switch (input) { case 1: game(); break; case 0: printf(退出游戏\n); break; default: printf(选择错误请重新选择\n); } } while (input); return 0; }这里需要的头文件为#includestdlib.h #includetime.h由于rand()的返回值范围较大但是我们的棋盘目前是9×9 的可以通过取模运算将它缩小到我们能用的有效范围int x rand() % row 1;//余数原本为0~8加一后变为0~9 int y rand() % col 1;//同上4.2布置十个雷定义一个EASY_COUNT用来统计雷的个数#define EASY_COUNT 10然后通过while循环在棋盘上随机布置10个雷oid 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--; } } }5.雷的查找5.1排雷的基本逻辑使用函数FindMine(),传入mine数组show数组棋盘行数棋盘列数由于前面在棋盘上打印了行号和列号因此我们可以通过输入坐标来进行排查。扫雷游戏获胜的条件是要把所有不是雷的位置全部找出来因此我们可以用while循环去遍历每一个不是雷的位置while (win row * col - EASY_COUNT)之后判断输入的坐标是否合法再将坐标带入到mine数组中去进行比对。并且无论是否是雷排一次雷最后都要打印一次show数组的棋盘以便于下一次排雷的进行。5.2排雷的情况排雷的结果就两种情况要么是雷“被炸死了”要么不是雷之后进入到下一次循环直到游戏胜利当排查的位置不是雷时就要在这个坐标上输出相邻周围8个位置上雷的个数。因此我们可以创建一个新的函数GetMine()来计算这个信息。使用GetMine()传入mine数组和排查时输入的坐标。int GetMineCount(char mine[ROWS][COLS], int x, int y) { return (mine[x - 1][y] mine[x - 1][y - 1] mine[x][y - 1] mine[x 1][y - 1] mine[x 1][y] mine[x 1][y 1] mine[x][y 1] mine[x - 1][y 1] - 8 * 0);//字符ASCII码值相减后为数字1-049-481 }此函数功能的分析由于mine数组中为1和0二者都是字符二者的ASCII值分别为49和48那么当二者相减时就会得到数字1那么此时这个数字1就可以表示为雷的数量。也就是说当排查坐标周围八个位置上的字符加起来并减去8*0,返回的结果即为雷的数量。因为最后打印show数组的棋盘上显示的应该是字符那么得到的返回值再0就可以得到相应数字的字符从而显示出周围雷的数量。5.3排雷总结最终代码如下void FindMine(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(请输入要排查的坐标:); scanf(%d %d, x, y); if (x 1 x row y 1 y col) //判断坐标是否合法 { if (mine[x][y] 1) { printf(很遗憾你被炸死了\n); DisplayBoard(show, ROW, COL); break; } else { //该位置不是雷就统计这个坐标周围有几个雷 int count GetMineCount(mine, x, y); show[x][y] count 0;//要显示出字符就要给相应的ASCII码值 DisplayBoard(show, ROW, COL); win; } } else { printf(坐标非法重新输入\n); } } if (win row * col - EASY_COUNT) { printf(排雷成功\n); DisplayBoard(mine, ROW, COL); } }6.game()函数最终结果void game() { char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息 //接下来要初始化棋盘 //1.mine数组最开始全是0 //2.show数组最开始全是’*‘ InitBoard(mine, ROWS, COLS, 0); InitBoard(show, ROWS, COLS, *); //之后要打印棋盘 DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL); //之后要布置雷 SetMine(mine, ROW, COL); //最后排查雷 FindMine(mine, show, ROW, COL); }四.最终的game.h#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #includestdio.h #includestdlib.h #includetime.h #define EASY_COUNT 10 #define ROW 9 #define COL 9 #define ROWS ROW2 #define COLS COL2 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);//排查雷 int GetMine(char mine[ROWS][COLS], int x, int y);//查照该坐标周围八个位置中有多少雷五.扫雷总结到此为止一个基础版的扫雷游戏就做好了在其中我们用到了通过函数对游戏功能进行拆分二维数组来存储棋盘数据宏定义棋盘大小循环嵌套对棋盘进行初始化rand()生成随机数取模运算缩小取值范围字符与数字之间的转换作为一个C语言的初学者第一次完整地记录自己做项目的过程我觉得这能帮助我加深对知识的理解希望也能帮到有需要的人如有错误欢迎各位大佬前来指正
RELATED

相关推荐

晶体内部自发织出三维结构:从雕刻到编写材料制造新路径

晶体内部自发织出三维结构:从雕刻到编写材料制造新路径

晶体内部竟然能自发织出三维结构。第一次看到这句话,我第一反应是:晶体不是最讲“秩序”的东西吗?原子、分子按周期性排列,重复、对称、稳定,怎么看都跟“织”这种带有交叉、缠绕和节点概念的工艺扯不上关系。但仔细想…

📅 2026/9/30 9:29:35
MCSDK Workbench下STM32F405RGT6 48V BLDC实战

MCSDK Workbench下STM32F405RGT6 48V BLDC实战

最近帮朋友评估一个48V直流无刷电机控制器项目,BOM、封装、成本都聊得差不多了,卡在了一个软件环节:他在 Motor Control Workbench 里新建工程时,愣是找不到 STM32F405RGT6 这颗料。下载了最新版 SDK,固件包也更新了&a…

📅 2026/9/6 9:45:44
域格移芯平台模块之open二次开发(二):AT指令

域格移芯平台模块之open二次开发(二):AT指令

适用模块:YM310 X09、YM310 X19、YM310 X08、YM310 W09、YM310 W19AT指令概述模块自带AT指令(RIL接口)RIL接口说明使用流程注意事项客户自定义AT指令(参考 demo_gpio.c)demo_gpio.c 的指令格式实现步骤注意事项实测验证…

📅 2026/9/20 15:03:31
MORE NEWS

更多资讯

📰

盐田初级电工证复审千万别踩坑,别让证书过期耽误你找工作

盐田初级电工证复审千万别踩坑,别让证书过期耽误你找工作 你是不是正在为找工作发愁?手里拿着一份初级电工证,却因为没及时复审而被企业拒之门外?别让【盐田初级电工证复审】这事儿,毁了你本该顺利上岗的机会。尤其是在商丘这样的工程活跃地区,电工证不…

📰

电工证新版证书样本怎么转异地?保姆级教程教你避坑

电工证新版证书样本怎么转异地?保姆级教程教你避坑 不知道去哪报名怕被中介坑,电工证新版证书样本怎么转异地?很多人因为不了解流程,被中介坑了钱还拿不到证,甚至耽误了工作机会。今天我就以一个真实案例来带你们看清楚整个流程,让你少走弯路,真正掌握…

📰

广州快速办理电工证多少钱?学历不高也能轻松拿下

广州快速办理电工证多少钱?学历不高也能轻松拿下 你是不是也担心自己学历不高,怕在广州办电工证连报名资格都没有?其实不然,只要符合条件, 广州快速办理电工证多少钱…

📰

普通电工证图片高清官方回应怕考不过白交培训费怎么办

普通电工证图片高清官方回应怕考不过白交培训费怎么办 怕考不过白交培训费?别急,听老电工怎么说。普通电工证图片高清在网络上一搜一大把,但真正靠谱的培训渠道你得擦亮眼。河南省应急管理厅最近回应,明确表示2024年特种作业操作证的考试标准、培训流…

📰

高级电工证上岗到期换证流程,全国通用证件如何顺利延期?

高级电工证上岗到期换证流程,全国通用证件如何顺利延期? 证书过期了不知道怎么复审延期?特别是像你这样在洛阳市政公用工程一线的电工,高级电工证上岗一旦过期,不仅影响日常工作,还可能被认定为无证上岗,面临处罚。别急,今天就带你一步步梳理清楚高级…

📰

楚雄可以考电工证吗2026河南最新政策下怎么转证更划算

楚雄可以考电工证吗2026河南最新政策下怎么转证更划算 之前考的证在外省能不能转过来?这是很多人在考电工证时最关心的问题,特别是在河南、楚雄这样的地区。2026年河南最新政策对证书的转接、报考条件、考试流程等都做了明确说明,如果你之前是在外…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬