C语言项目实战——扫雷

目录

1.前言

2.完整流程

2.1规划书

2.2代码部分

2.2.1文件的结构设计

2.2.2变量的创建

2.2.3菜单的基本实现

2.2.4初始化期棋盘

2.2.5输出完整棋盘

2.2.6埋雷的实现

2.2.7查询周围雷的数量

2.2.8扫雷的实现

 2.2.9完整代码

3.总结


1.前言

哈喽大家好吖,今天笔者就前期基本所学手搓了一个扫雷的小项目,考察的知识点很简单仅有基本的分支,循环,函数调用,数组的基本应用,相信你也可以做到滴,快来试试喔~

2.完整流程

2.1规划书

虽然完成扫雷的代码量并不大,但我们还是要理清楚我们需要完成的功能和运行的基本逻辑。

 

这是一张扫雷的基本界面,通过观察和实际游玩可分析得出:

基本功能包括:

  • 游戏可以通过菜单实现继续玩或者退出游戏
  • 扫雷的棋盘是9*9的格⼦
  • 默认随机布置10个雷
  • 可以排查雷
  • 如果位置不是雷,就显⽰周围有⼏个雷
  • 如果位置是雷,就炸死游戏结束
  • 把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束

在梳理完需要实现的功能以后,就可以开始逐步实现以上功能咯。(当然笔者目前无法做出上述照片如此精良的扫雷,只能做一个简略版滴~)

2.2代码部分

2.2.1文件的结构设计

扫雷的代码量虽然不大,但是已经不适合用单文件来完成了,于是乎我们就把,游戏逻辑的基本实现,相关游戏函数的声明,以及游戏相关函数的实现分别打包,方便管理。

我们创建以下三个文件:

test.c //游戏的测试逻辑 
game.c //游戏中函数的实现
game.h //游戏需要的数据类型和函数声明等

完成后是这个样子。

另外,在正式敲代码前,我们应先在game.h这个头文件下加上

#pragma once

 #pragma once 是一个预处理指令,用来防止头文件的内容在一个编译单元中被多次包含。这与传统的头文件保护(也称为包含保护或头文件守卫)非常相似,后者通常使用#ifndef, #define, 和 #endif来实现。

2.2.2变量的创建

在扫雷这个游戏中,我们一共需要用到以下几个变量:

  • 棋盘一(origin)用于记录是否埋雷
  • 棋盘二(infor)用于记录雷的相关信息
  • 常量row,col分别用于记录扫雷中最核心的9*9区域
  • 常量rows,cols用于记录大棋盘11*11区域
  • 常量easy用于记录所要标记的雷

 实现分别如下(常量记得要在head.h里面创建):

//相关函数的声明
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<time.h>//记录核心区域
#define row 9//记录行
#define col 9//记录列#define rows row+2
#define cols col+2#define easy 10//记录简单难度有十个雷

 俩个棋盘的创建我放在了game函数中。

