基于C语言 --- 自己写一个扫雷小游戏

C语言程序设计笔记---020

  • 初阶扫雷小游戏(开源)
    • 1、arr_main2.c程序大纲
    • 2、arr_game2.h
    • 3、arr_game2.c
      • 3.1、 自定义初化函数 InitBoard( ) 和 自定义显示函数 DisPlayBoard( )
      • 3.2、 自定义布置雷函数 SetMine( )
      • 3.4、 自定义排查雷函数 FindMine( )
    • 4、结束语

初阶扫雷小游戏(开源)

前言:
游戏规则:
(1)、在打印的数组棋盘里,输入坐标,排雷。
(2)、坐标格式:x(空格) y
(3)、x横坐标,y竖坐标
(4)、当布置的雷,被排除完,则判定胜利
如图所示
在这里插入图片描述

采用模块化编写
arr_game2.c执行主要逻辑程序
arr_game2.h存放头文件或函数声明等程序
arr_main2.c放主函数逻辑程序

1、arr_main2.c程序大纲

首先,从以往玩游戏的经验来谈,我们需要为游戏写一个游戏开始菜单,由玩家选择是否开始游戏。
这里可以借助所学的menu( )自定义函数,设计一个简易的菜单

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

当我们选择1,则开始游戏;当选择0,则退出游戏。
那么就得思考,如何对玩家得选择进行判定
1.利用 scanf( ) 函数获取输入值,将获取的值,借用 do while 循环语句和switch( )选择语句,进行下一步。
2.当选择1,开始游戏则执行game()自定义,游戏主逻辑执行程序
3.当选择0,switch中 进入case 0 : 的入口,执行退出游戏,并且 do while( ),判定为0,则退出程序
4.当玩家误选择非法数值,则default : 提示玩家输入错误

int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("\n*********** 三子棋游戏开始 *************\n\n");
			game();//游戏执行逻辑函数
			break;
		case 0:
			printf("\n退出游戏\n");
			break;
		default:
			printf("\n选择错误请重新选择\n\n");
			break;
		}

	} while (input);

接下来,主要阐述game( )函数内容,游戏的执行逻辑
1.当玩家选择1,开始游戏后,会显示整个以号展现的99棋盘,并且显示出行号与列号,方便玩家输入坐标。
如何实现9*9棋盘的显示呢?
(1)、因为棋盘是一个平面,平面由一条条线组成,线又由一个个点组成,利用所学的数组知识可以联想到,棋盘不过是由一个个字符拼接而成。
(2)、所以首先得定义和初识化两个大小相等,且类型相同的数组(mine和show),mine[ ][ ]数组负责布置雷和排查雷,show[ ][ ]负责将玩家在mine[ ][ ]数组所排查的雷显示出来,即显示排查情况。
(3)、自定义初识化函数 InitBoard( ) ,自定义显示棋盘函数 DisPlayBoard( ) ;
(4)、函数的参数,可想而知,需要数组名(board) — 首元素的地址指定需操作的数组,需要行(ROW)和列(COL)指定操作的元素或坐标或地址;

