(包可执行)扫雷游戏(高级版)完整版(C语言实现)万字!!!

   扫雷游戏的扩展

1.是否可以选择游戏难度
◦ 简单 9*9 棋盘,10个雷
◦ 中等 16*16棋盘,40个雷
◦ 困难 30*16棋盘,99个雷
2.如果排查位置不是雷,周围也没有雷,可以展开周围的一片
3.是否可以标记雷
4.是否可以加上排雷的时间显示
上面四个扩展的操作篇来啦嘿嘿!!!

扩展1:是否可以选择游戏难度

◦ 简单 9*9 棋盘,10个雷
◦ 中等 16*16棋盘,40个雷
◦ 困难 30*16棋盘,99个雷
很简单,只要改变ROW和COL和EASY_COUNT的值即可, 具体是:在涉及到雷个数的函数内设置雷的个数的形参,后面在test.c中写入不同的参数值,然后在开始游戏之前写个switch选择难度,不同分支下的雷数参数值不同。



扩展2:展开周围的一片

实现这一功能就不得不变化一下之前写的FindMine函数了(一点点变化),更重要的是编写ExplandDisplay函数的代码,使其嵌入到FindMine函数中。
大致思想:只要count=0就把周围8个坐标全部展开,如果还有count=0的继续展开,这就要用到 递归
在写主体代码的时候我遇到了 三个问题(第三个在下面):
第一个问题是:如果坐标(a,b)的count值等于0(即坐标(a,b)周围没有雷),展开周围8个坐标后发现坐标(c,d)也是count值等于0,那么调用自身(即递归一次),展开周围8个坐标,这样的话会把原先的坐标(a,b)又展开一遍,这样就陷入了 死循环
解决方法:将已经排查过的且count=0的地方改为空格符,这样防止了下次碰到时的循环。判断条件有两个:一个是count=0,第二个是不为空格符。满足后将此地址改为空格符,然后进入下一次递归。
第二个问题是 :因为棋盘有边界,不断地递归扩展可能会触碰到边界,导致越界,不规则展开,
结果如下:
88667fb057bb46f3a10a35a0b3f6edd3.png
解决方法
因此要设置好边界if (x >= 1 && x <= ROW & y >= 1 && y <= COL) //防止递归时越界导致的不规则展开(这可不是在规定输入的坐标范围!!!!)
下面是ExplandDisplay函数的代码(方法1)(具体代码看下下面的)
void ExplandDisplay(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	extern int win;//二次声明
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//防止递归时越界导致的不规则展开
	{
		int count = GetMineCount(mine, x, y);
		if (count != 0)
		{
			show[x][y] = count + '0';
			win++;
		}
		else 
		{
			show[x][y] = ' ';
			win++;
			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] != ' ')
					ExplandDisplay(mine, show, i, j);
				}
			}
		}
		else
		{
			return;
		}
	}
}

第三个问题在于:当FindMine函数调用ExplandDisplay函数时,如何使变量win地址固定(即使变量win在两个函数中通用)?其次还要求ExplandDisplay函数在每次递归时不重复初始化变量win?

刚开始我在想这个问题的时候,一直想用static关键词(static int win=0)把它放在ExplandDisplay函数中(这样能使win的值得到保留而不重复初始化),后来发现这样做只满足了第二个条件,变量win还是无法于FindMine函数中变化(因为作用域不同

解决方法:

那么我就想到了统一作用域,也就是说将变量win设置为全局变量,然后在game.c的FindMine函数和ExplandDisplay函数中调用它,这样static的亲兄弟extern关键字完美解决了这个问题。

方法1:(记得在test.c文件中定义全局变量win=0)

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	extern int win;//一次声明
	int x = 0;
	int y = 0;
	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] == '*')
			{

				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					Display(mine, row, col);
					break;
				}
				else
				{	
					ExplandDisplay(mine, show, x, y );
					Display(show, row, col);
				}
			}
			else
				printf("该坐标已经被排查了,重新输入坐标\n");
		}
		else
		{
			printf("输入语法错误,请重新输入\n");
		}
	}

	 if(win== row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		Display(mine, ROW, COL);
	}
}


void ExplandDisplay(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	extern int win;//二次声明
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//防止递归时越界导致的不规则展开
	{
		int count = GetMineCount(mine, x, y);
		if (count != 0)
		{
			show[x][y] = count + '0';
			
			win++;
		}
		else 
		{
			show[x][y] = ' ';
			win++;
			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] != ' ')
					ExplandDisplay(mine, show, i, j);
				}
			}
		}

	}
}

