扫雷游戏(实现了网页版的扫雷游戏的所有逻辑)

扫雷游戏的逻辑

今天我们要实现的是扫雷游戏,实现9X9的棋盘,
在这里插入图片描述
但是值得注意的一点是,当排雷坐标在1 1 的时候该如何知道这个坐标旁边有几个雷呢?(当选择这个坐标时,看其周围8给坐标是否有雷——>(排雷))在这里插入图片描述
为了使在边缘的坐标也可以检测周围8个坐标是否有雷的时候,所以我们的棋盘需要是11X11
在这里插入图片描述

game.h

这里放的是我们的头文件

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


#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2
//这里定义的是我的10个雷
#define Easy_count 10
//初始化棋盘
void Initboard(char board[ROWS][COLS], int row, int col,char set);
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
//设置雷
void Setmine(char board[ROWS][COLS], int row, int col);
//排雷
void Changemine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

test.c

游戏菜单

void meum()
{
    
    
	printf("***************************\n");
	printf("*******    1.Play   *******\n");
	printf("*******    0.Exit   *******\n");
	printf("***************************\n");
}

游戏逻辑

void game()
{
    
    
	//mine是放雷的棋盘
	char mine[ROWS][COLS] = {
    
     0 };
	//show是展示给我们看的棋盘
	char show[ROWS][COLS] = {
    
     0 };
	//初始化扫雷棋盘
	Initboard(mine, ROWS, COLS,'0');//放炸弹的棋盘
	Initboard(show, ROWS, COLS,'*');//展示的棋盘

	//打印棋盘
	
	Displayboard(show, ROW, COL);

	//埋雷
	Setmine(mine, ROW, COL);
	Displayboard(mine, ROW, COL);

	//排雷
	Changemine(mine,show, ROW, COL);
	
}

主函数

int main()
{
    
    
	int input = 0;
	//生成随机数,等会在Setmine()随机放雷
	srand((unsigned int)time(NULL));
	do
	{
    
    
		meum();
		printf("请选择数字:>");
		scanf("%d", &input);
		switch (input)
		{
    
    
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);



	return 0;
}

game.c

初始化棋盘

这里为什么传给一个“set”?
注意因为我们要初始两个棋盘
第一个:放雷的棋盘 (初始的时候不设置雷,所以棋盘全都是字符‘0’)
第二个:展示个我们自己看的棋盘 (初始的时候设置成字符‘*’

void Initboard(char board[ROWS][COLS], int row, int col,char set)
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
    
    
		for (j = 0; j < col; j++)
		{
    
    
			board[i][j] = set;
		}

	}

}

打印棋盘

void Displayboard(char board[ROWS][COLS], int row, int col)
{
    
    
	int i = 0;
	int j = 0;
	printf("——————扫雷游戏——————\n");
	for (j = 0; j <= col; j++)
	{
    
    
		printf("%d ", j);
	}
	printf("\n");
	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");

}

在这里插入图片描述

设置雷

Easy_count是前面我们在game.h定义的雷的数量,
至于为什么不在while()括号里面使用Easy_count,是因为#define 定义的是一个常量,不能修改

这里有个特殊说明为什么我们明明是想在9*9里放雷,可我们这里是board[ROWS][COLS]而不是board[ROW][COL]

需要注意的是我们初始化在这里插入图片描述
是11*11的棋盘,所以只能用ROWSCOLS,来接收

那我们想在9*9的棋盘放地雷怎么办呢?
解决方法就是在调用函数的时候传ROWCOL,这是我们在头文件定义的。

void Setmine(char board[ROWS][COLS], int row, int col) 
{
    
    
	
	int count = Easy_count;
	while (count)
	{
    
    
		int x = rand() % row + 1;
		int y = rand() % row + 1;
		if (board[x][y] == '0')
		{
    
    
			board[x][y] = '1';
			count--;
		}

	}

}

计算该坐标周围有几个雷

字符‘1’表示雷 ‘0’不是雷
字符‘1’在内存中存放的是Ascii码,它的Ascii码是49
字符‘0’Ascii是48

int getmine(char board[ROWS][COLS], int row, int col,int x,int y)
{
    
    
	int i = 0;
	int j = 0;
	int ret = 0;
	//这里共加了9个坐标
	//把本身也加上了
	for (i = -1; i <= 1; i++)
	{
    
    
		for (j = -1; j <= 1; j++)
		{
    
    
			ret += board[x + i][y + j];
		}
	}
	//所以需要减去该坐标的Ascii码
	return ret -board[x][y]-8 * '0';

}

排雷

