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.mzph.cn/news/741622.shtml

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

相关文章

docker常用操作-docker私有仓库的搭建(Harbor),并将本地镜像推送至远程仓库中。

1、docker-compose安装&#xff0c;下载docker-compose的最新版本 第一步&#xff1a;创建docker-compose空白存放文件vi /usr/local/bin/docker-compose 第二步&#xff1a;使用curl命令在线下载&#xff0c;并制定写入路径 curl -L "https://github.com/docker/compos…

Linux学习:基础开发工具的使用(1)

目录 1. Linux软件包管理器&#xff1a;yum工具1.1 yum是什么&#xff08;软件商城&#xff09;1.2 yum的使用1.3 yum的背景生态 2. 项目开发与集成开发环境3. vim编辑器3.1 vim编辑器的常见模式与模式切换3.3 vim编辑器的使用3.3.1 命令模式下的常见命令&#xff1a;3.3.2 vim…

【rk3368 android6.0 恢复出厂设置功能】

rk3368 android6.0 恢复出厂设置功能 恢复出厂设置三种方法一&#xff0c;设置--进入恢复出厂设置页面二&#xff0c;发送广播形式三&#xff0c;命令形式总结 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 恢复…

阿里云数据盘挂载目录

1、先登录服务器创建新目录aaa 2、云盘都快照备份下。后续操作完核实无误了&#xff0c;您根据您需求删除快照就行&#xff0c; 然后登录服务器内执行&#xff1a; fdisk -l sblk blkid ll /aaa 3、执行&#xff1a;&#xff08;以下命令是进行数据盘做ext4文件系统并挂载…

主机、虚拟机和开发板三者互相之间能ping通的配置参数

主机网络配置 开发板网络配置 虚拟机网络配置

无人火箭研发中的重要性

Python在无人火箭研发中的重要性可以从以下几个方面来讨论&#xff1a; 大数据处理&#xff1a;无人火箭的研发过程中会产生大量的数据&#xff0c;例如传感器数据、飞行轨迹数据等。Python具有强大的数据处理能力&#xff0c;可以方便地对这些数据进行处理、分析和可视化&…

OKHttpRetrofit

完成一个get请求 1.导入依赖 implementation("com.squareup.okhttp3:okhttp:3.14.")2.开启viewBinding android.buildFeatures.viewBinding true 3.加网络权限 和 http明文请求允许配置文件 <?xml version"1.0" encoding"utf-8"?> &l…

在受污染的二叉树中查找元素(Lc1261)——DFS+哈希表

给出一个满足下述规则的二叉树&#xff1a; root.val 0如果 treeNode.val x 且 treeNode.left ! null&#xff0c;那么 treeNode.left.val 2 * x 1如果 treeNode.val x 且 treeNode.right ! null&#xff0c;那么 treeNode.right.val 2 * x 2 现在这个二叉树受到「污染…

python下载文件的一些方法

背景,pdf或其他格式文件从网页(包括其他存储,比如AWS的S3,阿里的OSS等)下载,可以用如下方法: 1. 使用 requests 模块requests 是一个广泛使用的HTTP客户端库, 可以方便地下载各种类型的文件:import requests def download_file_with_requests(url, local_filename=No…

实战LangChain(二):探索RAG——为聊天机器人注入知识

实战LangChain(二):探索RAG——为聊天机器人注入知识 实战LangChain(一):构建您的第一个聊天机器人_langchai 机器人 实战LangChain(二):探索RAG——为聊天机器人注入知识 文章目录 实战LangChain(二):探索RAG——为聊天机器人注入知识引言一、RAG是什么二、使用步…

数据结构:详解【顺序表】的实现

1. 顺序表的定义 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。动态顺序表与数组的本质区别是——根据需要动态的开辟空间大小。 2. 顺序表的功能 动态顺序表的功能一般有如下几个&#xff1a; 初始化顺序表打印顺序…

腾讯云轻量服务器地域怎么选择?上海/北京/广州哪个合适?

腾讯云轻量应用服务器地域如何选择&#xff1f;地域就近选择&#xff0c;北方选北京地域、南方选广州地域&#xff0c;华东地区选上海地域。广州上海北京地域有什么区别&#xff1f;哪个好&#xff1f;区别就是城市地理位置不同&#xff0c;其他的差不多&#xff0c;不区分好坏…

【C++教程从0到1入门编程】第九篇:STL中Vector类

一、vector的介绍 1.vector的介绍 vector是表示可变大小数组的序列容器。 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&…

G6.antv自定义箭头 懒加载数据 箭头丢失

懒加载数据会导致箭头消失&#xff0c;只能自定义箭头 层次图&#xff1a;dagre 折线&#xff1a;polyline 设置endArrow&#xff1a;true 接口懒加载数据&#xff0c;执行 this.graph.read(this.graphData); this.graph.zoom(0.6);箭头消失&#xff0c;需自定义箭头 空心箭头…

maya自动重定向测试

目录 maya导入bvh maya python脚本测试 maya python脚本调用 maya重定向 Pose-to-Motion: Cross-Domain Motion Retargeting with Pose Prior maya导入bvh GitHub - jhoolmans/mayaImporterBVH: Importer script for BVH maya python脚本测试 首先打开MAYA&#xff0c;然…

香港理工大学主办!2024年第八届电力能源系统与应用国际会议(ICoPESA 2024)即将召开!

2024年第八届电力能源系统与应用国际会议&#xff08;ICoPESA 2024&#xff09; 2024年6月24日-26日 中国香港 ICoPESA 2024-Hong Kong (icpesa.org)https://icpesa.org/index.html 会议组织单位 会议出版及检索&#xff1a; 会议录用并注册的论文将由IEEE出版&#xff0c;…

一直出现问题,发现服务器磁盘空间已满导致,腾出服务器磁盘空间命令

要解决服务器磁盘空间已满的问题&#xff0c;你可以按照以下步骤操作&#xff1a; 查看磁盘使用情况&#xff1a;使用df -h&#xff0c; du -s -h ./*命令来查看服务器的磁盘空间使用情况。查找大文件&#xff1a;使用du -a | sort -rn | head -5命令来找出占用空间最大的前5个…

[BJDCTF2020]Cookie is so stable

hint提示查看cookies flag.php页面我们先随便输入一个名字 输入后我们重新进一次flag.php&#xff0c;发现cookie里存储了刚刚登陆时输入的用户名&#xff0c;直接猜是ssti 尝试后根据ssti特征判断是twig模板 {{_self.env.registerUndefinedFilterCallback("exec")…

西门子S7.NET通信库执行【写】操作的实践与优化

本文将深入探讨如何使用西门子S7.NET通信库执行写操作&#xff0c;并分享一些实践中的优化经验。我们将从基本概念、写操作流程、效率影响因素、性能测试与优化、常见错误及避免方法&#xff0c;以及面向对象的通信库使用示例等方面进行详细讲解。 一、西门子S7.NET通信库的基…