C语言--从零开始的扫雷游戏

C语言--从零开始的扫雷游戏

  • 1. 游戏说明
  • 2. 总体代码
  • 3. 详细讲解
    • 3.1 菜单部分
    • 3.2 游戏主体部分
      • 3.2.1 总体分析
      • 3.2.2 棋盘初始化
      • 3.2.3 棋盘展示
      • 3.2.4 设置地雷
      • 3.2.5 扫雷阶段
      • 3.2.6 统计雷个数的代码
      • 3.2.7 使用迭代的方式进行展开:
      • 3.2.8 扫雷部分主体代码
  • 4. 总结

1. 游戏说明

扫雷游戏的功能说明:

• 使⽤控制台实现经典的扫雷游戏

• 游戏可以通过菜单实现继续玩或者退出游戏

• 扫雷的棋盘是9*9的格⼦

• 默认随机布置10个雷

• 可以排查雷

◦ 如果位置不是雷,就显⽰周围有⼏个雷

◦ 如果位置是雷,就炸死游戏结束

◦ 把除10个雷之外的所有雷都找出来,排雷成功,游戏结束
在这里插入图片描述
在这里插入图片描述

2. 总体代码

//头文件部分
#define  _CRT_SECURE_NO_WARNINGS
#pragma once#include  <stdio.h>
#include  <time.h>
#include  <stdlib.h>//棋盘长宽
#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASY_COUNT 10 //地雷个数//初始化棋盘
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char ch);//打印棋盘
void PrintBoard(char arr[ROWS][COLS], int row, int col);//设置地雷
void MineSet(char mine[ROWS][COLS], int row, int col);//找雷
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS],int row,int col);//以上为头文件部分
//测试部分
#define  _CRT_SECURE_NO_WARNINGS#include "game.h"void menu()
{printf("******************\n");printf("****  1.play  ****\n");printf("****  0.exit  ****\n");printf("******************\n");
}void game()
{char show[ROWS][COLS];//  '*'char mine[ROWS][COLS];//  '数字'//初始化InitBoard(show, ROWS, COLS, '*');InitBoard(mine, ROWS, COLS, '0');//打印棋盘//PrintBoard(mine, ROW, COL);PrintBoard (show, ROW, COL);//设置地雷MineSet (mine, ROW, COL);//PrintBoard (mine, ROW, COL);//找地雷FindMine(mine,show, ROW, COL);}int main()
{int input=0;srand((unsigned int)time(NULL));do{menu();printf("请输入你的选择:> ");scanf("%d", &input);switch (input){case 1:printf("< 请注意,本轮一共有%d颗雷 >\n", EASY_COUNT);game();break;case 0:printf("游戏结束\n");break;default:printf("输入错误,请重新输入!\n");break;}} while (input);return 0;
}//以上为测试部分
//主体游戏部分
#define  _CRT_SECURE_NO_WARNINGS#include "game.h"void InitBoard(char arr[ROWS][COLS], int rows, int cols, char ch)
{int i = 0;int j = 0;for (i = 0; i < ROWS; i++){for (j = 0; j < COLS; j++){arr[i][j] = ch;}}
}void PrintBoard(char arr[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("********************\n");for (i = 0; i <= row; i++){printf("%d_", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d|", i );for (j = 1; j <= col; j++){printf("%c ", arr[i][j]);}printf("\n");}printf("********************\n");
}//设置地雷
void MineSet(char mine[ROWS][COLS], int row, int col)
{int count = 0;while (count < EASY_COUNT){int x = rand() % row + 1;int y = rand() % col + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count++;}}
}//统计该点附近雷的个数
int MineCount(char mine[ROWS][COLS],int x,int y )
{int i = 0;int j = 0;int count = 0;for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){count =count + (mine[i][j] - '0');}}return count;
}//如果附近没有雷,直接展开
void BlankExpansion(char mine[ROWS][COLS],char show[ROWS][COLS],int x,int y)
{int i = 0;int j = 0;int count = MineCount(mine, x, y);if (count == 0){show[x][y] = ' ';for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){if (show[i][j] == '*'){BlankExpansion(mine, show, i, j);}}}}else{show[x][y] = count + '0';}}//统计剩余‘*’的个数
int LeftCount(char show[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;int count = 0;for (i = 1; i <= row; i++){for (j = 1; j <= col; j++){if (show[i][j] == '*')count++;}}return count;
}//扫雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{int count = row * col;int x = 0;int y = 0;while (count > EASY_COUNT){	printf("请输入坐标:> ");scanf("%d%d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (mine[x][y] == '1'){printf("好可惜,你被雷炸死了\n");PrintBoard(mine, ROW, COL);break;}else{BlankExpansion(mine, show, x, y);count = LeftCount(show, ROW, COL);PrintBoard(show, ROW, COL);printf("剩余未知点数: %d\n", count);}}else{printf("输入坐标错误,请重新输入\n");}}if (count == EASY_COUNT){printf("恭喜你,你赢了!\n");}}//主体游戏部分

3. 详细讲解

3.1 菜单部分

打开游戏程序,第一步是展示菜单,提醒玩家进行选择,菜单与代码如下:
在这里插入图片描述


void menu()
{printf("******************\n");printf("****  1.play  ****\n");printf("****  0.exit  ****\n");printf("******************\n");
}

根据玩家的选择,进入不同的程序当中,当玩家选择 1 的时候,进入游戏程序,当玩家选择 0 的时候,结束游戏。考虑到玩家玩游戏不可能只是玩一次,因而需要在开头加入一个循环部分。代码实现如下:


int main()
{srand((unsigned int)time(NULL));//生成随机数int input=0;do{menu();printf("请输入你的选择:> ");scanf("%d", &input);switch (input){case 1:printf("< 请注意,本轮一共有%d颗雷 >\n", EASY_COUNT);game();break;case 0:printf("游戏结束\n");break;default:printf("输入错误,请重新输入!\n");break;}} while (input);return 0;
}

3.2 游戏主体部分

3.2.1 总体分析

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。
因为我们需要在 9 * 9 的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个 9 * 9 的数组来存放信息。

在这里插入图片描述
那如果这个位置布置雷,我们就存放1,没有布置雷就存放0.

在这里插入图片描述
假设我们排查 ( 2 , 5 ) 这个坐标时,我们访问周围的⼀圈 8 个⻩⾊位置,统计周围雷的个数是 1.
假设我们排查 ( 8 , 6 ) 这个坐标时,我们访问周围的⼀圈 8 个⻩⾊位置,统计周围雷的个数时,最下⾯的三个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的 9 * 9的坐标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成 11 * 11 是⽐较合适。
在这里插入图片描述
在这里插入图片描述

我们在棋盘上布置了雷,棋盘上雷的信息(1)和⾮雷的信息(0),假设我们排查了某⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪⾥呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。
这⾥我们肯定有办法解决,⽐如:雷和⾮雷的信息不要使⽤数字,使⽤某些字符就⾏,这样就避免冲突了,但是这样做棋盘上有雷和⾮雷的信息,还有排查出的雷的个数信息,就⽐较混杂,不够⽅便。
这⾥我们采⽤另外⼀种⽅案,我们专⻔给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到
mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。
同时为了保持神秘,show数组开始时初始化为字符’*‘,为了保持两个数组的类型⼀致,可以使⽤同⼀套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’。如下如:

在这里插入图片描述
mine数组布置雷后的状态

在这里插入图片描述
show输出初始化的状态

对应的数组应该是:

char mine[11][11] = {0};//⽤来存放布置好的雷的信息 char show[11][11] = {0};//⽤来存放排查出的雷的个数信息 

为方便代码管理,将文件内容分为以下三个方面

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

为了方便后期对难易程度的修正,可以将行和列的信息用宏的方式进行修正,如下所示:

//棋盘长宽
#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2//地雷个数
#define EASY_COUNT 10//修改后的数组
char show[ROWS][COLS];//  '*'
char mine[ROWS][COLS];//  '数字'

3.2.2 棋盘初始化

在布雷之前,需要将两个棋盘进行初始化。为了后面再计数的时候不影响判断,此时应该将 ROWS 与 COLS 传入函数当中。为了使初始化函数的能对两个数组都起作用,因而还要将要初始化的字符传进去。代码如下:


void InitBoard(char arr[ROWS][COLS], int rows, int cols, char ch)
{int i = 0;int j = 0;for (i = 0; i < ROWS; i++){for (j = 0; j < COLS; j++){arr[i][j] = ch;}}
}

3.2.3 棋盘展示

初始化之后,需要写一个棋盘显示函数来显示棋盘的初始化结果以及后面不同结果的展示,代码如下:


void PrintBoard(char arr[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("********************\n");for (i = 0; i <= row; i++){printf("%d_", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d|", i );for (j = 1; j <= col; j++){printf("%c ", arr[i][j]);}printf("\n");}printf("********************\n");
}

此处需要注意,传入数组的时候,要把整体 11 * 11 的数一起传进去,目的使方便后面对整体把控,但是在进行显示的时候,只需要显示 9 * 9 的范围即可,第一行和最后一行只是我们为了方便之后对地雷的统计而加进去的,给玩家展时的界面里面不应该存在第一行与最后一行的内容

3.2.4 设置地雷

在 mine 数组里面随机放置预选设置EASY_COUBT好的地雷数目,即EASY_COUBT代表的数。使用随机数生成函数生成随机坐标,mine 数组里面这个坐标的符号如果是 ’ 0 ’ ,则将 ’ 1 ’ 放入里面,用 count 进行计数,使布置的地雷数小于EASY_COUBT代表的数。代码如下:

void MineSet(char mine[ROWS][COLS], int row, int col)
{int count = 0;while (count < EASY_COUNT){int x = rand() % row + 1;int y = rand() % col + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count++;}}
}

在这里应该注意,布置地雷的时候,数组同样是将全部传递过来,但是进行布雷操作的时候,只对 1 ~ 9 行数组进行操作,对剩下的两组不做任何操作。此外,因为 rand() % row 的结果是 0 ~ 8 之间的数,需要在此基础上加一,才能保证其只对 1 ~ 9 进行操作。

3.2.5 扫雷阶段

玩家随意输入一个坐标,先进行判断其是否在棋盘之内,如果不是,则提醒其重新输入,如果是,则要判断该点是否的是雷,如果是,则直接结束游戏,如果不是,则要将其附近的雷的个数统计出来。

3.2.6 统计雷个数的代码

int MineCount(char mine[ROWS][COLS],int x,int y )
{int i = 0;int j = 0;int count = 0;for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){count =count + (mine[i][j] - '0');}}return count;
}

如果统计的个数为非零,则直接在 show 数组相应的坐标上直接显示附近雷的个数,如下图:
在这里插入图片描述
如果为零,则要对其附近的点进行架空处理,即在 show 数组上把其附近全部为零的坐标全部变为空格,直至附近的数全部显示为非零值,如下图:
在这里插入图片描述

3.2.7 使用迭代的方式进行展开:

当玩家输入的坐标附近的雷统计数为零的时候,对该坐标附近的八个坐标都要进行判断其附近是否为零,如果不为零,直接显示其附近雷的个数,如果为零,又要调用该函数对其附近的八个坐标进行判断,一直往复循环,直至附近的坐标全部为非零数。代码如下:

void BlankExpansion(char mine[ROWS][COLS],char show[ROWS][COLS],int x,int y)
{int i = 0;int j = 0;int count = MineCount(mine, x, y);if (count == 0){show[x][y] = ' ';for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){if (show[i][j] == '*'){BlankExpansion(mine, show, i, j);}}}}else{show[x][y] = count + '0';}}

