当前位置: 首页 > article >正文

C语言(超详细讲解,帮你零基础玩转C语言) | 扫雷游戏 —— 附完整代码可以直接copy运行游玩

扫雷游戏

引言

在C语言中,有一个很经典的小游戏——扫雷,这个游戏涉及到了数组和函数两大知识点,如果小伙伴们对这两个知识点有所了解,对于这篇文章会了解的更加深刻。btw 结尾附上完整代码,可以点击目录跳转直接运行游玩。

目录

  • 扫雷游戏
    • 引言
    • 扫雷游戏的分析与设计
      • 1.扫雷游戏的功能说明
      • 2.数据结构的分析——最重要的一个部分
      • 3.扫雷游戏的代码实现
    • 结尾——完整代码

扫雷游戏的分析与设计

在这里我会带着大家对于扫雷游戏的结构框架有一个整体的认识,帮助我们在后期的代码实现的更加效率。


1.扫雷游戏的功能说明

  • 扫雷游戏要基于控制台实现
  • 游戏可以通过菜单实现 继续游玩 还是 退出游戏
  • 扫雷的棋盘应该是9*9的格子
  • 默认的简单难度要随机布置10个雷
  • 可以排查雷
    1.如果所选位置不是雷,就显示周围有几个雷
    2.如果所选位置是雷,就被炸死游戏结束
    3.把除10个雷之外的所有非雷都找出来,就算排雷成功,游戏结束

下面是游戏的界面,以及我的酋长血统(排的第一个坐标就是雷:>)。
在这里插入图片描述


2.数据结构的分析——最重要的一个部分

要想实现扫雷游戏,我们就需要一个数据结构来存储我们的棋盘,因此我们采用一个 11x11(内置9x9的棋盘)的二维数组来存储。 如果这个地方没有雷我们就放入一个'0',如果有雷我们就要放入一个'1'在这里插入图片描述
为什么我们要采用一个 11x11(内置9x9的棋盘)的二维数组来存储呢? 因为如果是一个9x9的棋盘,我们访问边界的点位时,需要查看他周边一圈是否有雷。这个时候就有可能造成越界访问。因此我们在9x9的棋盘的外面加上一圈,并且不去布置雷,就可以做成一个11x11的棋盘,然后让玩家用到的还是9x9的棋盘就可以解决了。

接下来,我们要设置两个棋盘来运行游戏,一个棋盘(对应数组mine)我们专门用来存放布置好的雷的信息,另一个棋盘(对应另一个数组show)存放排查出雷的信息。 这样可以避免一个数组无法分辨此坐标是雷还是周边有雷的情况了。
我们只需要把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给玩家。
同时为了保持神秘,show数组开始时初始化为字符'*',为了保持两个数组的类型一致,可以使用同一套函数处理,mine数组最开始也初始化为'0',布置雷的点位初始化为'1'。见下图
在这里插入图片描述
对应的数组为

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

3.扫雷游戏的代码实现

在这里我将会手把手带你逐行分析代码如何实现扫雷游戏。请打起精神,这里将是本篇的核心。
首先我设计了3个文件用来实现,后面只会讲到game.c文件和test.c文件,game.h仅用来做各种声明。

test.c    //文件中写游戏的测试逻辑,以及各种函数的使用
game.c    //文件中写游戏中函数的实现等,以及各种函数的定义
game.h    //文件中写游戏需要的数据类型和函数声明等

game.h文件——需要大家看一下,知道每个变量和数据类型对应的是什么

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//文件中写游戏需要的数据类型和函数声明等#define EASY_COUNT 10#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2//各种函数的声明——声明是要加;//初始化棋盘函数
void initBoard(char board[ROWS][COLS], int rows, int cols, char set);//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);//接下来的操作是在逻辑数组上完成的
//然后是布置雷
void SetMine(char board[ROWS][COLS],int row,int col);//最后是排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

接下来是代码实现的讲解——每一段函数所属的文件我都会在代码块的第一行给大家标注出来
我们首先需要一个菜单来供大家选择

//test.c文件
void menu()
{printf("**************************\n");printf("********  1.扫雷  ********\n");printf("********  0.exit  ********\n");	//用0是因为也可以将选择的0作为假条件跳出循环printf("**************************\n");
}

