[C语言初阶]扫雷小游戏

目录

  • 一、原理及问题分析
  • 二、代码实现
    • 2.1 分文件结构设计
    • 2.2 棋盘初始化与打印
    • 2.3 布置雷与排查雷
    • 2.4 游戏主流程实现
  • 三、后期优化方向

在上一篇文章中,我们实现了我们的第二个游戏——三子棋小游戏。这次我们继续结合我们之前所学的所有内容,制作出我们的第三个项目——扫雷小游戏。
在这里插入图片描述

一、原理及问题分析

说起扫雷,这是一个非常经典的小游戏。扫雷游戏的核心逻辑是通过玩家输入的坐标排查雷的位置,若踩雷则游戏结束,否则显示周围雷的数量。接下来我们先以结构化的方式来宏观分析整个扫雷游戏中的关键要点:

  1. 双棋盘设计

    • mine数组:存储雷的位置(1为雷,0为非雷)。
    • show数组:存储玩家可见的信息(初始为*,排查后显示周围雷数,因为是字符,所以两个数组都是char类型)。
    • 设计意义:若只创建一个数组,雷为1,不是雷为0,若这个坐标周围只有1个雷,则分不清是雷,还是排查出的雷的信息,有歧义,所以再创建一个额外数组,专门用来存放排查出的雷的信息,只打印这个数组即可,用%c打印。
  2. 边界处理

    • 实际使用11x11的数组(通过ROWSCOLS定义),但只操作中间的9x9区域(通过ROWCOL定义)。
    • 目的:排查雷时边界容易越界,所以要实现9x9棋盘实际是创建11x11的数组才不会越界,。 但不要直接写数字,而是定义行和列的符号,方便后期修改。
  3. 模块化设计

    • test.c:处理菜单、循环流程和用户输入。
    • game.c:实现游戏核心逻辑(初始化、布置雷、排查雷)。
    • game.h:声明函数和定义常量。
  4. 游戏流程(在三子棋和之前的猜数字小游戏中已实现过)

    • 使用do-while循环支持重复游玩。
    • 玩家输入坐标后,通过递归展开无雷区域(进阶功能需自行实现)。

二、代码实现

2.1 分文件结构设计

文件分工与核心函数

  • game.h:定义常量、声明函数。
// game.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>// 常量定义:实际操作的棋盘大小为9x9,扩展为11x11避免越界
#define ROW 9    
#define COL 9    
#define ROWS ROW+2
#define COLS COL+2
#define EASY 10  // 默认雷的数量// 函数声明
void Start(char arr[ROWS][COLS], int rows, int cols, char get); // 初始化棋盘
void Display(char arr[ROWS][COLS], int row, int col);           // 打印棋盘
void Set(char arr[ROWS][COLS], int row, int col);               // 布置雷
int Choose(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); // 排雷逻辑
  • test.c:主流程和菜单逻辑。
  • game.c:核心功能实现。

2.2 棋盘初始化与打印

1. 初始化函数 Start

  • 功能:将棋盘所有位置初始化为指定字符(mine初始为'0'show初始为'*')因为一个函数要实现两种不同内容初始化,所以多加一个参数。
