扫雷【联机对战】

1.基础版实现思路

大致思路就分为:
在这里插入图片描述
扫雷的时候,我们需要两个二维数组,一个用来存放数据,另一个用来展示给玩家看;
所以我们需要对两个相同大小的数组进行不同的初始化,我们可以通过传递不同的参数来实现

(因为考虑到在数组最外围的坐标,遍历周围是否有雷时,可能会出现越界访问的情况,所以我们的二维数组的大小应该比我们需要用到的大上一圈)
在这里插入图片描述

游戏的效果:
在这里插入图片描述

2.改进

改进的思路大致为:
在这里插入图片描述

2.1可以对周围没有雷的坐标进行展开

当输入坐标合法,且周围坐标没有雷时,可以进行向外展开
我们可以用递归的方式实现周围八个坐标的连续展开

//将周围八个坐标的ASICC码值加起来,减去8个‘0’的ASICC码值,最后可以得到周围字符为'1'的个数
int how_many_mine(char arr[ROWS][COLS], int row, int col, int x, int y)//判断周围有多少雷
{
	return (arr[x + 1][y] + arr[x + 1][y - 1] + arr[x + 1][y + 1]
		+ arr[x][y - 1] + arr[x][y + 1]
		+ arr[x - 1][y - 1] + arr[x - 1][y] + arr[x - 1][y + 1]) - 8 * '0';
}

//递推:从x和y的位置,用 how_many_mine遍历周围8个坐标,如果返回值为0,循环执行此操作
//递归:用 how_many_mine计算当前位置的周围坐标返回值不为0,当前坐标等于这个函数的返回值
void mine_spread(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS],int row, int col, int x, int y)//展开(递归)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col && mine_show[x][y] != ' ')
	{
		int n = how_many_mine(mine_ifm, ROW, COL, x, y);
		if (n == 0)
		{
			mine_show[x][y] = ' ';
			mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y - 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y + 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x, y - 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x, y + 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
		}
		else
		{
			mine_show[x][y] = n + '0';
		}
	}
}

最后的效果是:

在这里插入图片描述

2.2使玩家不会在一开始就踩到雷

如果我们的运气不是太好,总是一开始就被炸死了,那就会显得很尴尬😅,我能被自己写的游戏给玩了?
我们可以将布置雷和扫雷两个步骤放到一个函数中,设置一个全局变量set,先输入一个合法坐标,当判断set等于1时,生成雷,生成的雷在指定范围内且不能等于输入的坐标,set改变。可以在进入game()函数之前,给set重置为1,方便下一次游戏继续使用。
生成雷的函数:

void raise_mine(char mine_ifm[ROWS][COLS], int row, int col, int a, int b)//生成雷
{
	
	int i = MINE;
	while (i)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine_ifm[x][y] == '0' && x > 0 && y > 0)
		{
			if (x == a && y == b)
			{
				continue;
			}
			mine_ifm[x][y] = '1';
			i--;
		}
	}
}

这样的话即使我们一共有81个坐标,我们给80个雷,雷也会绕着我们走:

在这里插入图片描述

在这里插入图片描述

2.3玩家可以标记雷

当周围的雷太多,记不住,猪脑过载了怎么办?
我们可以在输入坐标进行扫雷的时候多加一个选项,也就是标记雷;
标记雷:也是输入一个坐标,判断是否合法,只是说不用计算周围雷的个数,而是直接将show数组上你输入的位置的字符改为你标记的字符(再次输入取消标记);
判断输赢时也记得带上它!