void game()
{
	//定义两个大小相等,类型相同的数组棋盘
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初识化棋盘
	char set = 0;
	InitBoard(mine, ROWS, COLS, '0');//初识化布置雷棋盘
	InitBoard(show, ROWS, COLS, '*');//初识化排查雷棋盘
	//显示棋盘
	//DisPlayBoard(mine,ROW,COL);//注意:显示只需显示玩家看的9*9棋盘,参数用 ROW 和 COL
	DisPlayBoard(show, ROW, COL);

2.当我们完成棋盘的初识化和显示后,我们需要将雷布置进去,当雷布置好后,玩家才可以排雷;
所以就得写一个自定义布置雷和自定义排查雷的函数,直到玩家踩雷或排查完雷结束。
(1)、自定义布置雷函数 SetMine( ) ;
(2)、自定义排查雷函数 FindMine( ) ;
3.如何判断游戏结束?
那么很容易思考到,玩家可不断的进行排雷,就是反复的调用玩家排雷函数和显示函数,同时不停的判断每一次排雷是否踩中地雷或排完地雷。所以需要一个循环来实现,这里就用while循环,实现不停的下棋。
但是,不停的下棋,始终在死循环,那么就思考利用,break跳出循环,那么跳出的while函数的条件是什么呢?我们可以想到,如99的棋盘,不断的排雷,棋盘可存放的总数一共才99个数,还放了10个雷,那么我们只需要判断,我们排雷的次数,是否等于总数减去我们放的雷,当相等时说明,雷被排完了,游戏结束。

void game()
{
	//定义两个大小相等,类型相同的数组棋盘
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初识化棋盘
	char set = 0;
	InitBoard(mine, ROWS, COLS, '0');//初识化布置雷棋盘
	InitBoard(show, ROWS, COLS, '*');//初识化排查雷棋盘
	//显示棋盘
	//DisPlayBoard(mine,ROW,COL);//注意:显示只需显示玩家看的9*9棋盘,参数用 ROW 和 COL
	DisPlayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine ,ROW,COL);//雷只需要布置在9*9棋盘
	//排查雷
	FindMine(mine,show, ROW, COL);//首先,需要mine数组中排查,布置的雷,然后将排查出的雷,放置在show数组中显示出来,且雷的布置和显示棋盘均在9*9棋盘

arr_main.c程序大纲展示

#include "arr_game2.h"

//显示游戏菜单
void menu()
{
	printf("*****************************************\n");
	printf("************* 1.play game ***************\n");
	printf("************* 0.game over ***************\n");
	printf("*****************************************\n");
}

void game()
{
	//定义两个大小相等,类型相同的数组棋盘
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初识化棋盘
	char set = 0;
	InitBoard(mine, ROWS, COLS, '0');//初识化布置雷棋盘
	InitBoard(show, ROWS, COLS, '*');//初识化排查雷棋盘
	//显示棋盘
	//DisPlayBoard(mine,ROW,COL);//注意:显示只需显示玩家看的9*9棋盘,参数用 ROW 和 COL
	DisPlayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine ,ROW,COL);//雷只需要布置在9*9棋盘
	//排查雷
	FindMine(mine,show, ROW, COL);//首先,需要mine数组中排查,布置的雷,然后将排查出的雷,放置在show数组中显示出来,且雷的布置和显示棋盘均在9*9棋盘

}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();//显示游戏菜单
		printf("请输入;>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			printf("\n************ 扫雷游戏开始 **************\n\n");
			game();//游戏主逻辑函数
			break;
		case 0:
			printf("\n退出游戏\n\n");
			break;
		default:
			printf("\n输入错误,请重新输入\n\n");
			break;
		}
	} while (input);
	return 0;
}

2、arr_game2.h

用于存放所自定义的函数和头文件等声明的程序
通俗易懂,就不多赘述,详见代码注释的说明。

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//定义显示的棋盘show大小
#define ROW 9
#define COL 9

//定义外围的棋盘mine大小
#define ROWS ROW+2
#define COLS COL+2

//宏定义雷的个数
#define ESAY_MINE 10

//初识化棋盘
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 mine[ROWS][COLS], int row, int col);

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);

3、arr_game2.c

用于存放对 arr_main.c 程序大纲做提到的函数进行封装,实现具体的功能的程序
说明:基于arr_main2.c 程序大纲逻辑对代码进行讲解
注意:这里均以99的棋盘为例哦,但是我们需要考虑到边界的元素,当我们输入的坐标在边缘时,如何计算周围的雷的数量呢?
所以我们在arr_game2.h中,宏定义的数组大小可以知道,通过引用比9
9数组大一圈的数组覆盖9*9的棋盘即可。所以我们将引用两个大小相等,类型相同的数组。
为了方便理解,简单画个图:
在这里插入图片描述

3.1、 自定义初化函数 InitBoard( ) 和 自定义显示函数 DisPlayBoard( )

首先,根据需求我们需要一个棋盘才可以正常的扫雷.
如何让棋盘初识化和显示呢?
1.根据所学的二维数组知识,便可以知道,当我们遍历二维数组的每一个元素,使得填充为需要的字符,便可以由InitBoard( ) 达到效果。然后我们将mine数组放置字符 ’ 0 ',将show数组放置字符 ’ * ’ 。
补充:我们以mine数组填充字符0表示不是雷,以字符1表示为雷。以show字符填充字符 ✳号隐藏雷的位置,由玩家输入坐标进行逐步排雷。
注意因为我们使用的是两个大小相等,类型相同的数组,所以增加一个字符型变量,以传参为我们需要的字符

//初识化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)//下标0~10
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] =  set;//由set参数决定显示的棋盘
		}
	}
}