// game.c
void Start(char arr[ROWS][COLS], int rows, int cols, char get) 
{for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {arr[i][j] = get; // 将每个元素设置为传入的字符('0'或'*')}}
}

2. 打印函数 Display

  • 功能:打印棋盘,为了美化棋盘显示,优化玩家体验,我们添加了行列号和分隔线。
// game.c
void Display(char arr[ROWS][COLS], int row, int col){printf("--------扫雷游戏--------\n");// 打印列号(顶部标签)printf("   "); // 对齐行号for (int i = 1; i <= col; i++){printf("%d ", i);}printf("\n");// 打印分隔线printf("   ");for (int i = 1; i <= col; i++) {printf("--");}printf("\n");// 打印棋盘内容(带行号)for (int i = 1; i <= row; i++) {printf("%d |", i); // 行号+左侧竖线for (int j = 1; j <= col; j++) {printf("%c ", arr[i][j]); // 打印棋盘元素}printf("\n");}printf("--------扫雷游戏--------\n");
}

运行效果

--------扫雷游戏--------1 2 3 4 5 6 7 8 9 -------------------
1 |* * * * * * * * * 
2 |* * * * * * * * * 
...(略)

2.3 布置雷与排查雷

1. 布置雷函数 Set

  • 功能:在9x9区域内随机生成雷。(关于rand函数的用法以及取余的技巧在之前的三子棋和猜数字小游戏中已经详细介绍过,这里不再赘述)
// game.c
void Set(char arr[ROWS][COLS], int row, int col){int count = EASY; // 雷的数量while (count) {int x = rand() % row + 1; // 生成1~9的随机坐标int y = rand() % col + 1;if (arr[x][y] == '0') {   // 仅当该位置无雷时布置arr[x][y] = '1';      // 标记为雷count--;}}
}

2. 计算周围雷数 get_mine

  • 功能:计算坐标(x,y)周围8个位置的雷数总和。 这个函数可以不用在game.h中声明,因为只是为了在排查雷的函数中临时用的,不会用在其他地方。
// game.c
int get_mine(char arr[ROWS][COLS], int x, int y) 
{// 周围8个坐标的字符值相加('0'或'1'),再减去8*'0'得到实际数字return arr[x-1][y-1] + arr[x-1][y] + arr[x-1][y+1] +arr[x][y-1]   +               arr[x][y+1]   +arr[x+1][y-1] + arr[x+1][y] + arr[x+1][y+1] - 8 * '0';
}

3. 排雷逻辑 Choose

  • 功能:处理玩家输入的坐标,判断是否踩雷或显示周围雷数。
// game.c
int Choose(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col){int x = 0, y = 0;int Win = 0; // 记录已排查的非雷区域数量while (Win < ROW * COL - EASY) // 胜利条件:所有非雷区域均被排查{ printf("请选择排雷坐标(如2 3表示第二行第三列):>");scanf("%d %d", &x, &y);if (x >= 1 && y >= 1 && x <= row && y <= col)// 坐标合法性检查{ if (mine[x][y] == '1') // 踩雷{ printf("很遗憾,你被炸死了!\n");Display(mine, ROW, COL); // 展示雷的位置break; // 游戏结束} else  // 安全坐标{ int count = get_mine(mine, x, y); // 计算周围雷数show[x][y] = count + '0'; // 转换为字符存储(例如3 -> '3')Display(show, ROW, COL);Win++;}} else {printf("坐标非法!\n");}}if (Win == ROW * COL - EASY) // 胜利条件达成{ printf("恭喜通关!\n");Display(mine, ROW, COL); // 展示雷的位置}
}

2.4 游戏主流程实现

test.c:菜单和主循环逻辑。

// test.c
#include "game.h"void menu() {printf("*********************************\n");printf("*********************************\n");printf("***********扫雷小游戏************\n");printf("*********************************\n");printf("***********1.开始游戏************\n");printf("***********0.退出游戏************\n");printf("*********************************\n");printf("**********版本:Beta1.0***********\n");printf("**********作者:Yang210***********\n");printf("*********************************\n");printf("*********************************\n");printf("*********************************\n");
}void game() {char mine[ROWS][COLS]; // 存储雷的棋盘char show[ROWS][COLS]; // 显示给玩家的棋盘Start(mine, ROWS, COLS, '0'); // 初始化雷棋盘为全'0'Start(show, ROWS, COLS, '*'); // 初始化显示棋盘为全'*'Set(mine, ROW, COL);          // 布置雷Display(show, ROW, COL);      // 打印初始棋盘Choose(mine, show, ROW, COL); // 进入排雷逻辑
}int main() {srand((unsigned int)time(NULL)); // 设置随机数种子int input = 0;int add = 0; // 记录游戏次数do {menu();if (add == 0) {printf("请选择(输入1开始游戏,输入0退出游戏):>");} else {printf("是否继续游玩(输入1继续游玩,输入0退出游戏):>");}scanf("%d", &input);switch (input) {case 1:game();add++;break;case 0:printf("已退出游戏\n");break;default:printf("输入错误!\n");}} while (input); // input为0时退出循环return 0;
}

三、后期优化方向

  1. 递归展开:若排查坐标周围无雷(即雷数为0),自动展开相邻区域。
  2. 标记雷:允许玩家输入特殊指令(如m 3 4)标记可能为雷的位置。
  3. 难度调整:通过修改EASY的值实现不同难度的雷数设置。
  4. 界面优化:使用Windows API或第三方库(如EasyX)添加图形界面。

