趣味小游戏——扫雷(优化版)

目录

一.前言  

二.游戏主体框架

三.游戏内部实现

1.给出一个ROW*COL的棋盘

2.初始化两棋盘

3.随机生成雷

4.反馈信息

5.排雷过程

6.优化部分

四.结语


一.前言  

  这里是趣味小游戏第二篇,扫雷想必也是大家以前电脑上必备的休闲游戏,那么今天我就来带大家用代码的方式来回忆并实现它吧!

二.游戏主体框架

  要实现游戏我们首先要搭建出一个主体游戏框架。

#include 

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

void test()
{
	int input = 0;
	do
	{
		menu();//给出一个游戏的开始菜单
		printf("欢迎来到扫雷游戏\n");
		printf("输入1开始游戏,输入0退出游戏\n");
		printf("请输入:");
		scanf("%d", &input);
		switch (input)//对于不同输入值给出不同结果
		{
		case 1:
			game();//由此进入游戏
			break;
		case 0:
			printf("游戏已退出\n");//由此退出游戏
			break;
		default:
			printf("输入错误,请重新输入:");//输入其他值时给出反馈,增强代码健壮性
			break;
		}
	} while (input);//输入为真(1)时则进入do里的循环输入为假(0)时即退出循环
}

int main()
{
	test();//主体内容
	return 0;
}

三.游戏内部实现

  在搭建好主体后,我们就进入游戏实现阶段。

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

void game()
{
	//1.给出一个ROW*COL的棋盘
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
    //2.初始化两棋盘
	IniBoard(mine, ROWS, COLS, '0');
	IniBoard(show, ROWS, COLS, '*');
	//3.随机生成雷
	SetMine(mine, ROW, COL);
	//4.反馈信息
	//PrintBoard(mine, ROW, COL);
	PrintBoard(show, ROW, COL);
	//5.排雷过程
	FindMine(mine, show, ROW, COL);
}

1.给出一个ROW*COL的棋盘

我们在这里假定ROW=COL=9,那么目标就是产生这样一个9*9的棋盘。

  趣味小游戏——扫雷(优化版)_第1张图片

 而在我们统计一个格子周围雷的个数时,会出现一些格子统计雷的数量比较困难(如9 9坐标),因此我们将其行和列拓宽两行得到如下棋盘

趣味小游戏——扫雷(优化版)_第2张图片

 因此我们便创建两个11*11的二维数组,一个用来作为生成雷的数组(mine),一个用来作为玩家看与猜测的数组(show)。

2.初始化两棋盘

  在成功生成两棋盘后,我们对其分别进行初始化(即将所有的元素化成同一元素)。

在此,我们将mine数组初始化为'0'(后面会解释),将show数组初始化为'*'(方便玩家观察该数组)。因此,我们编写一个函数来实现此目的。

void IniBoard(char board[ROWS][COLS], int row, int col, char n)
{
	int i = 0;
	for (i = 0; i < row; i++)//得到每行数据
	{
		int j = 0;
		for (j = 0; j < col; j++)//得到每列数据
		{
			board[i][j] = n;//将每个元素都转变成字符n
		}
	}
}

3.随机生成雷

  在初始化两数组后,我们便开始在mine数组中随机布置雷。

(注意:可供我们操作的范围为1 1——9 9的范围内,因此雷的生成位置也应该处于1 1——9 9之间)

#define EASY 15
//简单模式的雷的个数

void SetMine(char mine[ROWS][ROWS], int row, int col)
{
    srand((unsigned int)time(NULL));//用时间戳来创造伪随机数
	int count = EASY;
	while(count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;//因为行数与列数确定,因此需要保证x,y都处于[1,9]这个区间
		if (mine[x][y] == '0')//判断该位置是否已经被布置了雷
		{
			mine[x][y] = '1';
			count--;//布置成功则使需要布置的雷的个数减少一个
		}
	}
}

4.反馈信息

  在游戏进行的过程中,我们作为玩家需要不断地知道自己输入坐标后的结果怎么样,所以便离不开显示棋盘这一操作,因此,我们便设计一个函数来进行这个操作。

void PrintBoard(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");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//打印每个元素
		}
		printf("\n");
	}
	printf("---------扫雷游戏--------\n");//下分割行
}

我们用它来打印show数组,得到如下的效果

趣味小游戏——扫雷(优化版)_第3张图片

5.排雷过程

  在看到棋盘后,想必大家已经手痒痒了吧,别急,让我来带各位进入正戏环节。

还是老样子,我们编写一个函数来实现这一步骤。

void Change(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int i = 1;
	for (i = 1; i <= row; i++)
	{
		int j = 1;
		for (j = 1; j <= col; j++)
		{
			if (mine[i][j] == '1')
			{
				show[i][j] = '#';//将mine棋盘中的雷表现为show数组中的#
			}
		}
	}
}

int MineCount(char board[ROWS][COLS], int x, int y)
{
	return board[x-1][y-1] + board[x-1][y] + board[x-1][y+1] + board[x][y-1]
		+ board[x][y+1] + board[x+1][y-1] + board[x+1][y] + board[x+1][y+1] -8*'0';//根据ASCII码表将所有1的个数相加并返回
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	printf("输入排查位置(如5 5)\n");
	int count = ROW * COL - EASY;//用count代表场上除去雷的剩余位置
	while(1)//无法具体确定判断条件,所以用1作为条件,后面完成行为后用break跳出
	{
		int x = 0;
		int y = 0;
		printf("请输入你想排查的坐标\n");
		printf("请输入:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的位置是否在棋盘内,如果不在内就反馈给玩家
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你碰到了炸弹,游戏结束\n");
				Change(show, mine, row, col);//在判定玩家已输的情况下,将情况反映到show数组上
				PrintBoard(show, row, col);//让玩家知道自己如何输的
				break;
			}
			else if (show[x][y] != '*')//如果玩家手误,可以借此重新输入
			{
				printf("该位置已被排查过,请重新输入\n");
			}
			else
			{
				int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
				show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
				PrintBoard(show, row, col);//将结果反馈给玩家
				count--;
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入\n");
		}
		if (count == 0)//当除雷以外的剩余个数为0时,那么棋盘上只剩下雷,即玩家获胜
		{
			printf("恭喜你,排雷成功\n");
			PrintBoard(mine, row, col);//将最后结果反馈给玩家
			break;
		}
	}
}

