【c语言版】带你领悟扫雷游戏


铁汁们,今天给大家分享一篇扫雷游戏的实现,来吧,开造⛳️

1.需要存储雷的信息,创建二维数组来存储数据信息,把为雷设置为1,不是雷设置为0。
2.在某个位置存储其所围成的正方形区域中雷的个数,若个数为1,则在查看棋盘的时候,会与雷的位置1产生歧义,所以创建两个二维数组,棋盘1(show二维数组)用来排查雷(玩家可以看到的棋盘RowCol)、棋盘2(meni二维数组 )用来设置雷的位置((Row+2)乘以(Col+2))。
3.在玩家可以看到的棋盘中,把全部数据初始化为 ‘
’,为了方便进行二个棋盘的相互利用,从而无需调用两个函数分别对两棋盘单独处理,把设置雷的二维数组存储的数据类型改成字符型。

设置雷

思路:
遍历棋盘1(show二维数组),并与棋盘2(meni二维数组 )搭配使用判断是否已经在该位置设置了雷

1.让电脑在合法的区域内(Row*Col)产生随机坐标;

2.设置雷的限制条件:如果该坐标在棋盘1内数据为 ’0‘时,才可以在此位置设置一个雷(原因:若放置前一个雷的坐标与本次放置雷的坐标相同,如果无限制条件,则在此位置处本次设置雷就覆盖了前一次设置的雷,因为设置雷的个数是确定的,会造成在棋盘中设置雷的个数不满足规定雷的个数)。

void SetMeni(char board[Rows][Cols], int row, int col)  //设置棋盘中雷的位置,雷设置的范围在Row*Col棋盘内
{
	int x = 0;  //将是雷的地方设置成1,不是雷的地方设置为0作用:在获得一个位置周边(所围成的正方形区域)雷的个数,可以直接进行计算,直接返回结果,不需要每次判断某个位置是否雷
	int y = 0;
	int count = Count; //已经定义规定好要设置的雷的个数
	while (count)
	{ //产生随机数的所有库函数:rand函数--调用-->srand函数--调用-->time函数
		x = rand() % row + 1;//电脑随机产生要设置雷的坐标
		y = rand() % col + 1; 
		if (board[x][y] == '0') //防止下一次设置雷的坐标与上次设置雷的坐标相同,重复设置,从而达不到规定的要设置的雷的个数
		{
			board[x][y] = '1';   
			--count;  //每设置完一个雷,规定数目的雷减一
		}
	}
}

将是雷的地方设置成1,不是雷的地方设置为0作用:在获得一个位置周边(所围成的正方形区域)雷的个数,可以直接进行计算,直接返回结果,不需要每次判断某个位置是否雷,效率提高。

产生随机数的所有库函数:rand函数–调用–>srand函数–调用–>time函数,rand函数、srand函数均需要调用头文件#include<stdlib.h>,time函数需要调用头文件#include<time.h>。

在这里插入图片描述
在这里插入图片描述

排查雷

思路:
排查的条件:循环变量小于(RowCol-Count)棋盘1中数据减去规定雷的个数,排查的坐标需要是合法的坐标范围内(在RowCol);
若该坐标在棋盘2(meni数组)对应的位置为雷,则跳出循环,反之,则进行扩展区域函数。