3.2.8 扫雷部分主体代码

void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{int count = row * col;int x = 0;int y = 0;while (count > EASY_COUNT){	printf("请输入坐标:> ");scanf("%d%d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (mine[x][y] == '1'){printf("好可惜,你被雷炸死了\n");PrintBoard(mine, ROW, COL);break;}else{BlankExpansion(mine, show, x, y);count = LeftCount(show, ROW, COL);PrintBoard(show, ROW, COL);printf("剩余未知点数: %d\n", count);}}else{printf("输入坐标错误,请重新输入\n");}}if (count == EASY_COUNT){printf("恭喜你,你赢了!\n");}}

4. 总结

在进行传参数的时候,一定要紧记,传数组时,要将整个数组全部传过去,但是对数组进行操作的时候,只对 1 ~ 9 行进行操作,因为玩家所能看到的展示界面只有 1 ~ 9 行,唯独初始化的时候是要对整个数组进行操作。

以上就是小编要分享的内容,如果有不对的地方,欢迎大家在留言区讨论。

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

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

相关文章

图片格式转换怎么操作?这一个方法快快收藏

图片格式转换能够改变图片的质量、大小兼容性。不同的图片格式用途也不同&#xff0c;当我们需要转换图片格式的时候要怎么操作呢&#xff1f;下面&#xff0c;小编给大家分享一款操作简单&#xff0c;小白也能轻松上手的图片转换器&#xff08;https://www.yasuotu.com/geshi&…

DDD领域模型驱动

传统MVC架构 DDD架构: api层:api请求方式,透传【传递参数】,几个业务对应api 业务层:做编排,业务里要有哪些服务,执行顺序是什么,以及怎么做 领域层:负责领域内调用,然后领域怎么划分 Dao层:数据库操作【或者另外一个应用 数据源之类的】 遵守原则: ①允许跨层…

什么是架构?架构设计原则是哪些?什么是设计模式?设计模式有哪些?

什么是架构?架构设计原则是哪些?什么是设计模式?设计模式有哪些? 架构的本质 架构本身是一种抽象的、来自建筑学的体系结构,其在企业及IT系统中被广泛应用。 架构的本质是对事物复杂性的管理,是对一个企业、一个公司、一个系统复杂的内部关系进行结构化、体系化的抽象,…

php apache 后台超时设置

最近在写一个thinkphp项目的时候&#xff0c;发现Ajax从后端请求数据时间比较长&#xff0c;大概需要45秒左右&#xff0c;但是一旦请求时间超过40s&#xff0c;页面就会超时500了&#xff0c;一开始以为是ajax请求时间不能太长&#xff0c;后来将Ajax请求改为同步且timeout设置…

iTOP-3A5000开发板ATX规范设计外加机箱就是一台电脑主机

性能强 采用全国产龙芯3A5000处理器&#xff0c;基于龙芯自主指令系统 (LoongArch)的LA464微结构&#xff0c;并进一步提升频率&#xff0c;降低功耗&#xff0c;优化性能。 桥片 采用龙芯 7A2000&#xff0c;支持PCIE 3.0、USB 3.0和 SATA 3.0.显示接口2 路、HDMI 和1路 VGA&…

DARTS: DIFFERENTIABLE ARCHITECTURE SEARCH

DARTS&#xff1a;可微架构搜索 论文链接&#xff1a;https://arxiv.org/abs/1806.09055 项目链接&#xff1a;https://github.com/quark0/darts ABSTRACT 本文通过以可微分的方式表述任务&#xff0c;解决了架构搜索的可扩展性挑战。与在离散和不可微搜索空间上应用进化或强…

VR文化旅游虚拟现实介绍|虚拟现实元宇宙|VR设备购买

虚拟现实&#xff08;VR&#xff09;技术正在改变我们对文化旅游的认知和体验。通过VR技术&#xff0c;人们可以身临其境地探索世界各地的文化遗产和旅游景点&#xff0c;无需亲临现场也能感受到逼真的体验。以下是VR文化旅游虚拟现实的介绍&#xff1a; 身临其境的体验&#x…

web项目抢购模块测试

web项目抢购模块测试 抢购模块(先测后台,再测前台)流程抢购用例编写测试点--后台抢购用例编写测试点--前台用例设计 面试题1: 当你发现研发实现的结果与需求不一致时怎么办? 需求评审的时候:需要确认所有输入类型的校验是针对单独的输入框做的还是在最终提交时校验 抢购模块 需…

springboot266基于Web的农产品直卖平台的设计与实现

农产品直卖平台的设计与实现 摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#x…

保研复习数据结构记(4)--树(二叉树、线索树、哈夫曼树,并查集)

一.树的基本术语 1.树 什么是空树&#xff1f;结点数为0的树非空树的特性&#xff1f;有且仅有一个根结点&#xff0c;没有后继的结点称为“叶子结点”&#xff0c;有后继的结点称为“分支结点”&#xff0c;除了根结点外任何一个结点都有且仅有一个前驱&#xff0c;每个结点…

Linux 基本命令

文章目录 1.echo2.cd3.find4.mkdir5.cp6.rm7.wc8.tar9.tail10.vim11.grep12.sed13 touch14 ls15 快捷键16 ln17 mv18 useradd19 usermod20 su 每天一个Linux命令 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 1.echo 中文 (Chinese): “回声” 或 “输…

(开源项目)OpenHarmony、社区共建Sample合入要求

1.新增Sample功能不能重复于当前已有Sample的功能&#xff1b; 2.新增Sample的工程推荐使用ArkTS语言编写&#xff1b; 3.新增Sample的工程推荐使用Stage模型编写&#xff1b; 4.新增Sample的工程中需要包含UI自动化用例&#xff08;ohosTest工程模块&#xff09;&#xff0…

使用腾讯云快速搭建WordPress网站流程详解

专栏系列文章&#xff1a; WordPress建站主题美化系列教程https://blog.csdn.net/seeker1994/category_12184577.html 一文搞懂WordPress是什么&#xff1f;为什么用它建站&#xff1f;怎么安装与部署&#xff1f; 初次安装WordPress后如何进行网站设置&#xff08;主题安装、…

String 底层是如何实现的?

1、典型回答 String 底层是基于数组实现的&#xff0c;并且数组使用了 final 修饰&#xff0c;不同版本中的数组类型也是不同的&#xff1a; JDK9 之前&#xff08;不含JDK9&#xff09; String 类是使用 char[ ]&#xff08;字符数组&#xff09;实现的但 JDK9 之后&#xf…

浅淡 C++ 与 C++ 入门

我们知道&#xff0c;C语言是结构化和模块化的语言&#xff0c;适用于较小规模的程序。而当解决复杂问题&#xff0c;需要高度抽象和建模时&#xff0c;C语言则不合适&#xff0c;而C正是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库…

铠侠SSD新型接口EDSFFE3 CM7 CD8P系列NVMe2.0 PCIe5.0

固态硬盘几个大厂&#xff0c;如果英特尔、铠侠、三星&#xff0c;陆续推出E3系列SSD&#xff0c;今天就我个人对E3系列的了解&#xff0c;做一个简单介绍。如有不妥&#xff0c;请多多交流 什么是E3&#xff1f; 简单理解就是一种新型的SSD外形尺寸。 E3 系列外形尺寸包含四种…

【教程】APP加固的那些小事

摘要 APP加固是保护APP代码逻辑的重要手段&#xff0c;通过隐藏、混淆、加密等操作提高软件的逆向成本&#xff0c;降低被破解的几率&#xff0c;保障开发者和用户利益。本文将介绍APP加固常见失败原因及解决方法&#xff0c;以及处理安装出现问题的情况和资源文件加固策略选择…

RabbitMQ发布确认高级版

1.前言 在生产环境中由于一些不明原因&#xff0c;导致 RabbitMQ 重启&#xff0c;在 RabbitMQ 重启期间生产者消息投递失败&#xff0c; 导致消息丢失&#xff0c;需要手动处理和恢复。于是&#xff0c;我们开始思考&#xff0c;如何才能进行 RabbitMQ 的消息可靠投递呢&…

Eohours防作弊算工时打卡HR管控系统(中英文版)

适用工程类算工时的企业&#xff0c;目前全世界拥有外劳的建筑企业更适合&#xff0c;他们有很多外国劳工&#xff0c;适合算工时。让工人得到一个公平&#xff0c;透明的工时数据&#xff0c;知道自己这个月能拿多少薪水。企业也能很好控制管理层和工人之间的协作&#xff0c;…

Spring之注入模型

前言 之前我写过一篇关于BeanDefinition的文章,讲述了各个属性的作用,其中有一个属性我没有提到,因为这个属性比较重要,所以这里单独开一篇文章来说明 上一篇博文链接Spring之BeanDefinitionhttps://blog.csdn.net/qq_38257958/article/details/134823169?spm1001.2014.3001…