void player_mine(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col)//玩家扫雷
{
	int x = 0;
	int y = 0;
	int how = 0;
	int set = 1;
	int n = 0;
	while (1)
	{
		system("cls");
		print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
		printf("请选择:\n1.扫雷 2.标记雷\n");
		scanf("%d", &n);
		
		if (n == 1)//扫雷
		{
			printf("请输入要排查的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
			{
				if (mine_show[x][y] == MODEL|| mine_show[x][y] == MARK)//坐标没有被排查过
				{
					if (set == 1)//没有扫过雷
					{
						raise_mine(mine_ifm, ROW, COL, x, y);//生成雷
						set--;
					}
					system("cls"); //清除屏幕
					if (mine_ifm[x][y] == '1')
					{
						printf("你被炸死了,游戏结束\n");
						print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
						break;
					}
					else
					{
						how = how_many_mine(mine_ifm, ROW, COL, x, y);
						if (how == 0)
						{
							mine_spread(mine_ifm, mine_show, ROW, COL, x, y);//展开
						}
						else
						{
							mine_show[x][y] = how + '0';
						}
						print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
						if (is_win(mine_show, ROW, COL, MODEL, MARK) == MINE)//判断输赢
						{
							printf("恭喜你,排雷成功\n");
							print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
							break;
						}
					}
				}
				else
					printf("该坐标已被排查\n");
			}
			else
				printf("坐标输入错误,请重新输入\n");
		}
		else if (n == 2)//标记雷
		{
			printf("如果输入已被标记的坐标,将被视为清除标记 ! ! !\n\n\a");
			printf("请输入要标记的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
			{
				system("cls"); //清除屏幕
				if (mine_show[x][y] == MODEL)//坐标没有被排查过
				{
					mine_show[x][y] = MARK; //标记雷
				}
				else if (mine_show[x][y] == MARK)
				{
					mine_show[x][y] = MODEL; //重复标记视为清除标记
					printf("清除标记成功\n");
				}
				else
					printf("该坐标已被排查\n");
				print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
			}
			else
				printf("坐标输入错误,请重新输入\n");
		}
		else
			printf("选择错误\n");
	}
}

2.4可以多人扫雷,积分制

正所谓,知己知彼,百战不殆。为了实现扫雷的更多功能,我去玩了好多不同版本的扫雷,发现一些平台上扫雷还能联机玩!
大致思路就是:
四个人,每人选定一个坐标扫雷,也可以选择标记雷;扫雷成功或标记雷成功都会获得分数,如果踩到雷或标记雷失败,则会扣分;每轮都有计时(我不会实现🤦‍♂️)
如果两人选择同一个坐标则得到的分数减半;最后计算总分判断输赢

2.5实现代码

最后我们直接看效果:
在这里插入图片描述
那让我们来浅浅实现一下吧:
text.c

#define _CRT_SECURE_NO_WARNINGS 1
extern set;
//1.创建两个二维数组,一个存放雷的信息,一个展示给玩家
//2.将两个数组初始化,一个全为字符0,一个全为*
//3.打印扫雷的棋盘
//4.在第一个数组中生成雷,玩家以后的操作都是在第一个数组中操作,再由数组2显示
//5.扫雷。。。
//扫雷:玩家输入一个坐标,如果这个坐标不是雷,显示周围雷的个数

//改进:
//1.展开
//如果棋子周围没有雷,那么可以从当前位置向周围展开,判断周围坐标的周围是否有雷
//有:返回雷的个数,没有:从当前位置继续展开

//2.避免一开始就踩雷
//将布置雷和扫雷放在一起,定义一个变量set,没有生成雷时为1,生成之后为0


//3.标记雷
//输入时,有两个选项1.扫雷,2.标记雷
//注意:标记雷也算作是MODEL

//4.联机模式
//玩家可以选择联机对战,大致思路如下:
//积分制:1.标记一次雷得两分;标记错误扣一分,坐标还原为MODEL;2.扫雷得一分,踩到雷扣三分
//玩家1下棋,执行完操作后进行积分结算,并根据操作改变mine_show,打印,等待玩家2操作
//玩家2下棋,执行完操作后进行积分结算,改变mine_show数组,打印
//玩家踩雷时,不应该结束游戏,应该进行扣分;所以我们要为联机模式下的判断输赢加上一些限制条件
//打印棋盘


#include "game.h"
void menu1()
{
	printf("-------------扫雷游戏--------------\n");
	printf("***********************************\n");
	printf("************1.开始游戏*************\n");
	printf("***********************************\n");
	printf("************0.退出游戏*************\n");
	printf("***********************************\n");
}
void menu2()
{
	printf("-------------扫雷游戏--------------\n");
	printf("—————————————————-\n");
	printf("——————1.经典模式——————-\n");
	printf("—————————————————-\n");
	printf("——————2.联机对战——————-\n");
	printf("—————————————————-\n");
}
void game(int input)
{
	char mine_ifm[ROWS][COLS] = { 0 };//放置雷的信息
	initial(mine_ifm, ROWS, COLS, '0');//数组的初始化
	char mine_show[ROWS][COLS] = { 0 };//展示出来信息
	initial(mine_show, ROWS, COLS, MODEL);//数组的初始化
	//print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
	//print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
	//扫雷
	if (input == 1)
		player_mine(mine_ifm, mine_show, ROW, COL);//玩家扫雷&&布置雷(经典)
	else if (input == 2)
	{
		printf("规则提示:\n");
		printf("扫雷成功,玩家获得2分,踩到雷则扣3分\n");
		printf("标记雷成功,玩家获得2分,标记失败则扣1分\n");
		printf("当棋盘上只剩下雷时,游戏结束,分高的一方获得胜利\n");
		player_with_friend(mine_ifm, mine_show, ROW, COL);//(联机)
	}
		
}
int main()
{
	int input1 = 0;
	int input2 = 0;
	srand((unsigned int)time(NULL));
	do
	{
		set = 1;
		menu1();
		printf("请选择:>");
		scanf("%d", &input1);
		switch (input1)
		{
		case 1:
			menu2();
			scanf("%d",&input2);
			if (input2 >= 1 && input2 <= 2)
			{
				game(input2);
			}
			else
				printf("选择错误,请重新选择\n");
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("输入错误,重新输入!\n");
		}
	}while (input1);
	return 0;
}

game.h

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

#define ROW 9 //展示出的扫雷棋盘的【行】
#define COL 9 //展示出的扫雷棋盘的【列】

#define ROWS ROW+2 //扫雷棋盘数据的【行】
#define COLS COL+2 //扫雷棋盘数据的【列】

#define MINE 9 //雷的数量
#define MODEL '-' //雷的模型
#define MARK '@' //标记的模型

void initial(char mine_ifm[ROWS][COLS], int rows, int cols, char n);//数组的初始化
void print_show(char mine_ifm[ROWS][COLS], int row, int col);//打印棋盘
void raise_mine(char mine_ifm[ROWS][COLS], int row, int col);//生成雷
void player_mine(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col);//经典模式
void player_with_friend(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col);//联机对战

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
int set = 1;

void initial(char mine_ifm[ROWS][COLS], int rows, int cols, char n)//数组的初始化
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			mine_ifm[i][j] = n;
		}
	}
}