如果玩家选择1,则进入游戏,如果玩家选择0,则退出游戏。


然后使用do while循环和switch语句——做到菜单可以多次供你选择。

//test.c文件
int main()
{int input = 0;srand((unsigned int)time(NULL));  //初始化随机数do{//先将菜单显示出来供你选择menu();printf("请选择:");scanf("%d",&input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default: printf("选择错误,重新选择\n");break;}} while(input);return 0;
}
srand((unsigned int)time(NULL));  //初始化随机数

这一段代码是用来创建随机数的,现在可以先不用管,后面随机点位布置雷的时候会讲到。
这段代码的意思是:首先让玩家输入一个值,并把这个值给到dowhile循环和switch分支的条件,他们接收到1和0,都会有对应的措施。


这个时候我们的初始框架就有了。接下来我们要实现game()函数。
game()
首先第一步初始化棋盘

//game.c文件中定义
void initBoard(char board[ROWS][COLS],int rows,int cols ,char set)  
{//参数需要传一个数组,以及这个数组的长度——数组的长度不要用宏,还有要放置的元素。//使用for循环的嵌套来实现初始化int i = 0;for (i=0;i<rows;i++){int j = 0;for (j = 0;j<cols;j++){board[i][j] = set;}}
}

简单的for循环嵌套,在棋盘中放置'0' '*'

//test.c文件中使用
initBoard(mine,ROWS,COLS,'0');
initBoard(show,ROWS,COLS,'*');

第二步,打印棋盘

//game.c文件中定义
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{//依旧是使用两个for循环来打印数组int i = 0;//打印棋盘框架printf("----------扫雷游戏----------\n");for (i=0;i<=col;i++){printf("%d ",i);		//打印棋盘的横坐标}printf("\n");for (i = 1; i <= row; i++){printf("%d ",i);   //打印棋盘的纵坐标int j = 0;for (j = 1; j <= col; j++){printf("%c ",board[i][j]);}printf("\n");}
}

通过for循环打印棋盘的横坐标,然后用for循环的嵌套,打印棋盘的纵坐标以及棋盘上的信息。

//test.c文件中使用
DisplayBoard(show, ROW, COL);//这里向玩家展示是9x9的棋盘,所以用ROW和COL

接下来的操作是在逻辑数组(mine)上完成的
第三步,布置雷

