扫雷游戏(9*9)

一、构思

我想做一个9*9的10雷版扫雷游戏。类似这样的。

那么展示出来的就是9*9的数组。

扫雷游戏的逻辑是这样的:确定坐标,如果不是雷,显示周围8个坐标雷的总数,如果是雷,那么游戏结束

难点难于如何设计确定坐标以及怎么统计雷的个数。同时,由于在统计边缘格子的时候,只有9*9的数组可能会发生数组越界。

如果用0和1来代表的话,0就表示不是雷,1表示是雷,那么在统计的时候,只需要把周围数字加一圈,有几个1就是几个雷。问题在于,数组是一组相同类型的元素组成的,而字符和数字不是相同类型的,不能放在同一个数组中。另外,当展示1的时候,显示出来的是这个位置是雷,还是周围雷的个数是雷,这可能会混淆。

所以我创建两个数组,一个用来放置雷,以及统计雷的个数,以0和1组成的数组。一个用来展示,以字符*组成的数组。两个数组大小一样。

别的想法:这个游戏,为了保持它的简洁性,我希望把函数的声明放在头文件,把函数的定义放在一个game.c文件,主函数放在play.c文件。

准备好这些,就可以开始动手了。

二、函数定义

2.1写出主函数的内容,大体的思路。

我想设计一个Print函数,它一开始就要打印。让我选择游戏开始还是退出。选择1,游戏开始,选择0,游戏退出。退出以后不再循环,游戏结束。选择1,则游戏继续。

因此,这里采用do while循环,条件写上 i 的值。和switch循环配套。选择0以后,退出游戏,但我想要它再显示一下进入游戏的界面,所以它要打印一下再退出。选择1,则开始游戏,写一个game函数,我需要game函数帮我实现玩游戏的功能。其他选择就是选择错误,就再输入一遍。

到这里我的主函数的内容就写完了。

#include "game.h" //包含一个头文件的内容,函数的声明
              //都在头文件中