void FindMeni(char meni[Rows][Cols], char show[Rows][Cols], int row, int col)  //排查雷
{
	int x = 0;
	int y = 0;
	while (i<row*col-Count)
	{
		printf("请输入要排查的坐标,中间以空格隔开>:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && y >= 1 && x <= row && y <= col)
		{
			if (meni[x][y] == '1')
			{
				printf("你被炸死了\n");
				printboard(meni, Row, Col);
				break;
			}
			else
			{
				Extendboard(meni, show, row, col,x,y);  //扩展区域
				printboard(show, Row, Col);  //打印扩展后Row*Col棋盘,用于检查是否扩展成功
				int input = 0;
				printf("请选择是否要标记雷,若要标记雷,请输入1>:");
				scanf("%d", &input);
				switch (input)
				{
				case 1:
					MarkMeni(show, row, col);
					break;
				default:
					break;
				}
            }
		}
		else
		{
			printf("需要排查的坐标不合法,请重新输入\n");
		}
	}
	if (i == row * col - Count)  //当所有不是雷的元素都排查完,才是排雷成功
	{
		printf("恭喜你,排雷成功\n");
		printboard(meni, Row, Col);
	}
}

在这里插入图片描述

标记雷

思路:
前提条件:标记雷的坐标要在合法的范围内(Row*Col),在棋盘1(show数组)进行标记,且要标记的位置原来存储的数据为 ’ * ',才可以进行标记(在棋盘上存储了空格和数字的位置已经被判断出不是雷)。

void MarkMeni(char show[Rows][Cols], int row, int col) //标记雷可能存在的地方
{ 
	printf("标记雷\n");
	int x = 0;   //存储我们输入的雷可能存在的坐标
	int y = 0;
	while (1)
	{
		printf("请输入要标记雷的坐标,中间以空格隔开");
		scanf("%d %d", &x, &y);
		if (x >= 1 && y >= 1 && x <= row && y <= col)  //标记雷的位置要在Row*Col棋盘上
		{
			if (show[x][y] == '*') //某位置可标记的条件
			{
				show[x][y] ='?';
				break;  
			}
			else
			{
				printf("该位置不能被标记,请重新输入\n");
			}
		}
		else
		{
			printf("需要标记雷的坐标不合法,请重新输入\n");
		}
	}
	printboard(show, Row, Col); //打印执行标记后Row*Col棋盘,可以检查该位置是否被标记
}

在这里插入图片描述

打印棋盘

思路:遍历棋盘1(Row*Col)

打印坐标的行、列数:对于棋盘2(meni二维数组),棋盘1(show二维数组)在棋盘2的位置行、列均是从1开始;

打印存放在棋盘1中的数据,供玩家观看。

void printboard(char board[Rows][Cols], int row, int col) //打印棋盘
{
	printf("------扫雷游戏------\n");
	int i = 0;
	for(i = 0 ; i <= row ; i++)  //设置列号,并打印
	printf("%d ", i); 
	printf("\n");
	i = 0;
	for (i = 1; i <= row; i++) //打印的是Row*Col棋盘
	{
		printf("%d ", i);   //设置行号,并打印
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);  //打印棋盘上的数据
		}
		printf("\n"); //每一行打印完都需要换行,不然下一行的数据会衔接
	}
}

在这里插入图片描述

初始化棋盘

思路:
遍历棋盘2(meni二维数组),赋初始值,打印Row*Col区域。
在这里插入图片描述

获得雷的个数

数字字符减去0字符等于其对应的数字
思路:
方法1:采用双层for循环来遍历棋盘2(meni二维数组)

从以某位置为中心围成的正方形区域(3*3)第一行开始遍历,直到遍历完第三行,依次将存放在棋盘2中对应位置的数字字符数据转化为对应的数字,在分别相加。


int GetMenicount(char meni[Rows][Cols], int x, int y)  //获得雷的个数,前提条件,该位置已经证明了不是雷,存放的是‘0’
{
	int sum = 0;  //存放获得一个位置周边(所围成的正方形区域)雷的个数
	int i = 0;  
	for (i = x - 1; i <= x + 1; i++)  //遍历该位置所围成的正方形区域
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)  
		{
			sum += meni[i][j]-'0';  //数字字符减去字符0得到的是数字
		}
	}
	return sum;
}

在这里插入图片描述

方法2:

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';

在这里插入图片描述

扩展区域

思路:
采用递归实现,该位置处不是雷、该位置周边(所围成的正方形区域)不是雷 ,且不能在扩展已经设为空格的位置,
否则会造成死递归,从而造成栈溢出采用递归实现,
扩展的坐标应在合法的区域(Row*Col),不然会造成数组越界;

采用双层for循环来遍历棋盘2(meni二维数组),从以某位置为中心围成的正方形区域(33)第一行开始遍历,
判断该坐标所在棋盘1(show数组)所对应的数值是否为 '
’ 或者为 ‘?’(‘?’只是我们手动标记雷可能存在的地方,可能也满足该函数的前提条件,若满足,则需要扩展),直到遍历完第三行,

void Extendboard(char meni[Rows][Cols], char show[Rows][Cols], int row, int col,int x,int y)  //扩展区域,进入到该函数就说明该位置处不是雷,从此位置向外扩展
{  //采用递归实现,该位置处不是雷、该位置周边(所围成的正方形区域)不是雷 ,且不能在扩展已经设为空格的位置,否则会造成死递归,从而造成栈溢出采用递归实现
	int count = 0;  //存放获得一个位置周边(所围成的正方形区域)雷的个数
	if (x >= 1 && y >= 1 && x <= row && y <= col)  //扩展的坐标应在Row*Col棋盘
	{
		count=GetMenicount(meni, x, y);  
		if (count==0)  //该位置可以扩展
		{
			show[x][y] = ' ';  
			i++;  //记录不是雷的元素个数
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*'||show[i][j]=='?') //注意‘?’只是我们手动标记雷可能存在的地方,可能也满足该函数的前提条件,若满足,则需要扩展
					{
						Extendboard(meni, show, row, col, i,j);
					}![在这里插入图片描述](https://img-blog.csdnimg.cn/c1cfaa1860174181a0827d6e9e62f3bc.png#pic_center)

				}
			}
		}
		else  //该位置周边(所围成的正方形区域)有雷,就把该位置周边(所围成的正方形区域)雷的个数转化为字符存储在该位
		{
			show[x][y] = count + '0';  // 数字+字符0等于数字字符
			i++;  //记录不是雷的元素个数
		}
	}	
}