void game() {char origin[rows][cols];char infor[rows][cols];//创建俩个棋盘//初始化棋盘init(origin, rows, cols, '0');init(infor, rows, cols, '*');//打印棋盘用于调试print(infor, row, col);//开始埋雷setboom(origin, row, col);//print(origin, row, col);//开始排雷findboom(origin, infor, row, col);}

2.2.3菜单的基本实现

为了菜单的基本美观,我们在实现的时候可以进行反复调试,代码如下:

void menu() {printf("------------------------\n");printf("------欢迎来到扫雷------\n");printf("------------------------\n");printf("----------菜单----------\n");printf("-------1.开始游戏-------\n");printf("-------2.结束游戏-------\n");//基础菜单实现printf("---------3.彩蛋---------\n");return;
}
int main() {int n = 0;//创建选项变量srand((unsigned int)time(NULL));do {menu();printf("请选择选项\n");scanf("%d", &n);switch (n) {case 1:game(); break;case 2:printf("已退出\n"); n = 0; break;case 3:printf("彩蛋\n"); n = 0; break;default:printf("请重新输入\n");}} while (n);//do while的巧妙利用return 0;
}

菜单的基本功能的实现是通过switch语句, 如果要玩游戏输入1进入game函数,如果选择退出或者彩蛋选项,将会在case语句中将n置为0从而跳出do while语句,最终实现是这个样子:

2.2.4初始化期棋盘

从这一部分往下,就是开始要分别写出实现每个小功能的函数,记得在game.c中写完函数要在game.h完成声明。

对于初始化,我们将origin这个棋盘全部置为字符'0',将infor这个棋盘全部置为'*'。

game.c中:

void init(char board[rows][cols], int rows1, int cols1, char set) {int i = 0;for (i = 0; i < rows; i++){int j = 0;for (j = 0; j < cols; j++){board[i][j] = set;}}
}

函数的声明我在这就不完成了,就是一个小小体力活, 最后完整代码会体现出来。效果如下:

2.2.5输出完整棋盘

输出的时候,除了将9*9的核心区域打印出来,还要记得标记行列(1~9)以及x,y轴,增加可玩性。

void print(char board[rows][cols], int row1, int col1) {printf("-----蘸豆---爽-----\n");printf("0 1 2 3 4 5 6 7 8 9 y\n");for (int i = 1; i <= row1; i++) {printf("%d ", i);for (int j = 1; j <= col1; j++) {printf("%c ", board[i][j]);}printf("\n");}printf("x\n");
}

最终效果如下:

2.2.6埋雷的实现

由于埋雷的随机性,这里需要一个c语言中的随机数函数rand。

rand用法:

在C语言中,rand() 是一个标准库函数,用于生成一个伪随机数。这个函数定义在 <stdlib.h> 头文件中。rand() 函数返回一个在 0 到 RAND_MAX 之间的整数,其中 RAND_MAX 是在 <stdlib.h> 中定义的一个宏,其值至少为 32767

由于 rand() 生成的是伪随机数,因此在每次程序运行时,如果不进行任何设置,它将生成相同的随机数序列。为了得到不同的随机数序列,通常在程序开始时使用 srand() 函数来设置随机数生成器的种子。种子的值通常来自于系统时间或者其他变化的数据。

这里就是用时间作为种子:srand((unsigned int)time(NULL)); (在主函数中书写了出来)

埋雷的时候我们使用while循环进行操作,另外有一个小小的注意点,如果该点已经被埋过雷了,注意排除,代码如下:

void setboom(char board[rows][cols], int row1, int col1) {int count = easy;//记录雷数while (count) {int x = rand() % 9 +1;int y = rand() % 9 +1;if (board[x][y] == '0') {board[x][y] = '1';count--;}}
}

如果该位置为'0'就进行埋雷操作,如果为'1'就重复该循环。

2.2.7查询周围雷的数量

在正式开始扫雷函数的实现前,我们还需要先实现查找以该点为中心(非雷)周围八个格子雷的数量。实现这个功能有很多方式,这我采用了ascii码的一些基本特性。 

int set_infor(char board[rows][cols], int x, int y) {return (board[x - 1][y - 1] + board[x][y - 1] +board[x - 1][y] + board[x][y + 1]+ board[x + 1][y - 1] + board[x + 1][y]+ board[x + 1][y + 1] + board[x - 1][y + 1] - 8 * '0');
}

因为我创建的俩个棋盘都是char类型,所以说这个方法就可以使用。

2.2.8扫雷的实现

这块部分比较核心,以下是思路讲解:

  • 创建下x,y变量用于记录坐标,创建win用于标记当前排除了多少个区域。
  • 主体函数为while循环,循环条件为win变量仍然小于9*9总格数减去总雷数,那么胜利条件自然是win变量等于9*9总格数减去总雷数。
  • 接下来讲解while主体里面思路:
  • 先让玩家输入x,y坐标,并判断该坐标是否为坐标上的合法目标,如果不是就让玩家重新输入,如果该坐标合法就进行下一步。
  • 判断如果玩家选择的坐标为雷,则该玩家被炸死并跳出循环。
  • 如果选择的非雷,就打印出该位置上周围八个格子的雷数。

最终代码如下:

void findboom(char origin1[rows][cols], char infor1[rows][cols], int row1, int col1) {int x = 0;int y = 0;int win = 0;//用于记录当前排掉了多少个雷while (win < row1 * col1 - easy) {printf("请输入你要排查的x坐标>__");scanf("%d", &x);printf("请输入你要排查的y坐标>__");scanf("%d", &y);if (x > 0 && y > 0 && x <= row1 && y <= col1) {if (origin1[x][y] == '1') {printf("你个憨批你被炸死了\n");break;}else {int num = set_infor(origin1, x, y);infor1[x][y] = num + '0';print(infor1, col1, row1);win++;}}else {printf("该坐标不在棋盘上,你这个笨蛋\n请重新输入\n");}}if (win == row1 * col1 - easy) {printf("恭喜你!你胜利咯\n");print(origin1, row1, col1);}
}

 运行如下图:

 2.2.9完整代码

game.h:

//相关函数的声明
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<time.h>//记录核心区域
#define row 9//记录行
#define col 9//记录列#define rows row+2
#define cols col+2#define easy 10//记录简单难度有十个雷void menu();void init(char board[rows][cols], int rows1, int cols1, char set);void setboom(char board[rows][cols], int row1, int col1);void print(char board[rows][cols], int row1, int col1);int set_infor(char board[rows][cols], int x, int y);void findboom(char origin1[rows],char infor1[rows][cols],int row1,int col1);

 game.c:

#define  _CRT_SECURE_NO_WARNINGS 1
//相关函数的定义
#include"game.h"
void menu() {printf("------------------------\n");printf("------欢迎来到扫雷------\n");printf("------------------------\n");printf("----------菜单----------\n");printf("-------1.开始游戏-------\n");printf("-------2.结束游戏-------\n");//基础菜单实现printf("---------3.彩蛋---------\n");return;
}void init(char board[rows][cols], int rows1, int cols1, char set) {int i = 0;for (i = 0; i < rows; i++){int j = 0;for (j = 0; j < cols; j++){board[i][j] = set;}}
}void print(char board[rows][cols], int row1, int col1) {printf("-----蘸豆---爽-----\n");printf("0 1 2 3 4 5 6 7 8 9 y\n");for (int i = 1; i <= row1; i++) {printf("%d ", i);for (int j = 1; j <= col1; j++) {printf("%c ", board[i][j]);}printf("\n");}printf("x\n");
}void setboom(char board[rows][cols], int row1, int col1) {int count = easy;//记录雷数while (count) {int x = rand() % 9 +1;int y = rand() % 9 +1;if (board[x][y] == '0') {board[x][y] = '1';count--;}}
}int set_infor(char board[rows][cols], int x, int y) {return (board[x - 1][y - 1] + board[x][y - 1] +board[x - 1][y] + board[x][y + 1]+ board[x + 1][y - 1] + board[x + 1][y]+ board[x + 1][y + 1] + board[x - 1][y + 1] - 8 * '0');
}void findboom(char origin1[rows][cols], char infor1[rows][cols], int row1, int col1) {int x = 0;int y = 0;int win = 0;//用于记录当前排掉了多少个雷while (win < row1 * col1 - easy) {printf("请输入你要排查的x坐标>__");scanf("%d", &x);printf("请输入你要排查的y坐标>__");scanf("%d", &y);if (x > 0 && y > 0 && x <= row1 && y <= col1) {if (origin1[x][y] == '1') {printf("你个憨批你被炸死了\n");break;}else {int num = set_infor(origin1, x, y);infor1[x][y] = num + '0';print(infor1, col1, row1);win++;}}else {printf("该坐标不在棋盘上,你这个笨蛋\n请重新输入\n");}}if (win == row1 * col1 - easy) {printf("恭喜你!你胜利咯\n");print(origin1, row1, col1);}
}

test.c:

#define  _CRT_SECURE_NO_WARNINGS 1//游戏逻辑的实现
#include"game.h"void game() {char origin[rows][cols];char infor[rows][cols];//创建俩个棋盘//初始化棋盘init(origin, rows, cols, '0');init(infor, rows, cols, '*');//打印棋盘用于调试print(infor, row, col);//开始埋雷setboom(origin, row, col);//print(origin, row, col);//开始排雷findboom(origin, infor, row, col);}int main() {int n = 0;//创建选项变量srand((unsigned int)time(NULL));do {menu();printf("请选择选项\n");scanf("%d", &n);switch (n) {case 1:game(); break;case 2:printf("已退出\n"); n = 0; break;case 3:printf("彩蛋是这个~\n"); n = 0; break;default:printf("请重新输入\n");}} while (n);//do while的巧妙利用return 0;
}