简易版
void Changemine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	//win表示
	//棋盘是9*9的总共81格
	//假如放10个雷,还剩下70格也表示我们可以走70步
	//当70步走完还没有碰见雷,游戏胜利
	int win = 0;
	while (win < row * col - Easy_count)
	{
    
    

		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		//判断属于坐标是否在9*9的范围
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			//检查输入坐标是否重复
			if (show[x][y] != '*')
			{
    
    
				printf("坐标重复输入,请重新输入坐标\n");
			}
			else
			{
    
    
				//遇见雷
				if (mine[x][y] == '1')
				{
    
    
					printf("抱歉,你被炸死了,排雷失败,游戏结束\n");
					Displayboard(mine, ROW, COL);
					break;
				}
				else
				{
    
    
					//当该坐标不是雷
					//查看该坐标周围有几个雷
					int count=getmine(mine,row,col,x,y)
					//因为show[][]初始化的时候放的是字符‘*’
					//所以为了使该坐标周围雷的数量——>变成字符‘1’/'2'/'3'数量等等
					show[x][y]=count+'0';
					Displayboard(show, ROW, COL);
					//标记雷的位置
					int input = 0;
					int i = 0;
					int j = 0;
					printf("是否标记雷的位置 1.标记  0.不标记 :>");
					scanf("%d", &input);
					while (input)
					{
    
    
						printf("请输入要标记的坐标:>");
						scanf("%d %d", &i, &j);
						show[i][j] = '!';
						Displayboard(show, ROW, COL);
						printf("继续标记雷的位置,请按'1',否则,请按‘0’:>");
						scanf("%d", &input);

					}

				}
			}



		}
		//坐标范围不合法
		else
		{
    
    
			printf("坐标不合法,请重新输入:>\n");
		}
		//游戏胜利
		if (win == row * col - Easy_count)
		{
    
    
			printf("恭喜你,获得游戏胜利\n");
			Displayboard(mine, ROW, COL);
		}
	}

}

实现的逻辑,棋盘现在可以不用看

在这里插入图片描述

进阶版

1.实现标记功能

上面已给出

2.实现游戏刚开始炸一片功能和输入坐标周围没有雷也炸一下

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

void Changemine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;
	//进阶版代码
	//生成随机坐标,在游戏刚开始的时候,就炸一片
	while (1)
	{
    
    
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//为了刚开始该坐标不是雷
		//并且周围也没有雷
		//如果少了这个“周围也没有雷”的判断条件,
		//那就checkmine()只会执行一次,不会递归,读者可以减去这个条件看一下效果
		if (mine[x][y] != '1'&&(mine[x - 1][y - 1] + mine[x - 1][y] +
			mine[x - 1][y + 1] +
			mine[x][y - 1] + mine[x][y + 1] +
			mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0' == 0))
		{
    
    
			//递归实现
			win=checkmine(mine, show, x, y, win ,row, col);
			break;
		}
	}
	Displayboard(show, ROW, COL);
	while (win < row * col - Easy_count)
	{
    
    

		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			if (show[x][y] != '*')
			{
    
    
				printf("坐标重复输入,请重新输入坐标\n");
			}
			else
			{
    
    
				//遇见雷
				if (mine[x][y] == '1')
				{
    
    
					printf("抱歉,你被炸死了,排雷失败,游戏结束\n");
					Displayboard(mine, ROW, COL);
					break;
				}
				else
				{
    
    
				//这里实现了输入坐标且该坐标周围没有雷也炸一下
				//如果有雷就会显示该坐标周围有多少雷
					win=checkmine(mine, show, x, y, win, row, col);
					Displayboard(show, ROW, COL);
					//标记雷的位置
					int input = 0;
					int i = 0;
					int j = 0;
					printf("是否标记雷的位置 1.标记  0.不标记 :>");
					scanf("%d", &input);
					while (input)
					{
    
    
						printf("请输入要标记的坐标:>");
						scanf("%d %d", &i, &j);
						show[i][j] = '!';
						Displayboard(show, ROW, COL);
						printf("继续标记雷的位置,请按'1',否则,请按‘0’:>");
						scanf("%d", &input);

					}

				}
			}



		}
		else
		{
    
    
			printf("坐标不合法,请重新输入:>\n");
		}
		if (win == row * col - Easy_count)
		{
    
    
			printf("恭喜你,获得游戏胜利\n");
			Displayboard(mine, ROW, COL);
		}
	}

}
炸一片功能的实现

//递归实现
//该坐标不是雷
//该坐标旁边没有雷
//该坐标没有被排查
//是该坐标变成‘ ’(show[x][y]= ’ ')
在这里插入图片描述从红色开始,因为我们首先进行了判断
在这里插入图片描述

所以第一个坐标100%符合条件,
然后在x-1,y-1坐标开始递归(也就是黄色)
注意这个坐标里面包括了(红色),也就是我们已经筛选好的,如果递归里面再有这个(红色),肯定死递归

int checkmine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int win, int row, int col)
{
    
    
	//只在9*9棋盘递归,
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
    
    
		if (mine[x - 1][y - 1] + mine[x - 1][y] +
			mine[x - 1][y + 1] +
			mine[x][y - 1] + mine[x][y + 1] +
			mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1]-8*'0' == 0)
		{
    
    
			win++;
			show[x][y] = ' ';
			//循环该坐标周围8个坐标
			int i = 0;
			int j = 0;
			for (i = -1; i <= 1; i++)
			{
    
    
				for (j = -1; j <= 1; j++)
				{
    
    
				//这里是排除已经检验过坐标,防止死递归
					if ((show[x + i][y + j] == '*'))
					{
    
    
						checkmine(mine, show, x + i, y + j, win, row, col);
					}
				}
			}
		}	
		else
		{
    
    
			win++;
			int count = getmine(mine, row, col, x , y );
			show[x][y] = count + '0';
		}
	}


	return win;
}

至于win这个变量,就是为了保持当炸一片的时候,步数也要+1,不然雷排完了,步数对不上,就不会显示获胜
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

至此扫雷逻辑已经全部实现,
各位观众老爷们,点个赞呗!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/fight_p/article/details/124652945