6.优化部分

在经过了上述流程后,虽然我们体验到了扫雷游戏,但是总感觉玩起来不是很方便,此时我们的心头便会冒出一些想法:可不可以在周围有0个雷的时候直接展开一片?在排查完雷之后可不可以自己标记雷的位置?在标记后可不可以显示自己主观认为的剩余的雷的个数?

答案是:当然!

我们先对展开方面进行优化

趣味小游戏——扫雷(优化版)_第4张图片

 我们在反馈前先构建一个Cls函数进行对0的清除与展开,即改为如下这样

else
			{
				int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
				show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
                Cls(mine, show, row, col, x, y);
				PrintBoard(show, row, col);//将结果反馈给玩家
				count--;
			}

下面具体实现Cls函数

void Cls(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (show[x][y] == '0')//如果此元素已经为'0'才进入此函数
	{
		show[x][y] = ' ';//将其修改为' '便于观查
		int i = x - 1;
		for (; i <= x + 1; i++)
		{
			int j = y - 1;
			for (; j <= y + 1; j++)//得到该元素的从左上到右下的全部下标
			{
				if (show[i][j] == ' ')//如果已经有元素被替换为' ',则无需再次进行下面的操作
				{
					continue;
				}
				if (i >= 1 && i <= row && j >= 1 && j <= col)//判断该元素是否在棋盘内
				{
					if (show[i][j] != '*')//如果该元素不是'*',即已经被赋值了数字就无需进行下面的操作
					{
						continue;
					}
					int ret = MineCount(mine, i, j);//对该元素进行判定
					show[i][j] = '0' + ret;
					Cls(mine, show, row, col, i, j);//不断迭代,直到不再出现0或数字为止
				}
				
			}
		}
	}
}

 成功展开后,我们再来对标记进行操作与优化

else
			{
				int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
				show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
                Cls(mine, show, row, col, x, y);
                PlayerJudge(show, row, col);
				PrintBoard(show, row, col);//将结果反馈给玩家
				count--;
			}

同样的我们编写一个PlayerJduge函数来实现此操作

void Judge(char show[ROWS][COLS], int row, int col)
{
	printf("坐标操作:\n");
	printf("1 - 不确定是否为雷\n");
	printf("2 - 确定是雷\n");//根据ASCII码表将1和2转换成对应的?与#
	printf("3 - 取消标记\n");
	printf("请输入你想标记的坐标(如5 5) :\n");
	int x = 0;
	int y = 0;
	while(1)
	{
		printf("请输入:");
		scanf("%d %d", &x, &y);
		printf("请输入你想对该坐标进行的操作\n");
		int c = 0;
		scanf("%d", &c);
		if (show[x][y] == '*')
		{
			if (c == 1)
			{
				c = 63;//将1转换成?所对应的ASCII值
			}
			if (c == 2)
			{
				c = 35;//将2转换成#所对应的ASCII值
			}
			char ch = c;
			show[x][y] = ch;
			printf("标记成功!\n");//给出反馈
			PrintBoard(show, row, col);
			break;//结束本次标记
		}
		else if (show[x][y] == '?' || show[x][y] == '#' && c == 3)//如果输入3还要判断该元素是否已经被标记
		{
			show[x][y] = '*';
			printf("取消成功!\n");//给出反馈
			PrintBoard(show, row, col);
			break;
		}
		else
		{
			printf("输入位置不合法,请重新输入\n");//输入不合理
		}
	}
}

void PlayerJudge(char show[ROWS][COLS], int row, int col)
{
	int m = 0;
	printf("请问你是否想标记某位置\n");
	printf("是:1 否:0\n");
	do
	{
		printf("请输入(如已标记完毕请输入0,想继续标记请输入1):");//在进行完一次标记后,玩家需要确定是否进行下一次标记
		scanf("%d", &m);
		switch (m)
		{
		case 1:
			Judge(show, row, col);//具体实现标记操作
			break;
		case 0:
			break;
		default:
			printf("输入有误,请重新输入:\n");
			break;
		}
	} while (m);
}

最后呢,我们再对雷量的剩余做一个LeaveMine函数来反馈

else
			{
				int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
				show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
                Cls(mine, show, row, col, x, y);
                PlayerJudge(show, row, col);
                int res = LeaveMine(show, row, col);//show数组中#的个数
				printf("剩余雷量:%d\n", EASY - res);//反馈剩余的雷的个数
				PrintBoard(show, row, col);//将结果反馈给玩家
				count--;
			}

下面是具体的实现办法

int LeaveMine(char show[ROWS][COLS], int row, int col)
{
	int i = 1;
	int num = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 1;
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '#')//统计所有元素中#的个数
			{
				num++;
			}
		}
	}
	return num;//返回#的个数
}

至此,我们便完成了扫雷游戏的优化!

四.结语

  以上就是扫雷游戏的全部实现过程了,希望各位程序员能够玩得开心,同时也希望能从本文中得到灵感与启发!路过的点个赞吧,谢谢!

你可能感兴趣的:(c语言)