//显示棋盘
void DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("\n************** 扫雷 *************\n");
	//打印行号、列号,方便输入坐标
	for (i = 0; i <= col; i++)//打印行号
	{
		printf("%d ",i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ",i);//打印列号
		//打印数组元素
		for (j = 1; j <= col; j++)
		{
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
	printf("\n************** 扫雷 *************\n\n");
}

如图所示
在这里插入图片描述

3.2、 自定义布置雷函数 SetMine( )

我们定义坐标变量,并使用随机值rand函数约束坐标范围,将雷的坐标以while循环的方式存放进mine数组中,当布置一个雷,雷则少一个,所以直接以雷的数量作为循环判断条件即可。

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = ESAY_MINE;
	while (count)//由雷的数量,布置一个少一个,作为判定的条件
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')//是否被占用
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

如图所示
在这里插入图片描述

3.4、 自定义排查雷函数 FindMine( )

那么接下来,我们如何进行排查雷呢?
首先,我们由玩家循环输入将排查的坐标(x,y),然后判断输入的坐标是否超出9*9扫雷棋盘的范围,若超出,可打印提示语句,重新输入。当符合范围,就判断是否踩中了雷,若踩中雷,break跳出循环,游戏结束。当玩家没有踩中雷,则显示输入坐标处(x,y),周围雷的数量情况。
所以思考怎么计算坐标处周围雷的数量呢?
当然不难想到,当我们知道了一个数组的元素坐标时,可通过简单的加减运算,即可得知周围坐标。
如图所示
在这里插入图片描述
当我们知道了输入坐标周围的元素坐标,那么就可以通过该对应的坐标提取该元素的值,为字符0还是为字符1,联系前文中提到的0/1(非雷/雷),就可知我们mine数组填充0/1来作为是否为雷的巧妙之处了,因为我们计算周围的雷的数量只需要将该对应的元素值加起来就得到了数量情况了。
但是值得注意的是:我们自始自终mine和show数组都是字符型数组,所以在进行加减运算时,要区分字符与十进制数值的关系。

众所周知:
字符 ‘0’ - ‘0’ = 0
‘5’ - ‘0’ = 5
‘9’ - ‘0’ = 9
所以我们将周围元素加一起,再减去8* ‘0’即可。

//返回雷的数量
int  GetMine(char mine[ROWS][COLS],int x,int y)
{
	return (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] + mine[x-1][y]) - 8 * '0';
}

最后通过GetMine( )获取的雷的数量,ret再将它加上一个字符‘ 0 ’,赋值给shou数组,然后作为DisPlayBoard( )的参数,打印出来即可。

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	//如何判断输赢?
	while (1)
	{
		printf("请输入排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <=  col)
		{
			if (mine[x][y] == '1')
			{
				printf("\n很遗憾,您踩中雷了\n\n");
				DisPlayBoard(mine,ROW,COL);
				break;
			}
			else//没猜中雷,返回雷的数量
			{
				int ret = GetMine(mine, x, y);//返回雷的数量
				show[x][y] = ret + '0';//放置再在show数组显示
				DisPlayBoard(show, ROW, COL);
			}
		}
		else
		{
			printf("\n输入非法,请重新输入\n");
		}
	}
	if (win == row * col - ESAY_MINE)
	{
		printf("恭喜,您已经成功排完雷\n");
		DisPlayBoard(mine, ROW, COL);

	}
}

但是我们调试发现:
当玩家没有踩中雷时,一直进行排雷,那么如何判断输赢游戏结束呢?
在上文arr_main2.c大纲中提到,当棋盘所有的雷被排查完游戏结束。
即理解为:当棋盘中可以输入排查的坐标位置数量win,当win 小于了row*col(棋盘可下棋坐标棋子的总数量) 减去 地雷数量就跳出循环结束游戏,判定胜利
所以优化代码:

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	//如何判断输赢?
	//当棋盘所有的雷被排查完
	//即理解为:当棋盘的可排查的棋子坐标位置,win 小于了row*col(棋盘可下棋坐标棋子的总数量) - 地雷数量就跳出循环结束游戏,判定胜利
	while (win <row*col - ESAY_MINE)
	{
		printf("请输入排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <=  col)
		{
			if (mine[x][y] == '1')
			{
				printf("\n很遗憾,您踩中雷了\n\n");
				DisPlayBoard(mine,ROW,COL);
				break;
			}
			else//没猜中雷,返回雷的数量
			{
				int ret = GetMine(mine, x, y);//返回雷的数量
				show[x][y] = ret + '0';//放置再在show数组显示
				DisPlayBoard(show, ROW, COL);
			}
		}
		else
		{
			printf("\n输入非法,请重新输入\n");
		}
	}
	if (win == row * col - ESAY_MINE)
	{
		printf("恭喜,您已经成功排完雷\n");
		DisPlayBoard(mine, ROW, COL);

	}
}

最后我们将arr_game2.h中宏定义的雷(ESAY_MINE)的数量改为80,来证明while跳出的条件是正确的。
如图所示
在这里插入图片描述

4、结束语

相信通过这样一个扫雷的小游戏,更具掌握了对数组的操作以及对自定义函数的深刻认识;
如果觉着文章对您有所帮助,请不要吝啬的一赞三连哦,谢谢阅读,不足之处还请多多指教。

扫雷源码获取链接: 扫雷小游戏初阶

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】进程通信 — 共享内存

文章目录 &#x1f4d6; 前言1. 共享内存2. 创建共享内存2.1 ftok()创建key值&#xff1a;2.2 shmget()创建共享内存&#xff1a;2.3 ipcs指令&#xff1a;2.4 shmctl()接口&#xff1a;2.5 shmat()/shmdt()接口:2.6 共享内存没有访问控制&#xff1a;2.7 通过管道对共享内存进…

【计算机网络】网络层协议 -- IP协议

文章目录 1. 网络层做了什么事2. IP协议的简介3. IP协议格式4. 分片与组装5. 网段划分6. 特殊的IP地址7. IP地址的数量限制8. 私网IP地址和公网IP地址9. 路由 1. 网络层做了什么事 保证数据可靠地从一台主机到另一台主机 当双方在进行基于TCP的网络通信时&#xff0c;要保证将数…

maven本地仓库地址修改+maven国内镜像设置+maven运行所需pos.xml文件配置基本写法

1&#xff0c;maven本地仓库地址修改 maven在使用过程中&#xff0c;本地项目仓库其空间占用会越来越大&#xff0c;但是其默认仓库位置往往是以C盘为主&#xff0c;C盘作为系统盘常常会遇到所在盘空间占满的情况&#xff0c;所以我们将其改至其他硬盘空间位置为适合做法&#…

Kotlin泛型的协变与逆变

以下内容摘自郭霖《第一行代码》第三版 泛型的协变 一个泛型类或者泛型接口中的方法&#xff0c;它的参数列表是接收数据的地方&#xff0c;因此可以称它为in位置&#xff0c;而它的返回值是输出数据的地方&#xff0c;因此可以称它为out位置。 先定义三个类&#xff1a; op…

ansible安装lnmp(集中式)

文章目录 一、安装nginx二、安装mysql三、安装php测试&#xff1a; 一、安装nginx - name: the nginx playhosts: webserversremote_user: roottasks:- name: stop firewalld #关闭防火墙service: namefirewalld statestopped enabledno- name: selinux stopc…

Verilog语法学习——边沿检测

边沿检测 代码 module edge_detection_p(input sys_clk,input sys_rst_n,input signal_in,output edge_detected );//存储上一个时钟周期的输入信号reg signal_in_prev;always (posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)signal_in_prev < 0;else…

软工导论知识框架(三)结构化的设计

一.传统软件工程方法学采用结构化设计技术&#xff08;SD&#xff09; 从工程管理角度结构化设计分两步&#xff1a; 概要设计&#xff1a; 将软件需求转化为数据结构和软件系统结构。详细设计&#xff1a;过程设计&#xff0c;通过对结构细化&#xff0c;得到软件详细数据结构…

Python 进阶(三):正则表达式(re 模块)

❤️ 博客主页:水滴技术 🌸 订阅专栏:Python 入门核心技术 🚀 支持水滴:点赞👍 + 收藏⭐ + 留言💬 文章目录 1. 导入re模块2. re模块中的常用函数2.1 re.search()2.2 re.findall()2.3 re.sub()2.4 re.compile()2.5 re.split()3. 正则表达式的语法4. 匹配对象的属性和

重学C++系列之异常

一、什么是异常 异常一般是指程序运行期发生的非正常情况。异常一般是不可预测的&#xff0c;如&#xff1a;内存不足&#xff0c;打开文件失败&#xff0c;数组越界&#xff0c;范围溢出等。 在某段程序发生无法继续正常执行的情况时&#xff0c;C允许程序进行所谓抛出异常&am…

3ds max 烘培世界坐标到贴图/顶点色

设置Diffuse 为ObjectNormal Normalize(objectNormal) * 0.5 0.5 把Diffuse烘培到顶点色 烘培Diffuse到贴图 模型按UV展开 右键复制 &#xff0c; 到mesh上粘贴 烘培到贴图 UE使用 贴图导入为BC7 float3 n ObjectNormal*2-1; return float3(n.x,n.z,n.y); // x ,z ,y

selenium官网文档阅读总结(day 2)

1.selenium元素定位方法 1.1selenium命令 当我们使用chormdriver打开网页后&#xff0c;接下来就要用python操作元素&#xff0c;模拟用户会作出的操作&#xff0c;这些操作元素的方法就是命令。比如 (1) click&#xff1a;点击&#xff08;按钮&#xff0c;单选框&#xff…

C++多线程编程(第三章 案例1,使用互斥锁+ list模拟线程通信)

主线程和子线程进行list通信&#xff0c;要用到互斥锁&#xff0c;避免同时操作 1、封装线程基类XThread控制线程启动和停止&#xff1b; 2、模拟消息服务器线程&#xff0c;接收字符串消息&#xff0c;并模拟处理&#xff1b; 3、通过Unique_lock和mutex互斥方位list 消息队列…

前端需要知道的计算机网络知识

1 Web 机制 无论通过有线方式 (通常是网线) 还是无线方式&#xff08;比如 wifi 或蓝牙)&#xff0c;通信需要进行连接&#xff0c;网络上的每台计算机需要链接到路由器&#xff08;router&#xff09;。 路由器确保从一台计算机上发出的一条信息可以到达正确的计算机。计算机…

TensorFlow项目练手(三)——基于GRU股票走势预测任务

项目介绍 项目基于GRU算法通过20天的股票序列来预测第21天的数据&#xff0c;有些项目也可以用LSTM算法&#xff0c;两者主要差别如下&#xff1a; LSTM算法&#xff1a;目前使用最多的时间序列算法&#xff0c;是一种特殊的RNN&#xff08;循环神经网络&#xff09;&#xf…

spring boot合并 http请求(前后端实现)

为什么要合并http请求 页面加载时要初始化很多资源信息&#xff0c;会发送好多http请求&#xff0c;根据http的通信握手特点&#xff0c;多个http请求比较浪费资源。进而如果能将多个http请求合并为一个发送给服务器&#xff0c;由服务器获取对应的资源返回给客户端 奇思妙解 …

Hadoop学习日记-YARN组件

YARN(Yet Another Resource Negotiator)作为一种新的Hadoop资源管理器&#xff0c;是另一种资源协调者。 YARN是一个通用的资源管理系统和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度 YARN架构图 YARN3大组件&#xff1a; &#xff08;物理层面&#xff09…

C语言每日一题:12《数据结构》相交链表。

题目&#xff1a; 题目链接 思路一&#xff1a; 1.如果最后一个节点相同说明一定有交点。 2.使用两个循环获取一下长度&#xff0c;同时可以获取到尾节点。 3。注意初始化lenA和lenB为1&#xff0c;判断下一个节点是空是可以保留尾节点的。长度会少一个&#xff0c;尾节点没有…

小白到运维工程师自学之路 第六十集 (docker的概述与安装)

一、概述 1、客户&#xff08;老板&#xff09;-产品-开发-测试-运维项目周期不断延后&#xff0c;项目质量差。 随着云计算和DevOps生态圈的蓬勃发展&#xff0c;产生了大量优秀的系统和软件。软件开发人员可以自由选择各种软件应用环境。但同时带来的问题就是需要维护一个非…

【HarmonyOS】ArkTS 组件内转场动画,动画播放时颜色异常问题

【关键字】 HarmonyOS、ArkTS、组件内转场动画、颜色异常 【问题描述】 根据组件内转场动画文档中示例编写代码&#xff0c;使用动画转场组件button&#xff0c;并给button设置背景色让button透明度为0&#xff0c;实现动画转场时&#xff0c;会先出现默认蓝色button&#xf…

Ai创作系统ChatGPT源码搭建教程+附源码

系统使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到本系统&#xff01; 更新内容&#xff1a; 同步官方图片重新生成指令 同步官方 Vary 指令 单张图片对比加强 Vary(Strong) | Vary(Subtle) 同步官方 Zoom 指令 单张图片无限缩放 Zoom out 2x | Zoom out 1.5x 新增GP…