void print_show(char mine_ifm[ROWS][COLS], int row, int col)//扫雷棋盘的打印
{
	int i = 0;
	int j = 0;
	printf("———————扫雷游戏——————-\n");
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			printf(" %c |",mine_ifm[i][j]);
		}
		printf(" %d",i);//打印坐标【行】
		printf("\n");
		for (j = 1; j <= col; j++)
		{
			printf("-—|");
		}
		printf("\n");
	}
	//打印坐标【列】
	for (j = 1; j <= col; j++)
	{
		printf(" %d  ",j);
	}
	printf(" 0\n");
	printf("———————扫雷游戏——————-\n");
}

void raise_mine(char mine_ifm[ROWS][COLS], int row, int col, int a, int b)//生成雷
{
	
	int i = MINE;
	while (i)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine_ifm[x][y] == '0' && x > 0 && y > 0)
		{
			if (x == a && y == b)
			{
				continue;
			}
			mine_ifm[x][y] = '1';
			i--;
		}
	}
}

int is_win(char arr[ROWS][COLS], int row, int col, char model, char mark)//判断输赢
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (arr[i][j] == model || arr[i][j] == mark)
				count++;
		}
	}
	return count;
}


//将周围八个坐标的ASICC码值加起来,减去8个‘0’的ASICC码值,最后可以得到周围字符为'1'的个数
int how_many_mine(char arr[ROWS][COLS], int row, int col, int x, int y)//判断周围有多少雷
{
	return (arr[x + 1][y] + arr[x + 1][y - 1] + arr[x + 1][y + 1]
		+ arr[x][y - 1] + arr[x][y + 1]
		+ arr[x - 1][y - 1] + arr[x - 1][y] + arr[x - 1][y + 1]) - 8 * '0';
}

//递推:从x和y的位置,用 how_many_mine遍历周围8个坐标,如果返回值为0,循环执行此操作
//递归:用 how_many_mine计算当前位置的周围坐标返回值不为0,当前坐标等于这个函数的返回值
void mine_spread(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS],int row, int col, int x, int y)//展开(递归)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col && mine_show[x][y] != ' ')
	{
		int n = how_many_mine(mine_ifm, ROW, COL, x, y);
		if (n == 0)
		{
			mine_show[x][y] = ' ';
			mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y - 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y + 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x, y - 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x, y + 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y);//展开
			mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
		}
		else
		{
			mine_show[x][y] = n + '0';
		}
	}
}