3.总结

以上就是扫雷的基本功能实现了,其实还有许多可以扩展,例如点开一个非雷区自动展开周围其他非雷区域,调整难度等,大家都可以自己尝试尝试哦~。都看到这里了,希望大家对我多多支持喔,大家一起进步~

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

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

相关文章

深入了解数据结构中的查找算法

目录 前言 1. 查找的基本概念 2. 顺序查找和折半查找 2.1 顺序查找 2.2 折半查找 2.3分块查找 3. 树形查找 3.1 二叉搜索树 (BST) 3.2平衡二叉树 3.3红黑树 4. B 树和 B 树 4.1 B 树 4.2 B 树 5. 哈希表 (Hash Table) 5.1 基本操作 5.2 实现 5.3 复杂性分析 5…

# IntelliJ IDEA 中 springboot 启动类 SpringApplication.run 报红分析

IntelliJ IDEA 中 springboot 启动类 SpringApplication.run 报红分析 一、原因分析&#xff1a;通常 SpringApplication.run 报红&#xff0c;可能是由以下几种原因造成的&#xff1a; 1、项目的主配置文件中 pom.xml 可能没有导入相关依赖。 2、方法参数错误&#xff1a;S…

全量知识系统 程序详细设计 库模式的存储库模型: “三生”(派生衍生自生) (QA SmartChat)

