news 2026/8/6 23:42:51

C 语言项目复盘:用数组和函数实现扫雷游戏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C 语言项目复盘:用数组和函数实现扫雷游戏

C 语言项目复盘:用数组和函数实现扫雷游戏

学完二维数组和函数模块化编程之后,终于动手完成了第一个完整的小项目 —— 控制台版扫雷游戏。整个过程把数组操作、函数拆分、多文件工程写法都串起来了,也踩了不少细节坑。这篇博客完整复盘设计思路、核心实现和高频易错点,既是内化总结,也方便以后回头复习。
一、游戏整体设计与思路
1.1 基础功能需求
我们实现的是经典初级扫雷,核心功能如下:
控制台交互,主菜单选择开始游戏 / 退出
9×9 的游戏棋盘,默认随机布置 10 个雷
玩家输入坐标排查雷:
踩到雷:游戏失败,展示全部雷的位置
没踩到雷:显示该位置周围 8 格的雷数
排查完所有非雷格子,游戏胜利
1.2 核心数据结构设计
这是整个项目最关键的设计点,一开始没想明白很容易写乱。
两个独立棋盘
我们用两个二维数组分别存储数据,互不干扰:
mine 数组:存放雷的布置信息,字符 ‘0’ 表示非雷,‘1’ 表示雷
show 数组:展示给玩家的棋盘,初始全是 ‘*’,排查后显示周围雷数
用两个数组的好处是数据逻辑分离,而且初始化、打印这类通用函数可以复用一套代码,不用写两遍。
为什么数组是 11×11,不是 9×9?
这是新手最容易忽略的设计细节。
如果只开 9×9 的数组,当排查边缘格子(比如第一行、最后一列)时,统计周围 8 个格子的雷数会出现数组下标越界,导致程序异常。
解决方案:在 9×9 棋盘外围多扩一圈缓冲区,数组大小变成 11×11。雷只布置在中间 1~9 行、1~9 列的区域,外围一圈永远是 ‘0’。这样统计任何位置的周围雷数时,都不会越界访问,代码写起来也不用额外写边界判断。
我们用宏定义统一管理尺寸,改难度也方便:

#defineROW9// 实际游戏行数#defineCOL9// 实际游戏列数#defineROWSROW+2// 数组总行数#defineCOLSCOL+2// 数组总列数#defineEASY_COUNT10// 雷的数量

1.3 多文件工程拆分
按照模块化编程的思路,把代码拆成 3 个文件:
game.h:头文件,放宏定义、函数声明、需要的标准头文件
game.c:游戏所有功能函数的具体实现
test.c:主程序入口,写菜单、游戏主流程控制
二、核心功能函数实现拆解
2.1 棋盘初始化函数
因为两个棋盘都需要初始化,所以做成通用函数,通过参数 set 指定初始化的字符:
mine 数组初始化为全 ‘0’
show 数组初始化为全 ‘*’

voidInitBoard(charboard[ROWS][COLS],introws,intcols,charset){inti=0;for(i=0;i<rows;i++){intj=0;for(j=0;j<cols;j++){board[i][j]=set;}}}

2.2 打印棋盘函数
为了方便玩家输入坐标,打印时要带上行号和列号,且都从 1 开始显示,和玩家输入对齐。