在这里插入图片描述

test.c的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"meni.h"
void meue()//菜单栏
{
	printf("****************\n");
	printf("**** 1.play ****\n");
	printf("**** 0.exit ****\n");
	printf("****************\n");
}

void game()
{
	char meni[Rows][Cols] = { 0 }; //设置雷数组
	char show[Rows][Cols] = { 0 }; //排查雷数组
	Initboard(meni, Rows,Cols,'0'); //初始化设置雷的棋盘全为‘0’
	//printboard(meni, Row, Col);
	SetMeni(meni, Row, Col);  //设置棋盘中雷的位置
	//(meni, Row, Col); //打印设置雷的棋盘
	Initboard(show, Rows, Cols, '*');  //初始化排查雷的棋盘为‘*’
	printboard(show, Row, Col);  //打印排查雷的棋盘
	FindMeni(meni, show, Row, Col);  //排查雷
}

int main()
{
	 srand((unsigned int)time(NULL)); //生成随机数要调用的函数,srand的头文件为#include<stdlib.h>、time的头文件为#include<time.h>
	 int input = 0;
     do
	 {
		 meue();
		printf("请选择>:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出游戏\n");
			break;
		case 1:
			game();
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}

	 }while (input);
	 return 0;
}

meni.c的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"meni.h"
int i = 0;
void Initboard(char board[Rows][Cols], int rows, int cols, char set)//初始化棋盘,形参的名字和实参的名字可以相同也可不同
{
	int i = 0;    //初始化棋盘
	for (i = 0; i < rows; i++) //遍历Rows*Cols棋盘
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;  //将棋盘初始化为自己规定的格式
		}
	}
}

void printboard(char board[Rows][Cols], int row, int col) //打印棋盘
{
	printf("------扫雷游戏------\n");
	int i = 0;
	for(i = 0 ; i <= row ; i++)  //设置列号,并打印
	printf("%d ", i); 
	printf("\n");
	i = 0;
	for (i = 1; i <= row; i++) //打印的是Row*Col棋盘
	{
		printf("%d ", i);   //设置行号,并打印
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);  //打印棋盘上的数据
		}
		printf("\n"); //每一行打印完都需要换行,不然下一行的数据会衔接
	}
}

void SetMeni(char board[Rows][Cols], int row, int col)  //设置棋盘中雷的位置,雷设置的范围在Row*Col棋盘内
{
	int x = 0;  //将是雷的地方设置成1,不是雷的地方设置为0作用:在获得一个位置周边(所围成的正方形区域)雷的个数,可以直接进行计算,直接返回结果,不需要每次判断某个位置是否雷
	int y = 0;
	int count = Count; //已经定义规定好要设置的雷的个数
	while (count)
	{ //产生随机数的所有库函数:rand函数--调用-->srand函数--调用-->time函数
		x = rand() % row + 1;//电脑随机产生要设置雷的坐标
		y = rand() % col + 1; 
		if (board[x][y] == '0') //防止下一次设置雷的坐标与上次设置雷的坐标相同,重复设置,从而达不到规定的要设置的雷的个数
		{
			board[x][y] = '1';   
			--count;  //每设置完一个雷,规定数目的雷减一
		}
	}
}

int GetMenicount(char meni[Rows][Cols], int x, int y)  //获得雷的个数,前提条件,该位置已经证明了不是雷,存放的是‘0’
{
	int sum = 0;  //存放获得一个位置周边(所围成的正方形区域)雷的个数
	int i = 0;  
	for (i = x - 1; i <= x + 1; i++)  //遍历该位置所围成的正方形区域
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)  
		{
			sum += meni[i][j]-'0';  //数字字符减去字符0得到的是数字
		}
	}
	return sum;
}