//game.c文件中定义
void SetMine(char board[ROWS][COLS], int row, int col)
{//布置10个雷//生成随机的坐标,布置雷int count = EASY_COUNT;    //可以在这里根据难度的不同来设置雷的数量while (count){//制定x y的随机值int x = rand() % row + 1;int y = rand() % col + 1;//如果这个地方是'0',就放设置一个雷,并用count计数if (board[x][y] == '0'){board[x][y] = '1';count--;}}}

通过while循环和rand()函数来生成随机的坐标放置雷,并用count来计数。

//test.c文件中使用
SetMine(mine, ROW, COL);

最后一步,排查雷——也是扫雷游戏的核心逻辑

//game.c文件中定义
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{//坐标为x , y 形式int win = 0;while (win < row * col - EASY_COUNT){int x = 0;int y = 0;printf("请输入要排查的坐标:");scanf("%d %d", &x, &y);int c;										  // 防止非法格式输入坐标while ((c = getchar()) != '\n' && c != EOF);  // 因为在缓冲区中,非法格式的坐标不能被读取出来,数据就一直在缓冲区中。// 每一次循环的scanf,都会直接去缓冲区拿数据,每次都是非法字符,就会造成死循环。if (x >= 1 && x <= row && y >= 1 && y <= col)	//因为我们的棋盘实际上是11*11的,但是玩家所看到的9*9的,所以我们需要让坐标在合法范围内{//开始判定是否是雷if (mine[x][y] == '1'){printf("很遗憾,你被炸死了\n");DisplayBoard(mine, ROW, COL);break;}else{//该位置不是雷,就统计这个坐标周围有几个雷int count = GetMineCount(mine, x, y);   //统计周围有几个雷show[x][y] = count + '0';	            //将信息同步到玩家的棋盘上DisplayBoard(show, ROW, COL);win++;                                  //统计扫了几个点位了 }}else{printf("坐标非法,重新输入\n");}}if (win == row * col -EASY_COUNT)//row*col为棋盘中所有的点位,EASY_COUNT为棋盘中所有的炸弹数,当win==两位相减就证明已经排除了所有的雷{printf("恭喜你,排雷成功\n");DisplayBoard(mine, ROW, COL);   //打印棋盘}}

首先函数参数传入两个数组用来判定和展示,然后设置一个变量win来记录查找的点位数量,如果点位数量等于了全部点位减去全部雷位(也就是所有安全点位),则证明挑战成功。其次通过第一个外循环来接收玩家选择的点位以及通过if语句来判断是否通关。在外循环中内置一个if - else结构,来判断玩家输入的是否是正确的合法坐标。嵌套的if - else结构来判定是否有雷,以及不是雷的话,统计坐标周围有几个雷。并将棋盘信息展示给玩家。

//game.c文件中定义以及使用
//计算周围有几颗雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{//计算所选坐标的周围8个坐标有几个 '1'——雷return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
}

其中涉及到的统计炸弹函数GetMineCount(mine, x, y)在这里,此函数把所选点位旁边的8个点位相加,如果都不是雷则相加值减去8个'0',就得0。相反同理。

//test.c文件中使用

结尾——完整代码

game.c文件

#define _CRT_SECURE_NO_WARNINGS 1 
#include"game.h"//文件中写游戏中函数的实现等
//各种函数的定义//初始化棋盘函数
void initBoard(char board[ROWS][COLS],int rows,int cols ,char set)  //参数需要传一个数组,以及这个数组的长度——数组的长度不要用宏,还有要放置的元素。
{//使用for循环的嵌套来实现初始化int i = 0;for (i=0;i<rows;i++){int j = 0;for (j = 0;j<cols;j++){board[i][j] = set;}}
}//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{//依旧是使用两个for循环来打印数组int i = 0;//打印棋盘框架printf("----------扫雷游戏----------\n");for (i=0;i<=col;i++){printf("%d ",i);		//打印棋盘的横坐标}printf("\n");for (i = 1; i <= row; i++){printf("%d ",i);   //打印棋盘的纵坐标int j = 0;for (j = 1; j <= col; j++){printf("%c ",board[i][j]);}printf("\n");}
}//接下来的操作是在逻辑数组上完成的
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{//布置10个雷//生成随机的坐标,布置雷int count = EASY_COUNT;while (count){//制定x y的随机值int x = rand() % row + 1;int y = rand() % col + 1;//如果这个地方是'0',就放设置一个雷,并用count计数if (board[x][y] == '0'){board[x][y] = '1';count--;}}}//计算周围有几颗雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{//计算所选坐标的周围8个坐标有几个 '1'——雷return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
}//排查雷——————游戏的主体
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{//坐标为x , y 形式int win = 0;while (win < row * col - EASY_COUNT){int x = 0;int y = 0;printf("请输入要排查的坐标:");scanf("%d %d", &x, &y);int c;										  // 防止非法格式输入坐标while ((c = getchar()) != '\n' && c != EOF);  // 因为在缓冲区中,非法格式的坐标不能被读取出来,数据就一直在缓冲区中。// 每一次循环的scanf,都会直接去缓冲区拿数据,每次都是非法字符,就会造成死循环。if (x >= 1 && x <= row && y >= 1 && y <= col)	//因为我们的棋盘实际上是11*11的,但是玩家所看到的9*9的,所以我们需要让坐标在合法范围内{//开始判定是否是雷if (mine[x][y] == '1'){printf("很遗憾,你被炸死了\n");DisplayBoard(mine, ROW, COL);break;}else{//该位置不是雷,就统计这个坐标周围有几个雷int count = GetMineCount(mine, x, y);   //统计周围有几个雷show[x][y] = count + '0';	            //将信息同步到玩家的棋盘上DisplayBoard(show, ROW, COL);win++;                                  //统计扫了几个点位了 }}else{printf("坐标非法,重新输入\n");}}if (win == row * col -EASY_COUNT)//row*col为棋盘中所有的点位,EASY_COUNT为棋盘中所有的炸弹数,当win==两位相减就证明已经排除了所有的雷{printf("恭喜你,排雷成功\n");DisplayBoard(mine, ROW, COL);   //打印棋盘}}

game.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//文件中写游戏需要的数据类型和函数声明等#define EASY_COUNT 10#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2//各种函数的声明——声明是要加;//初始化棋盘函数
void initBoard(char board[ROWS][COLS], int rows, int cols, char set);//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);//接下来的操作是在逻辑数组上完成的
//然后是布置雷
void SetMine(char board[ROWS][COLS],int row,int col);//最后是排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"//文件中写游戏的测试逻辑
//各种函数的使用//先写一个菜单的函数
void menu()
{printf("**************************\n");printf("********  1.扫雷  ********\n");printf("********  0.exit  ********\n");			//用0是因为也可以将选择的0作为假条件跳出循环printf("**************************\n");
}//再写一个扫雷的函数——使用扫雷要用到的函数
void game()
{//首先要创造两个数组,一个是逻辑数组——用来存放雷,和判定周围一圈有几个雷.一个是展示数组——用来向玩家展示扫雷棋盘的情况//首先第一步初始化棋盘//1.mine的数组最开始是全 '0 '//2.show的数组最开始全是 '* 'char mine[ROWS][COLS];//存放布置好的雷char show[ROWS][COLS];//存放排查出的雷的信息//写一个函数来初始化棋盘initBoard(mine,ROWS,COLS,'0');initBoard(show,ROWS,COLS,'*');//其次打印棋盘DisplayBoard(show, ROW, COL);//这里向玩家展示是9x9的棋盘,所以用ROW和COL//接下来的操作是在逻辑数组上完成的//然后是布置雷SetMine(mine, ROW, COL);//最后是排查雷FindMine(mine,show, ROW, COL);
}int main()
{//做一个选择菜单界面——使用do while循环和switch语句——做到菜单可以多次供你选择。int input = 0;srand((unsigned int)time(NULL));  //初始化随机数do{//先将菜单显示出来供你选择menu();printf("请选择:");scanf("%d",&input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default: printf("选择错误,重新选择\n");break;}} while(input);return 0;
}

到这里扫雷游戏的讲解就全部结束了,祝大家玩得愉快~

http://www.lryc.cn/news/2414871.html

相关文章:

  • 【Android 系统开发】CyanogenMod 13.0 源码下载 编译 ROM 制作 ( 手机平台 : 小米4 | 编译平台 : Ubuntu 14.04 LTS 虚拟机)
  • 哪些平台可以免费发布信息?热门三大免费信息发布网站
  • 统一身份认证(SSO/AD域/LDAP)
  • Chrome 使用的37个技巧
  • 2022小米红米手机最新最全MIUI刷机教程内测版到稳定版 不清除数据(线刷、卡刷)
  • flash as3 类型转换
  • oracle hint用法汇总
  • [ANT]apache ant 安装说明
  • Java 与 Python 数据交互
  • 统信桌面操作系统【打印机网络共享】方法
  • 免费下载中国知网、万方学术论文的几种方法(福利合集)
  • Image2Lcd图片取模软件
  • Android - 从零开始的AWS Lambda,kotlin实战
  • Python:火山小视频-无水印视频-多线程-批量采集实现和完整代码
  • 详解Asp.Net Sql数据库连接字符串
  • 看上去很美--次世代游戏平台XBOX360测评
  • cs_havana.wad_通过WAD和Docker热部署Java Enterprise
  • ]许多代码段,没准儿有你需要的 C++ Builder
  • QT5中文乱码解决 2021-06-26
  • system.exe病毒探秘
  • 对scope的隐藏面板打开
  • MPI集合操作reduce性能测试
  • 【C语言】链表太难学不懂?看这一篇就够了
  • 数字电子技术设计--彩灯控制电路的设计与制作
  • HarmonyOS分布式应用开发实战:ArkTS全场景解决方案
  • SVN 使用文档
  • 安装adbyby
  • Windows下bat脚本判断端口是否可用
  • 英文版权声明_【好书推荐】英文原版绘本 凯奖作家Steve Jenkins作品 Animal Dads 平装...
  • VMware Workstation安装win10操作系统