Q1. 请仔细说说 派生和衍生 各自的意思以及关系 派生和衍生是两个近义词&#xff0c;但在语境不同的情况下有着不同的含义。 派生通常指从某种事物或概念中产生出新的东西或概念。它可以用来表示从一个基本的概念或原始的实体中推导或演化出来的新的思想、理论、产品或方法。…

【C++例】虚函数—代码实例

1.虚函数的动态绑定 //虚函数&#xff08;非静态的成员函数&#xff09; //动态绑定&#xff1a;只有通过基类的指针或引用调用虚函数时 #include<iostream> using namespace std; class Base1 { public:virtual void display() const; }; // 如果把某个函数指定为final…

通用型自定义拼接 SQL 脚本,摆脱重复工作量

通用型自定义拼接sql脚本,摆脱重复工作量 在开发 Restful 接口时&#xff0c;我们经常需要根据前端传递的参数动态拼接 SQL 查询语句&#xff0c;以满足不同的查询需求。本文将介绍一个通用的自定义拼接 SQL 脚本的方法&#xff0c;帮助开发人员减少重复工作量。 首先&#x…

【C++打怪之路Lv3】-- 类和对象(上)

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;C打怪之路&#xff0c;python从入门到精通&#xff0c;数据结构&#xff0c;C语言&#xff0c;C语言题集&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文(平均质量分82)&#…

避坑:我找到了Hexo安知鱼的音乐馆配置(目前好像还没有教程)

我发现安知鱼的音乐配置根本就找不到说明就无奈自己逆向了一下&#xff0c;找到了配置文件&#xff0c;写在博客里记录一下也算是给大家避坑 我的版本是1.6.12&#xff0c;如果你和我的不一样可以看后面 配置文件就是Blog\themes\anzhiyu\source\js\utils.js 打开该文件&…

开发环境搭建:Windows 桌面应用程序

文章目录 前言1、开发环境准备2、Hello World !3、发布总结 前言 操作系统&#xff1a;Windows 10 企业版 LTSC 1809 IDE&#xff1a;Microsoft Visual Studio 2022 Community 说明&#xff1a;Windows 10 企业版 LTSC 1809 不支持 .NET 4.8.1 详情请查看官方说明文档 1、开发…

【华为OD机试】手机App防沉迷系统【C卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 智能手机方便了我们生活的同时,也侵占了我们不少的时间。 “手机App防沉迷系统”能够让我们每天合理地规划手机App使用时间,在正确的时间做正确的事。 它的大概原理是这样的: 在一天24小…

The Log-Structured Merge-Tree (LSM-Tree) 论文阅读笔记

原论文&#xff1a;The Log-Structured Merge-Tree (LSM-Tree) LSM-Tree的简介和关键技术要点 LSM-Tree&#xff08;Log-Structured Merge-Tree&#xff09;是一种为高吞吐量读写操作优化的数据结构&#xff0c;特别适用于写入密集型的应用场景。它由Patrick O’Neil等人开发…

基于streamlit快速部署机器学习项目(Public URL)