void Extendboard(char meni[Rows][Cols], char show[Rows][Cols], int row, int col,int x,int y)  //扩展区域,进入到该函数就说明该位置处不是雷,从此位置向外扩展
{  //采用递归实现,该位置处不是雷、该位置周边(所围成的正方形区域)不是雷 ,且不能在扩展已经设为空格的位置,否则会造成死递归,从而造成栈溢出采用递归实现
	int count = 0;  //存放获得一个位置周边(所围成的正方形区域)雷的个数
	if (x >= 1 && y >= 1 && x <= row && y <= col)  //扩展的坐标应在Row*Col棋盘
	{
		count=GetMenicount(meni, x, y);  
		if (count==0)  //该位置可以扩展
		{
			show[x][y] = ' ';  
			i++;  //记录不是雷的元素个数
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*'||show[i][j]=='?') //注意‘?’只是我们手动标记雷可能存在的地方,可能也满足该函数的前提条件,若满足,则需要扩展
					{
						Extendboard(meni, show, row, col, i,j);
					}
				}
			}
		}
		else  //该位置周边(所围成的正方形区域)有雷,就把该位置周边(所围成的正方形区域)雷的个数转化为字符存储在该位
		{
			show[x][y] = count + '0';  // 数字+字符0等于数字字符
			i++;  //记录不是雷的元素个数
		}
	}	
}

void MarkMeni(char show[Rows][Cols], int row, int col) //标记雷可能存在的地方
{ 
	printf("标记雷\n");
	int x = 0;   //存储我们输入的雷可能存在的坐标
	int y = 0;
	while (1)
	{
		printf("请输入要标记雷的坐标,中间以空格隔开");
		scanf("%d %d", &x, &y);
		if (x >= 1 && y >= 1 && x <= row && y <= col)  //标记雷的位置要在Row*Col棋盘上
		{
			if (show[x][y] == '*') //某位置可标记的条件
			{
				show[x][y] ='?';
				break;  
			}
			else
			{
				printf("该位置不能被标记,请重新输入\n");
			}
		}
		else
		{
			printf("需要标记雷的坐标不合法,请重新输入\n");
		}
	}
	printboard(show, Row, Col); //打印执行标记后Row*Col棋盘,可以检查该位置是否被标记
}

void FindMeni(char meni[Rows][Cols], char show[Rows][Cols], int row, int col)  //排查雷
{
	int x = 0;
	int y = 0;
	while (i<row*col-Count)
	{
		printf("请输入要排查的坐标,中间以空格隔开>:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && y >= 1 && x <= row && y <= col)
		{
			if (meni[x][y] == '1')
			{
				printf("你被炸死了\n");
				printboard(meni, Row, Col);
				break;
			}
			else
			{
				Extendboard(meni, show, row, col,x,y);  //扩展区域
				printboard(show, Row, Col);  //打印扩展后Row*Col棋盘,用于检查是否扩展成功
				int input = 0;
				printf("请选择是否要标记雷,若要标记雷,请输入1>:");
				scanf("%d", &input);
				switch (input)
				{
				case 1:
					MarkMeni(show, row, col);
					break;
				default:
					break;
				}
            }
		}
		else
		{
			printf("需要排查的坐标不合法,请重新输入\n");
		}
	}
	if (i == row * col - Count)  //当所有不是雷的元素都排查完,才是排雷成功
	{
		printf("恭喜你,排雷成功\n");
		printboard(meni, Row, Col);
	}
}

meni.h的实现

#define _CRT_SECURE_NO_WARNINGS 1
#define Row 9 //控制排查雷的棋盘,用户可以直接看到的棋盘
#define Col 9 //#define 行、列的值,有利于实现多列、多行的棋盘
#define Rows Row+2  //为了记录该位置周边(所围成的正方形区域)雷的个数时,防止造成越界
#define Cols Col+2
#define Count 10 //规定雷的个数,常量不能被修改,不可直接++、--
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void Initboard(char board[Rows][Cols], int rows, int cols, char set); //初始化棋盘
void printboard(char board[Rows][Cols], int rows, int cols);  //打印棋盘
void SetMeni(char board[Rows][Cols], int rows, int cols);  //设置雷
void FindMeni(char meni[Rows][Cols],char show[Rows][Cols], int rows, int cols);  //排查雷
int GetMenicount(char meni[Rows][Cols], int rows, int cols);  //获得该位置周边(所围成的正方形区域)雷的个数
void Extendboard(char meni[Rows][Cols], char show[Rows][Cols], int rows, int cols,int x,int y);  //扩展区域
void MarkMeni( char show[Rows][Cols], int rows, int cols);  //标记雷
运行结果

在这里插入图片描述

铁铁们,扫雷游戏的实现就到此结束啦,请动动你们的手给作者点个👍鼓励吧,你们的鼓励就是我的动力✨

  • 50
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 35
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值