简单地总结一下,在这篇文章中,我们通过分文件设计和模块化的运用,将扫雷游戏的逻辑逐一地理清并且给出了后期扩展的方向。我们在代码中通过定义符号常量(如ROWEASY)提高可维护性,方便后期修改,这是一种很重要的编程习惯。我们通过我们现阶段所学的知识,做出了这个经典的游戏,这是对我们所学知识的肯定,也是我们对经典跨越时间的致敬。最后,我想送给大家一句话:"人生没有白走的路,每一步都算数。"我们的决定,决定了我们。在介绍完了两个C语言的项目之后,下一章,我们将回归C语言的知识学习,介绍一下操作符的相关知识,敬请期待。
请添加图片描述

作者其他文章链接:
[C语言初阶]三子棋小游戏
[C语言初阶]数组
[C语言初阶]递归
Gitee详细使用教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROS云课三分钟-破壁篇GCompris-一小部分支持Edu应用列表-2025

开启蓝桥云课ROS ROS 机器人操作系统初级教程_ROS - 蓝桥云课 安装和使用GCompris 终端输入&#xff1a;sudo apt install gcompris sudo apt install gcompris ok&#xff0c;完成即可。 sudo apt install gcompris 如果是平板&#xff0c;秒变儿童学习机。 启动 流畅运…

Linux系统基础——是什么、适用在哪里、如何选

一、Linux是什么 Linux最初是由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;基于个人兴趣爱好开发的个人项目&#xff0c;他编写了最核心的内核&#xff1b;后面为了发展壮大Linux系统他将整个项目开源到GitHub上&#xff0c;可以让全世界的人都参与到项目的开发维护中…

26、AI 预测性维护 (燃气轮机轴承) - /安全与维护组件/ai-predictive-maintenance-turbine

76个工业组件库示例汇总 AI 预测性维护模拟组件 (燃气轮机轴承) 概述 这是一个交互式的 Web 组件,旨在模拟基于 AI 的预测性维护 (Predictive Maintenance, PdM) 概念,应用于工业燃气轮机的关键部件(例如轴承)。它通过模拟传感器数据、动态预测剩余使用寿命 (RUL),并根…

el-form 使用el-row el-col对齐 注意事项

1.el-form 使用inline&#xff0c;el-form-item宽度会失效。 2.为了保证el-form-item 和 它内部的el-input 能在一行&#xff0c;要设置el-form-item的label-width <el-form :model"editInspectform"><el-row style"margin-bottom: 20px"><…

mac 安装 mysql 和 mysqlshell

1. 安装 mysql https://dev.mysql.com/downloads/mysql/?spma2c6h.12873639.article-detail.4.37474f4dTHdszC 默认mysql未配置环境变量&#xff0c;可以在设置中找到 2. 安装 mysqlshell https://dev.mysql.com/downloads/shell/ #启动mysql-shell mysqlsh 3. 使用 mysq…

漏洞检测与渗透检验在功能及范围上究竟有何显著差异?

漏洞检测与渗透检验是确保系统安全的重要途径&#xff0c;这两种方法各具特色和功效&#xff0c;它们在功能上有着显著的差异。 目的不同 漏洞扫描的主要任务是揭示系统内已知的安全漏洞和隐患&#xff0c;这就像是对系统进行一次全面的健康检查&#xff0c;看是否有已知的疾…

机器学习模型度量指标(混淆矩阵、准确率、精确率、召回率、F1分数、ROC曲线、AUC、平均精度均值)

我们研究的是多分类问题&#xff0c;下面所有例子以多分类问题举例 混淆矩阵&#xff08;Confusion Matrix&#xff09; 混淆矩阵&#xff08; Confusion Matrix &#xff09;是一个表格&#xff0c;用于可视化机器学习模型在分类问题上 的性能。混淆矩阵的行表示实际类别&…

打卡day35

一、模型结构可视化 理解一个深度学习网络最重要的2点&#xff1a; 了解损失如何定义的&#xff0c;知道损失从何而来----把抽象的任务通过损失函数量化出来了解参数总量&#xff0c;即知道每一层的设计才能退出—层设计决定参数总量 为了了解参数总量&#xff0c;我们需要知…

时序数据库 TDengine × Superset:一键构建你的可视化分析系统

如果你正在用 TDengine 管理时序数据&#xff0c;写 SQL 查询没问题&#xff0c;但一到展示环节就犯难——图表太基础&#xff0c;交互不够&#xff0c;甚至连团队都看不懂你辛苦分析的数据成果&#xff1f;别担心&#xff0c;今天要介绍的这个组合&#xff0c;正是为你量身打造…