void player_mine(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col)//玩家扫雷
{
	int x = 0;
	int y = 0;
	int how = 0;
	int set = 1;
	int n = 0;
	while (1)
	{
		system("cls");
		print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
		printf("请选择:\n1.扫雷 2.标记雷\n");
		scanf("%d", &n);
		
		if (n == 1)//扫雷
		{
			printf("请输入要排查的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
			{
				if (mine_show[x][y] == MODEL|| mine_show[x][y] == MARK)//坐标没有被排查过
				{
					if (set == 1)//没有扫过雷
					{
						raise_mine(mine_ifm, ROW, COL, x, y);//生成雷
						set--;
					}
					system("cls"); //清除屏幕
					if (mine_ifm[x][y] == '1')
					{
						printf("你被炸死了,游戏结束\n");
						print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
						break;
					}
					else
					{
						how = how_many_mine(mine_ifm, ROW, COL, x, y);
						if (how == 0)
						{
							mine_spread(mine_ifm, mine_show, ROW, COL, x, y);//展开
						}
						else
						{
							mine_show[x][y] = how + '0';
						}
						print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
						if (is_win(mine_show, ROW, COL, MODEL, MARK) == MINE)//判断输赢
						{
							printf("恭喜你,排雷成功\n");
							print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
							break;
						}
					}
				}
				else
					printf("该坐标已被排查\n");
			}
			else
				printf("坐标输入错误,请重新输入\n");
		}
		else if (n == 2)//标记雷
		{
			printf("如果输入已被标记的坐标,将被视为清除标记 ! ! !\n\n\a");
			printf("请输入要标记的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
			{
				system("cls"); //清除屏幕
				if (mine_show[x][y] == MODEL)//坐标没有被排查过
				{
					mine_show[x][y] = MARK; //标记雷
				}
				else if (mine_show[x][y] == MARK)
				{
					mine_show[x][y] = MODEL; //重复标记视为清除标记
					printf("清除标记成功\n");
				}
				else
					printf("该坐标已被排查\n");
				print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
			}
			else
				printf("坐标输入错误,请重新输入\n");
		}
		else
			printf("选择错误\n");
	}
}



int play(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col, int score)//联机对战
{
	int n = 0;
	int x = 0;
	int y = 0;
	int how = 0;
	again:
	printf("请选择:\n1.扫雷 2.标记雷\n");
	scanf("%d", &n);
	if (n == 1)//扫雷
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
		{
			if (mine_show[x][y] == MODEL || mine_show[x][y] == MARK)//坐标没有被排查过
			{
				if (set == 1)//没有扫过雷
				{
					raise_mine(mine_ifm, ROW, COL, x, y);//生成雷
					set--;
				}
				if (mine_ifm[x][y] == '1')
				{
					score = score - 3;   //被炸了,扣3分
				}
				else
				{
					how = how_many_mine(mine_ifm, ROW, COL, x, y);
					if (how == 0)
					{
						mine_spread(mine_ifm, mine_show, ROW, COL, x, y);//展开
					}
					else
					{
						mine_show[x][y] = how + '0';
					}
					score = score + 2;//玩家正确排雷,得到1分
				}
			}
			else
			{
				printf("该坐标已被排查\n");
				goto again;
			}
		}
		else
		{
			printf("坐标输入错误,请重新输入\n");
			goto again;
		}
		system("cls");
		print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
	}


	else if (n == 2)//标记雷
	{
		printf("请输入要标记的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
		{
			if (mine_show[x][y] == MODEL)//坐标没有被排查过
			{
				mine_show[x][y] = MARK; //标记雷
				if (mine_ifm[x][y] == '1')
				{
					score = score + 2;   //标记雷成功,得两分
				}
				else
				{
					score = score - 1;  //标记雷失败,扣1分
				}
			}
			else if (mine_show[x][y] == MARK)
			{
				printf("请勿标记已排查过的雷\n");
				goto again;
			}
			else
			{
				printf("该坐标已被排查\n");
				goto again;
			}
		}
		else
		{
			printf("输入坐标超出范围\n");
			goto again;
		}
	}
	else
	{
		printf("选择错误\n");
		goto again;
	}
	return score;
}





void player_with_friend(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col)//联机对战
{
	
	int n = 0;
	int score1 = 0; //玩家1的得分
	int score_1 = 0;
	int score2 = 0; //玩家2的得分
	int score_2 = 0;

	while (1)
	{
		print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
		score_1 = score1;
		score_2 = score2;
		printf("玩家1扫雷:>\n");
		score1 = play(mine_ifm, mine_show, ROW, COL, score1);
		printf("玩家2扫雷:>\n");
		score2 = play(mine_ifm, mine_show, ROW, COL, score2);
		system("cls");
		int i = 0;
		printf("正在计算结果");
		for (i = 0; i < 3; i++)
		{
			printf(".");
			Sleep(500);
		}
		printf("\n");
		print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
		printf("玩家1得分:%d,玩家2得分:%d\n", score1 - score_1, score2 - score_2);
		if (score1 < 0)
			score1 = 0;
		if (score2 < 0)
			score2 = 0;
		printf("\n玩家1的总分是 %d\n", score1);
		printf("玩家2的总分是 %d\n", score2);
		Sleep(3000);


		if (is_win(mine_show, ROW, COL, MODEL, MARK) == MINE)//判断输赢
		{
			printf("雷已排完,玩家1得分:%d,玩家2得分%d\n", score1, score2);
			if (score1 > score2)
				printf("玩家1胜利\n");
			else if (score1 < score2)
				printf("玩家2胜利\n");
			else
				printf("比分相同,平局\n");

			print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
			break;
		}
		system("cls");
	}
}
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值