扫雷游戏实现

简介:

  扫雷游戏是一个家喻户晓的小游戏,此次文章则着重实现扫雷游戏的一些基本功能,若读者有其余想法可在该基础上进行叠加。本文章采用边分析效果边实现的代码的方式进行阐述。

文件分配方式:

  由于此次代码可能比较多,所以采用分文件的方式进行编写。创建test.c文件作为主函数的位置;创建game.c文件来保存该游戏相关函数实现部分;创建game.h作为头文件以及各个函数声明的位置。具体格式如下:

 功能一:菜单实现

  对于任何一款游戏,刚开始界面均有菜单来指示你是要开始游戏还是退出游戏。因此在主函数中创建void Menu()函数来实现该功能,此代码比较简单,主要就是printf打印。具体代码如下:

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

  此代码规定1作为开始游戏的选项;0作为退出游戏的选项。

功能二:主函数内部逻辑的实现

   结合功能1所规定的两个选项,不同的选项对应不同的功能,所以可用switch-case语句来实现;当一场游戏结束时,应该在给用户进行重新选择的权力--是继续游戏还是退出游戏,所以这个功能可以采用循环结构来实现。对于循环结束的标志,可以发现当用户输入1为继续,输入0为退出,因此循环结构可以通过判断用户输入是否为1来作为标志。具体代码如下:

void test()
{
	int input = 1;
	srand((unsigned int)time(NULL));
	while (input)
	{
		Menu();
		printf("请选择你的游戏方式\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			system("pause");
			system("cls");
			break;
		case 0:
			printf("游戏已退出!");
			break;
		default:
			printf("输入错误,请重新输入");
			break;
		}
	}
}

  补充:

1.game()函数是游戏运行的主函数; 

2.srand((unsigned int)time(NULL));这句代码是根据时间戳来设置随机数种子,使产生的随机数随当前系统时间发生改变,注意:需要添加头文件#include<time.h>和#include<stdlib.h>;

3.system("pause")和system("cls")的实现功能为:第一个语句实现的功能可姑且理解为‘暂停’,只有按下任意键才可继续下面操作;第二个语句是清除运行窗口的界面。

功能三:游戏界面的实现

  对于此功能我们可以结合下面两个图。由下面两个图可得到,在最开始的“外层”游戏界面和点击空白处的“内层”界面是有区别的。若用C语言程序实现,有三个要点:

第一:为了区分“外层”和“内层”,我们可以采用两种不同的字符来代表“外层”和“内层”。在此次程序中,采用‘*’作为“外层”界面显示;采用‘0’作为“内层”界面显示。其中我们将“外层”作为外界显示,即打印的目标;而“内层”是我们实现其余功能的部分。

第二:为了能把不同的字符按照下图存储并打印,我们可设置两个二维数组来进行存储和打印。其中设置“外层”数组为:char show[ROWS][COLS] = { 0 };“内层”数组为:char mine[ROWS][COLS] = { 0 };初始化函数为void Init_Board();打印函数为void Display_Board();

第三:若设置游戏区域为9*9大小的,那我们需要在游戏区域外界在扩大一圈,这是为了防止后续查询雷的个数时发生数组越界访问

//game()函数中的两个二维数组的定义和初始化函数的调用
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	Init_Board(mine, ROWS, COLS, '0');
	Init_Board(show, ROWS, COLS, '*');

}
//二维数组初始化函数
void Init_Board(char arr[ROWS][COLS], int rows, int cols, char ch)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr[i][j] = ch;
		}
	}
}
//二维数组打印函数
void Display_Board(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}

运行框中打印效果如下: 

 功能四:雷的设置

  在扫雷游戏中,雷的位置往往都是随机的,所以在此次程序中使用生成伪随机数函数:rand(),其中可以使用rand() % row + 1语句来自适应棋盘大小。并且功能二的srand((unsigned int)time(NULL))语句就是来配合rand()函数来使用的。具体代码如下:

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

  由上述代码可知,该程序将雷的位置设为‘1’,并且为了防止在一个地方重复设置雷,所以加入if语句进行判断。 

功能五:扫雷功能的实现

小功能一:输入坐标返回周围雷的数量

  由功能四可得,我们使用坐标进行雷的设置,因此同样使用坐标来实现对雷的查询。

  扫雷游戏中返回的雷的数量是点击处周围八个位置雷的总个数。同样,在此次程序中。我们通过计算输入坐标处的周围八个位置的雷的个数来实现该功能。具体代码如下:

int get_mine_num(char arr[ROWS][COLS], int x, int y)
{
	return arr[x - 1][y - 1] + arr[x - 1][y] + 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';
}

 解释:因为在功能四中我们设置字符1来表示雷,但是字符串无法直接进行叠加,所以采用ASCII码的关系进行计数。由下面部分ASCII码表可得,‘0’ -- 80;‘1’ -- 81,所以将‘1’ - ‘0’ = int类型的数值1;上述代码就借助此原理来对雷的数量进行计数。

小功能二:扩展连续的无雷位置的功能

   该功能的示意图如下:

   经过对上面的示意图进行分析可得满足这种情况下的三种条件:1.点击处无雷;2.点击处的周围均无雷,即get_mine_num()函数返回0;3.点击处周围的八个位置的其中一个位置的周围也存在都无雷的情况;针对于该功能的实现,将结合下面代码进行说明:

void expand_Board(char arr[ROWS][COLS], char arr1[ROWS][COLS], int x, int y)
{
	if (x < 1 || x >= ROWS || y < 1 || y >= COLS || arr1[x][y] != '*')
	{
		return;
	}

	arr1[x][y] = get_mine_num(arr, x, y) + '0';

	if (arr1[x][y] == '0')
	{
		for (int i = x - 1; i <= x + 1; i++)
		{
			for (int j = y - 1; j <= y + 1; j++)
			{
				if (i != x || j != y)
				{
					expand_Board(arr, arr1, i, j);
				}
			}
		}
	}
}

 因为我们需要不断的判断输入坐标周围位置的周围位置是否有雷,所以此功能采用函数递归的方式,不断去调用函数本身来实现无雷位置的拓展(空白位置用‘0’来表示)。

  函数递归需要设置一个终止条件,否则会陷入死循环,所以该程序设置if语句来判断递归时传递给函数x,y坐标是否满足数组越界,若满足,则return返回。

  arr1[x][y] = get_mine_num(arr, x, y) + '0';该语句与小功能一的字符类型转整数类型相同,这是通过返回的整数类型加上‘0’,转换为字符类型

小功能三:标记雷的功能

  当用户判断某一位置是雷,则可以通过输入该坐标将该位置用另一种方式进行显示,来进行标识。在此次程序中采用‘#’作为标记雷时的标志。

综上:对所有小功能进行整合得到扫雷功能的实现

  具体代码如下:

void fine_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int num = 0;
	int count = row * col - LEI_NUM;
	while (count)
	{
		printf("请选择你的操作:\n");
		printf("**** 1.扫雷 ****\n");
		printf("**** 0.标记雷 ****\n");
		scanf("%d", &num);
		switch (num)
		{
		case 1:
			printf("请输入你的坐标\n");
			scanf("%d%d", &x, &y);
			if (x > row || x<1 || y>col || y < 1)
			{
				printf("输入有误,请重新输入\n");
			}
			else
			{
				if (arr[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					return;
				}
				else
				{
					if (arr1[x][y] == '*')
					{
						int ret = get_mine_num(arr, x, y);
						if (ret)
						{
							arr1[x][y] = ret + '0';
							count--;
							Display_Board(arr1, ROW, COL);
						}
						else
						{
							expand_Board(arr, arr1, x, y);
							Display_Board(arr1, ROW, COL);
						}
					}
					else
					{
						printf("该坐标已经被搜寻过\n");
					}
				}
			}
			break;
		case 0:
			printf("请输入雷的坐标\n");
			scanf("%d%d", &x, &y);
			arr1[x][y] = '#';
			Display_Board(arr1, ROW, COL);
			break;
		default:
			printf("输入错误,请重新输入");
			break;
		}

	}
	if (count == 0)
	{
		printf("恭喜你,扫雷成功\n");
	}
}

   补充:

1.在上述代码中,扫雷和标记雷是并列关系,通过switch-case语句来实现。

2.通过count变量来表示雷的数量,当检测到雷时,count会减一,当count == 0时,则游戏成功结束。

3.该段代码中也加入输入坐标大小的判断语句,防止发生数组越界的错误。

附录:

game()函数的具体实现:

void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	Init_Board(mine, ROWS, COLS, '0');
	Init_Board(show, ROWS, COLS, '*');

	set_mine(mine,ROW,COL);

	Display_Board(mine, ROW, COL);
	printf("------扫雷游戏------\n");
	Display_Board(show, ROW, COL);

	fine_mine(mine, show, ROW, COL);
}

 头文件:

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


#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define LEI_NUM 10


void Menu();
void Init_Board(char arr[ROWS][COLS], int rows, int cols, char ch);
void Display_Board(char arr[ROWS][COLS], int row, int col);
void set_mine(char arr[ROWS][COLS], int row, int col);
void fine_mine(char arr[ROWS][COLS], char arr1[ROWS][COLS], int row, int col);
void expand_Board(char arr[ROWS][COLS], char arr1[ROWS][COLS], int x, int y);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值