int  main()
{
	int i = 0;
	do{   //使用do while循环,一开始就执行,输入i
		//当i==1时,继续玩,当i==0时,程序结束。
		Print();//定义一个函数,展示选择内容
		printf("请输入数字:>>");
		scanf("%d", &i);
		switch (i)
		{
		case 0: Print();//选择0,打印了开局选择再结束进程
			break;
		case 1: game();//选择1 玩游戏,写个函数定义一下游戏内容
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (i);

	return 0;
}

2.2函数的定义

2.2.1实现游戏开场选择设置

接下来,我要在另一个文件game.c来写各函数的定义。

进行开局打印的Print函数

游戏开场的设置如下图所示,它只需要展示下列内容,不需要返回值,所以用void。1是游戏开始,0是退出游戏。

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

}

2.2.2实现game函数

首先我要把game函数写出来,然后再往里面加内容。

前面分析过了,我用9*9的数组可能会导致数组越界,所以我要创建一个11*11的二维数组,此时,雷所展示的区域就在中间的9*9的位置。这样在统计边缘位置的雷的个数时,就不会发生数组越界的情况了。

arr1的数组全是字符0,待会我要往里面放雷,这个是白板,里面放着谜底(雷布置的区域等等)。arr2的数组全是*,是黑板,它用来给我玩扫雷的。

void game()
{

	char arr1[11][11] = {'0'};
	char arr2[11][11] = {'*'};
	Boarddefine(arr1,arr2); //初始化棋盘
	/*Boardplay(arr1);*/ //打印棋盘  arr1的棋盘如果提前打印出来,字符就会发生改变
	                  //前面说了,arr1和arr2的排列一样第一行和第一列的字符都是
	                 //坐标,所以统计雷的时候,若提前打印了arr1,就会统计出错。
	                //真正玩的时候,只需要打印arr2就好。而这个打印棋盘的代码是给
	               //自己调试的时候用的。
	mineset(arr1);//放置雷
	printf("\n");
	Boardplay(arr2);  //展示arr2的暗棋盘。
	playgame(arr1,arr2);//扫雷
	
}

有了两个棋盘就要把里面放东西,一个放字符0,一个放字符*。写一个函数Boarddefine帮我初始化棋盘,参数是两个数组。

初始化完之后,下面可以再写个函数Boardplay(其实是boarddisplay,但当时忘了单词)打印一下两个棋盘的情况,是否有得到想要的效果。如果是的话,那么先把这行代码屏蔽,再往下走。不是的话,看一下自己哪个代码没写对,先修改再往下走。一边写一边调试。

我需要在白板的棋盘放置雷,写个函数mineset(arr1)帮我实现随机放置雷的功能。参数是arr1,因为我只需要在arr1放置雷就可以了。

放好雷以后,再用个换行功能。这样待会打印出来层次分明看的清楚。把刚刚需要的打印棋盘的函数放在这里。用来打印一下arr2的棋盘。打印出来是为了下一步进行玩游戏。展示给玩家没有被扫的雷的初始情况。

打印出来以后,就可以玩游戏了。具体的判断雷,游戏成功,游戏失败等内容,我也封装在一个叫playgame的函数里面,让它帮我实现玩游戏的功能。因为这一步要用到两个数组,所以传参是两个数组都传过去。

这样,我的game函数就写完了。接下来要填充刚才需要具体实现的内容。

2.2.3 填充函数具体内容

初始化棋盘函数
void Boarddefine(char arr1[11][11], char arr2[11][11])
{
	int a = 0, b = 0;
	for (a = 0; a < 11; a++)
	{
		for (b = 0; b < 11; b++)
		{
			arr1[a][b] = '0';  //arr1  全部都是0
			arr2[a][b] = '*';
		}
	}
}

我没有改变形参的名字。初始化棋盘代码没什么复杂的。学过二维数组都知道,需要注意的是给char类型的初始化要用单引号。

打印棋盘的函数
void Boardplay(char arr2[11][11])
{
	int i = 0, j = 0;
	for (i = 0; i < 10; i++)
	{

		for (j = 0; j < 10; j++)
		{                                 //展示棋盘 棋盘2 
			                             //虽然这个数组是11×11,但我只需要
			                            //9乘以9 第一行和第一列作为坐标
			if (0 == i && 0 == j)      //这样布置会导致数组arr1也是按照arr2的方式布置的
				arr2[i][j] = i+'0';
			else if(0==i)
				arr2[i][j] = j+'0';
			else if (0 == j)
				arr2[i][j] = i+'0';
			printf("%c ", arr2[i][j]);
			if (j == 9)
				printf("\n");
		}
	}
}

明确一下我玩游戏时,想见到的arr2(黑板数组)是这样的。它需要有坐标,这样我扫雷的时候直接输入坐标就可以。内容依然是9*9,但展示的数组是10*10.整个数组是11*11的大小,但我只需要展示出这10*10的内容就可以。由于这是个字符数组,所以,作为整型数字的i加上作为字符的0才等于作为字符的 i 。

布置雷的函数
void mineset(char arr1[][11])
{
	int i = 0, j = 0, m = 0;
	srand((int unsigned)time(NULL));
    while(m<10)                   //布置十个雷
	{
		int i = rand() % 9 + 1;   //把雷布置在 1-9的坐标上
		int j = rand() % 9 + 1;
		if (arr1[i][j] == '0')
		{                        //不能重复布置 
			arr1[i][j] = '1';
			m++;
		}
	}
}

引用随机数函数,随机布置十个雷。但是我只需要在1-9的位置放置雷就可以了。那是我们玩扫雷9*9的区域。只有当这个位置是‘0’的时候,代表它没有雷,那么放置雷‘1’,雷的数量+1。用m来计算雷的数量,如果是1的话,m是不变的。这样当m=10的时候,就可以跳出循环,也代表雷放置成功了。

放置成功以后,也可以再把打印棋盘的函数打印一下arr1看看是否是自己想要的。

玩扫雷的函数
void playgame(char arr1[][11],char arr2[][11]) //arr1和arr2的元素个数必须一致,不然
                                           //可能会出现坐标错误
{
	int i = 0, j = 0, n = 0;           //n的声明不能放在循环里面,不然只有n==1时才可以通关
	do {
		printf("请输入坐标:>>");
		scanf("%d %d", &i, &j);
		n++;
		if (arr1[i][j] == '1')
		{
			Boardplay(arr1);
			printf("很遗憾,你被炸死了\n");
			break;
		}
		else
		{
			int lei = countmine(arr1,i,j);//我需要一个把雷的数量统计一下的函数
			arr2[i][j] = lei + '0';//显示周围雷的个数  数字+字符等于字符
			Boardplay(arr2);//再展示一下现在的游戏进度
		}
		if (n == 71)
		{
			printf("恭喜你,游戏通关\n");//如果排了71个雷,说明已经成功了
			break;
		}
	} while (1);
}

仍然用do while循环,因为我希望上来就可以直接玩。前面打印出来了这个arr2,所以下面我要输入坐标。如果输入1 1,那么要判断这个位置是不是雷,如果是雷,那么游戏结束。如果不是雷,那么周围有几个雷。雷放在arr1里面,但arr2和arr1的大小坐标是一样的。所以arr2输入的坐标在arr1中也是有效的。

我们用arr1来判断雷。如果输入的坐标值是字符1,那么被炸死,如果不是1,那么统计周围雷的个数。统计完以后这个坐标要显示数字。我写一个函数countmine用来计算周围雷的个数,并把计算的结果返回到这个坐标。然后再展示剩下的扫雷情况。

当我扫完10个雷的时候,游戏通关。每输入一次坐标就相当于排雷一次,如果我能排雷71次,我就成功了。所以用n计数,每输一次坐标就+1.当然后期想玩点大的,71也可以改成变量的形式。这里要注意的是,变量的声明要在do while循环外面,不然每次进循环,n都会重新变为0,那么永远无法判定成功通过游戏了。

无论被炸死还是通过游戏,都要用break来跳出循环。回到选择1玩游戏还是0退出游戏的页面。

数雷的函数

下面只剩下数雷的函数,把周围坐标加一圈就可以了。这里要注意的只是字符减字符等于数字。减字符0的话,值不变。x是整型数字,而arr1的元素都是字符,所以这里要通过减字符0来得到数字,再返回。当然也可以创建一个字符变量,这样不用减字符0。返回以后直接把字符数值赋值在对应的arr2的坐标上就可以。

int countmine(char arr1[][11],int i, int j) //把周围雷的数量都加一下就知道了
{
	int x= arr1[i + 1][j + 1]-'0' + arr1[i + 1][j] - '0' + arr1[i + 1][j - 1] - '0' + arr1[i][j - 1] - '0' + arr1[i - 1][j - 1] - '0' + arr1[i - 1][j] - '0' + arr1[i - 1][j + 1] - '0' + arr1[i][j + 1] - '0';
	return x;
}

接下来再在game.h的头文件里面把需要声明的函数都声明一下,在其他两个文件中包含一下头文件就可以了。

三、运行

运行以后就是这样,可以玩通关了。游戏通关后,会再显示主页面功能给玩家选择继续游戏还是退出游戏。运行一次可以玩到腻。

ps:刚准备写的时候真没想到会写这么长。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值