方法2:

在预习指针内容后,我又发现也可以通过指针将win的地址交给另一个变量pw,这样win在两个函数中都可以统一使用啦,ExplandDisplay函数要多定义一个*pw形参

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int* pw = &win;
	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] == '*')
			{

				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					Display(mine, row, col);
					break;
				}
				else
				{
					ExplandDisplay(mine, show, x, y, pw);
					Display(show, row, col);
				}
			}
			else
				printf("该坐标已经被排查了,重新输入坐标\n");
		}
		else
		{
			printf("输入语法错误,请重新输入\n");
		}
	}

	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		Display(mine, ROW, COL);
	}
}

void ExplandDisplay(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* pw)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//防止递归时越界导致的不规则展开
	{
		int count = GetMineCount(mine, x, y);
		if (count != 0)
		{
			show[x][y] = count + '0';
			(*pw)++;
		}
		else 
		{
			show[x][y] = ' ';
			(*pw)++;
			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] == '*')
					ExplandDisplay(mine, show, i, j, pw);
				}
			}
		}

	}
}

这两个方法都可以很好地实现扩展效果




扩展3:是否可以标记雷

标记雷需要创建一个函数并且将其和排查雷放在一起

代码如下:

void Flag(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	Display(show, row, col);
	printf("请输入要标记的坐标:");
	scanf("%d %d", &x, &y);
	system("cls");
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
	{
		if (show[x][y]=='!')
		{
			printf("该坐标已被标记,请重新输入\n");
		}
		else if (show[x][y] == '*')
		{
			show[x][y] = '!';
			system("cls");
			printf("标记成功\n");
			Display(show, row, col);
		}
	}
	else
		printf("输入语法错误,请重新输入\n");
	
}

扩展4:计算游戏时间

			start = clock();
			game();
			stop = clock();
			printf("用时:%f秒\n", (double)(stop - start) / (CLOCKS_PER_SEC));

在game()开始的前后计算时间即可

其它扩展:

1.取消标记函数

   和标记函数相似:
 

void CancelFlag(char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	Display(show, row, col);
	printf("请输入要取消标记的坐标:");
	scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == '!')
		{
			show[x][y] = '*';
			system("cls");
			printf("取消成功\n");
			Display(show, row, col);
		}
		else
			printf("该坐标未被标记,取消失败\n");
	}
	else
		printf("输入语法错误,请重新输入\n");

}

2.爆炸函数

效果还行哈哈哈

void Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	system("cls");
	printf("3");
	Sleep(888);
	system("cls");
	printf("2");
	Sleep(888);
	system("cls");
	printf("1");
	Sleep(888);
	system("cls");
	int i, j = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*' || show[i][j] == '!')
			{
				int count = GetMineCount(mine, i, j);
				show[i][j] = count + '0';
			}
		}
	}
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{

			if (mine[i][j] == '1')
			{
				show[i][j] = '#';
			}

		}
	}
	Display(show, row, col);
	printf("\n");
	printf("炸弹为:#\n");
	printf("你被炸死了\n");
}


完整源码:

1.game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 1
#include<time.h>
#include<stdlib.h>
#include<windows.h>

void  InitBoard(char board[ROWS][COLS], int rows,int cols,char set);//初始化函数

void  Display(char board[ROWS][COLS], int row, int col);//打印函数

void  SetMine(char board[ROWS][COLS], int row, int col);//埋雷函数

int GetMineCount(char board[ROWS][COLS], int x, int y);//算雷函数

void FindMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col);//找雷函数

void ExplandDisplay(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);//扩展函数

void Flag(char show[ROWS][COLS], int row, int col);//标记函数

void CancelFlag(char show[ROWS][COLS], int row, int col);//取消标记函数

void Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//爆炸函数

//计时器内置

2.game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void  InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{

	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}