基于streamlit的AIGC项目前端展示 1.Streamlit 简介与入门1.1 安装 Streamlit1.2 开发Streamlit应用程序1.3 启动并运行1.3.1 本地运行1.3.2 部署 现在LLM技术发展迅速&#xff0c;很多人在学习的时候&#xff0c;都想展示效果&#xff0c;并且想部署在服务器上&#xff0c;但是…

SpringBoot的核心内容之自动装配

不面试不知道&#xff0c;现在面试的问题真的是五花八门的&#xff0c;最近就有读者说&#xff0c;在面试的过程中&#xff0c;面试官还提到了关于最初版本的 Spring 还有 SpringMVC 以及现在的 SpringBoot 中的一些相关问题&#xff0c;比如他提到了这个 SpringBoot 的自动装配…

【人工智能基础】线性回归实验分析

实验使用到的库&#xff1a;numpy、matplotlib、scikit-learn 实验使用的开发环境&#xff1a;anaconda、jupyter 一、线性回归 线性回归就是使用一个线性函数&#xff08;多项式回归可以是曲线&#xff09;去拟合给定的训练集&#xff0c;测试时&#xff0c;对输入的x值&#…

Selenium一本通

Selenium中文官方文档 从一个测试脚本说起 from selenium import webdriver #引入selenium webdriver模块 driver webdriver.Firefox() #初始化Firefox浏览器的webdriver对象&#xff0c;启动浏览器 driver.get("http://cn.bing.com/") #跳转到对应页面 assert Bi…

Jammy@Jetson Orin - Tensorflow Keras Get Started: Concept

JammyJetson Orin - Tensorflow & Keras Get Started: Concept 1. 源由2. 模型2.1 推理流程2.1.1 获取图像2.1.2 算法识别2.1.3 判断决策 2.2 理想情况2.2.1 多因素输入2.2.2 理想识别概率 2.3 学习过程2.3.1 标记训练集2.3.2 损失函数2.3.3 训练网络2.3.4 渐进方法 3. 总…

jvm(JVM快速入门、stack栈、堆、GC垃圾回收、Arthas)

文章目录 1. JVM快速入门1.1. 结构图1.2. 类加载器ClassLoader1.3. 执行引擎Execution Engine1.4. 本地接口Native Interface1.5. Native Method Stack1.6. PC寄存器(程序计数器)1.7. Method Area方法区 2. stack栈3. 堆3.1. 堆体系概述3.1.1. 新生区3.1.2. 老年代3.1.3. 永久代…

Python 打包:pyinstaller

目录 为什么选择它用法详解所有命令常规-h-v、 --version--distpath DIR--workpath Workpath-y、 --noconfirm--upx dir upx_dir Upx-a、 --ascii--clean--log-level LEVEL 生成内容-D、 --onedir-F、 --onefile--specpath DIR-n NAME&#xff0c;--NAME NAME-w 执行程序 为什么…

python基础知识点(蓝桥杯python科目个人复习计划66)

今日复习内容&#xff1a;算法双周赛 第一题&#xff1a;疯狂星期六 题目描述&#xff1a; 麦肯鸡是一家名声在外的汉堡店&#xff0c;他们最近推出了一份名为vivo50的套餐&#xff0c;只需要在门口大声喊出vivo50&#xff0c;就可以获得这个套餐。 现在&#xff0c;请你打…

【VUE】解决 Element UI 中 el-tab 切换时 ECharts 渲染宽度问题

解决 Element UI 中 el-tab 切换时 ECharts 渲染宽度问题 在使用 Element UI 的 el-tabs 组件时&#xff0c;我们常常会遇到一个问题&#xff1a;当 ECharts 图表被放在非当前激活的 Tab 内时&#xff0c;图表无法正确渲染其宽度和高度。由于在 Tab 未激活状态下&#xff0c;图…

[Java、Android面试]_21_Dalvik VM与JVM的区别

欢迎查看合集&#xff1a; Java、Android面试高频系列文章合集 文章目录 1. 结构2. 编译3. 运行环境4. Dalvik进程管理 Dalvik是android系统中的虚拟机&#xff0c;JVM是java虚拟机。 Dalvik VM可以支持将已转化为.dex文件格式的java应用程序的运行&#xff0c;.dex是专为Dalvi…