voidDisplayBoard(charboard[ROWS][COLS],introw,intcol){inti=0;printf("--------扫雷游戏-------\n");// 打印列号for(i=0;i<=col;i++){printf("%d ",i);}printf("\n");// 打印每行数据和行号for(i=1;i<=row;i++){printf("%d ",i);intj=0;for(j=1;j<=col;j++){printf("%c ",board[i][j]);}printf("\n");}}

2.3 随机布置雷函数
生成随机坐标,在合法范围内布置雷,注意要判重,避免同一个位置重复放雷。

voidSetMine(charboard[ROWS][COLS],introw,intcol){intcount=EASY_COUNT;while(count){// 生成1~row、1~col的随机坐标intx=rand()%row+1;inty=rand()%col+1;if(board[x][y]=='0'){board[x][y]='1';count--;}}}

2.4 统计周围雷数函数
这是最巧妙的一个小函数。
因为 mine 数组存的是字符 ‘0’ 和 ‘1’,我们把周围 8 个格子的字符值加起来,再减去 8 个字符 ‘0’ 的 ASCII 值,得到的就是数字形式的雷的总数。

intGetMineCount(charmine[ROWS][COLS],intx,inty){return(mine[x-1][y-1]+mine[x-1][y]+mine[x-1][y+1]+mine[x][y-1]+mine[x][y+1]+mine[x+1][y-1]+mine[x+1][y]+mine[x+1][y+1]-8*'0');}

2.5 排查雷主逻辑
游戏的核心循环,负责接收玩家输入、判断胜负、更新棋盘。

voidFindMine(charmine[ROWS][COLS],charshow[ROWS][COLS],introw,intcol){intx=0;inty=0;intwin=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(mine,ROW,COL);break;}else{// 统计周围雷数,更新展示棋盘intcount=GetMineCount(mine,x,y);show[x][y]=count+'0';// 数字转字符存入DisplayBoard(show,ROW,COL);win++;}}else{printf("坐标非法,重新输入\n");}}// 排雷成功判断if(win==row*col-EASY_COUNT){printf("恭喜你,排雷成功\n");DisplayBoard(mine,ROW,COL);}}

三、复盘:我踩过的高频易错点
这部分是写项目最有价值的收获,很多语法点学的时候觉得懂了,一写代码就踩坑。

  1. 数组下标越界
    这是新手写扫雷最容易犯的错。
    一开始直接用 9×9 数组,统计边缘格子周围雷数时,x-1 或 y+1 会跑到下标 0 或者 9 的位置,越界访问内存,轻则输出乱码,重则程序直接崩溃。
    解决方式:扩大一圈数组做缓冲区,11×11 的设计从根源上避免越界判断。
  2. 字符 ‘0’ 和数字 0 混淆
    mine 数组存的是字符型的 ‘0’ 和 ‘1’,不是整型 0 和 1。
    字符 ‘0’ 的 ASCII 码是 48,直接相加得到的是 ASCII 值的和,不是雷的数量。一开始没注意,统计出来的雷数永远是几百,完全不对。
    解决方式:
    字符转数字:‘1’ - ‘0’ = 1
    数字转字符:count + ‘0’ 得到对应字符
  3. 随机数种子放置错误
    一开始把 srand((unsigned int)time(NULL)) 写在了 game() 函数里,每开一局就调用一次。快速连续开两局的话,时间戳几乎一样,生成的雷的位置完全相同。
    解决方式:srand 整个程序只调用一次,放在 main 函数开头即可。
  4. 布置雷没有判重
    如果随机生成了重复的坐标,不做判断就直接赋值,会导致实际雷的数量比设定值少。
    解决方式:布置雷前先判断当前位置是不是 ‘0’,确认没有雷再布置,只有布置成功才让计数器减一。
  5. 胜利条件逻辑写错
    总共有 ROW*COL 个格子,减去 EASY_COUNT 个雷,剩下的就是需要排查的非雷格子总数。
    一开始把胜利条件写反了,或者 win 计数器累加的位置放错了,导致排查完所有格子也不提示胜利。
    注意:只有排查非雷格子成功的时候,才让 win++。
  6. 多文件头文件重复包含
    拆文件初期,经常出现重复定义的报错。
    解决方式:头文件开头加上 #pragma once,防止头文件被重复包含;每个 .c 文件都包含对应的 .h 文件,不要漏写。
    四、可扩展的优化方向
    基础版写完之后,还可以继续拓展功能,练手更多语法:
    多难度选择:简单(9×9,10 雷)、中等(16×16,40 雷)、困难(30×16,99 雷),通过菜单选择,用变量控制数组尺寸和雷数
    空白区域自动展开:如果当前位置周围雷数为 0,递归 / 循环展开周围一圈的格子,直到遇到有数字的边界,还原经典扫雷体验
    标记雷功能:支持玩家输入指令标记疑似雷的位置
    计时功能:记录排雷用时,增加游戏性
    总结
    这次扫雷项目做下来,最大的收获不是写出了一个游戏,而是学会了怎么把一个完整的需求,拆成数据结构 + 一个个独立功能函数,再用多文件组织成工程。
    很多知识点比如数组传参、函数封装、边界处理,上课听的时候觉得很简单,真正动手写才会发现各种细节坑。尤其是字符和数字的转换、数组越界、逻辑边界判断,都是 C 语言编程里的高频易错点。纸上得来终觉浅,多动手写项目,踩坑再复盘,才是巩固知识最有效的方式。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/6 23:41:25

构建健壮的AI输出处理管道:从Prompt工程到生产部署

在工程实践中&#xff0c;AI 模型的输出正被越来越多地集成到各类应用系统中。然而&#xff0c;直接将未经处理的 AI 生成内容&#xff08;尤其是来自大语言模型或生成式 AI 的内容&#xff09;作为最终结果交付给用户或下游系统&#xff0c;是一种高风险行为。这种未经校验、直…

作者头像 李华
网站建设 2026/8/6 23:40:54

开源对话模型实战:从选型部署到API集成全指南

这次我们来看一个关于前沿开源模型如何改变智能对话格局的话题。这个话题的核心不是某个单一的模型&#xff0c;而是一个正在发生的趋势&#xff1a;开源模型在推理、代码、多模态和长上下文能力上的集体突破&#xff0c;正在让高质量的智能对话能力变得触手可及&#xff0c;甚…

作者头像 李华
网站建设 2026/8/6 23:38:43

2024年厦门php网站建设全流程深度解析,从服务器搭建到上线运营的避坑指南,揭秘本地化服务的真实成本与性能优化策略,助你在跨境电商与中小企业数字化转型中站稳脚跟

在厦门做互联网生意,或者说想做互联网生意的朋友,大家可能都有一个共同的困惑:找外包公司写代码,到底是坑多还是雷多?尤其是当你们公司属于传统行业转型,或者是初创期的跨境电商团队,预算有限但要求又不低的时候,这种焦虑感简直像闽南台风天里的暴雨一样,来得又急又猛…

作者头像 李华
网站建设 2026/8/6 23:33:05

终极指南:用Godot引擎快速构建2D平台跳跃游戏

终极指南&#xff1a;用Godot引擎快速构建2D平台跳跃游戏 【免费下载链接】godot-platformer-2d 2d Metroidvania-inspired game for the 2019 GDquest Godot Kickstarter course project. 项目地址: https://gitcode.com/gh_mirrors/go/godot-platformer-2d 想要学习使…

作者头像 李华
网站建设 2026/8/6 23:29:01

单片机毕业设计-基于 STM32/51 单片机的水环境参数阈值报警控制系统设计 基于 STM32 的多模式水质监测硬件终端设计与实现(011002)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/8/6 23:28:55

2024年如何从零开始搭建一套高转化的商城类网站建设指南与避坑全解析

说心里话,很多刚开始想做电商的朋友,或者是手里有货想转型线下的老板,第一次提到“建网站”或者“做商城”的时候,眼神里既有着对未来的憧憬,也藏着深深的迷茫。这种迷茫不是因为没有梦想,而是因为周围的声音太杂了。有的朋友告诉你,随便找个模板几千块就能搞定,卖货那…

作者头像 李华