void  Display(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("---------------扫雷游戏---------------\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}

}

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

int GetMineCount(char board[ROWS][COLS], int x, int y)//显示周围雷的个数
{
	return (board[x - 1][y] + board[x - 1][y - 1] + board[x][y - 1] + board[x + 1][y - 1] + board[x + 1][y]+
		board[x + 1][y + 1] + board[x][y + 1] + board[x - 1][y + 1] - 8 * '0');


void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int operate = 0;
	int* pw = &win;


		while (win < row * col - EASY_COUNT)
		{
			printf("|------------------|\n");
			printf("|--- 1.排查地雷 ---|\n");
			printf("|--- 2.标记地雷 ---|\n");
			printf("|----3.取消标记----|\n");
			printf("|------------------|\n");
			printf("\n");
			printf("请选择你的操作:");
			scanf("%d", &operate);
			system("cls");
			if (operate == 1)
			{
				Display(show, row, col);
				printf("请输入要排查的坐标:");
				scanf("%d %d", &x, &y);
				if (x >= 1 && x <= row && y >= 1 && y <= col)
				{
					if (show[x][y] == '*')
					{

						if (mine[x][y] == '1')
						{
							Boom(mine, show, ROW, COL);
							break;
						}
						else
						{
							system("cls");
							ExplandDisplay(mine, show, x, y, pw);
							Display(show, row, col);
						}
					}
					else
						printf("该坐标已经被排查了,重新输入坐标\n");
				}
				else
				{
					printf("输入语法错误,请重新输入\n");
				}
			}
			if(operate==2)
				Flag(show, ROW, COL);
			if(operate == 3)
				CancelFlag(show, ROW, COL);
		}

		if (win == row * col - EASY_COUNT)
		{
			system("cls");
			printf("恭喜你,排雷成功\n");
			Display(mine, ROW, COL);

		}


}

void ExplandDisplay(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* pw)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//防止递归时越界导致的不规则展开
	{
		int count = GetMineCount(mine, x, y);
		if (count != 0)
		{
			show[x][y] = count + '0';
			(*pw)++;
		}
		else 
		{
			show[x][y] = ' ';
			(*pw)++;
			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] == '*')//不能写成if (show[i][j] != ' ')否则会把标记也展开而消失
					ExplandDisplay(mine, show, i, j, pw);
				}
			}
		}

	}
}


void Flag(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	Display(show, row, col);
	printf("请输入要标记的坐标:");
	scanf("%d %d", &x, &y);
	system("cls");
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
	{
		if (show[x][y]=='!')
		{
			printf("该坐标已被标记,请重新输入\n");
		}
		else if (show[x][y] == '*')
		{
			show[x][y] = '!';
			system("cls");
			printf("标记成功\n");
			Display(show, row, col);
		}
	}
	else
		printf("输入语法错误,请重新输入\n");
	
}

void CancelFlag(char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	Display(show, row, col);
	printf("请输入要取消标记的坐标:");
	scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == '!')
		{
			show[x][y] = '*';
			system("cls");
			printf("取消成功\n");
			Display(show, row, col);
		}
		else
			printf("该坐标未被标记,取消失败\n");
	}
	else
		printf("输入语法错误,请重新输入\n");

}

void Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	system("cls");
	printf("3");
	Sleep(888);
	system("cls");
	printf("2");
	Sleep(888);
	system("cls");
	printf("1");
	Sleep(888);
	system("cls");
	int i, j = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*' || show[i][j] == '!')
			{
				int count = GetMineCount(mine, i, j);
				show[i][j] = count + '0';
			}
		}
	}
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{

			if (mine[i][j] == '1')
			{
				show[i][j] = '#';
			}

		}
	}
	Display(show, row, col);
	printf("\n");
	printf("炸弹为:#\n");
	printf("你被炸死了\n");
}

3.test.c

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

void menu()
{
	printf("---------------扫雷游戏---------------\n");
	printf("******************************\n");
	printf("************1.开始游戏********\n");
	printf("************0.结束游戏********\n");
	printf("******************************\n");
}


void game()
{
	//创建11*11数组
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初始化11*11棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//setMine埋雷(9*9范围内)
	SetMine(mine, ROW, COL);

	//打印9*9棋盘
	//Display(mine, ROW, COL);
	Display(show, ROW, COL);
	//(核心)排雷
	FindMine(mine, show, ROW, COL);
}




void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls");
			printf("扫雷开始\n");
			clock_t start,stop;
			start = clock();
			game();
			stop = clock();
			printf("用时:%f秒\n", (double)(stop - start) / (CLOCKS_PER_SEC));
			break;
		case 0:
			printf("游戏结束\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}

	} while (input);

}


int main()
{
	test();
	return 0;
}

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值