C# 初学者的 3 种重构模式

(Martin Fowlers Example) 1. 积极使用 Guard Clause&#xff08;保护语句&#xff09; "如果条件不满足&#xff0c;立即返回。将核心逻辑放在最少缩进的地方。" 概念定义 Guard Clause&#xff08;保护语句&#xff09; 是一种在函数开头检查特定条件是否满足&a…

基于51单片机和8X8点阵屏、独立按键的滑动躲闪类小游戏

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、独立按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板。 【单片机】STC89C52RC 【频率】12T11.0592MHz 【外设】8X8点阵屏、独立按键 效果查看/操作演示&#xff…

Java面向对象 一

系列文章目录 Java面向对象 二-CSDN博客 Java面向对象 三-CSDN博客 目录 系列文章目录 前言 一、初步认识面向对象 1.类和对象的简单理解 2.类的构成 二、类的实例化 1.对象的创建 2.对象的初始化 三、this引用的作用 四、构造方法 1.构造方法的提供 2.对象的构…

深度学习Y8周:yolov8.yaml文件解读

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 本周任务&#xff1a;根据yolov8n、yolov8s模型的结构输出&#xff0c;手写出yolov8l的模型输出、 文件位置&#xff1a;./ultralytics/cfg/models/v8/yolov8.…

【RocketMQ 生产者和消费者】- 生产者启动源码 - MQClientInstance 定时任务(4)

文章目录 1. 前言2. startScheduledTask 启动定时任务2.1 fetchNameServerAddr 拉取名称服务地址2.2 updateTopicRouteInfoFromNameServer 更新 topic 路由信息2.2.1 topic 路由信息2.2.2 updateTopicRouteInfoFromNameServer 获取 topic2.2.3 updateTopicRouteInfoFromNameSer…

解决Docker容器内yum: not found、apt: not found、apk: command not found等命令找不到问题

Linux有很多发行版&#xff0c;各发行版的包管理工具不一定相同。 Alpine的包管理工具是 apk Debian/Ubuntu的包管理工具是 apt Centos/RHEL的包管理工具是 yum 在安装软件之前&#xff0c;需要先查看Docker容器内的Linux是什么发行版&#xff0c;可使用 cat /etc/os-rele…

每日c/c++题 备战蓝桥杯(修理牛棚 Barn Repair)

修理牛棚 Barn Repair 题解 问题背景与挑战 在一个暴风雨交加的夜晚&#xff0c;Farmer John 的牛棚遭受了严重的破坏。屋顶被掀飞&#xff0c;大门也不翼而飞。幸运的是&#xff0c;许多牛正在度假&#xff0c;牛棚并未住满。然而&#xff0c;为了保护那些还在牛棚里的牛&am…

鸿蒙版Flutter库torch_light手电筒功能深度适配

鸿蒙版Flutter库torch_light手电筒功能深度适配&#xff1a;跨平台开发者的光明之路 本项目作者&#xff1a;kirk/坚果 适配仓库地址 作者仓库&#xff1a;https://github.com/svprdga/torch_light# 在数字化浪潮的推动下&#xff0c;跨平台开发框架如 Flutter 凭借其高效、…

【信息系统项目管理师】一文掌握高项常考题型-项目进度类计算

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、进度类计算的基本概念1.1 前导图法1.2 箭线图法1.3 时标网络图1.4 确定依赖关系1.5 提前量与滞后量1.6 关键路径法1.7 总浮动时间1.8 自由浮动时间1.9 关键链法1.10 资源优化技术1.11 进度压缩二、基本公式…

深入了解linux系统—— 操作系统的路径缓冲与链接机制

前言 在之前学习当中&#xff0c;我们了解了被打开的文件是如何管理的&#xff1b;磁盘&#xff0c;以及ext2文件系统是如何存储文件的。 那我们要打开一个文件&#xff0c;首先要先找到这个文件&#xff0c;操作系统又是如何去查找的呢&#xff1f; 理解操作系统搜索文件 …

Docker Hub仓库介绍

Docker Hub仓库全解析&#xff1a;从公共市场到私有化部署指南 一、Docker Hub公共镜像市场 1.1 核心功能解析 全球最大容器镜像库&#xff1a;累计托管超500万镜像核心服务矩阵&#xff1a; #mermaid-svg-CAMkhmtSWKEUw7z0 {font-family:"trebuchet ms",verdana,a…