C语言扫雷游戏(实现自定义大小,雷的标记,展开一片)

扫雷是经典游戏之一,现在我们用C语言实现一个简易版的扫雷游戏,这需要用到循环、数组、函数等多方面的知识。

首先,我们需要一个棋盘                              

在代码中,就用*来代替棋盘上的格子。如下图就是一个9*9的棋盘,这用可以用字符类型的二维数组储存,用两个for循环进行输出。

第二步,就是布置地雷,由于地雷随机生成,此处必须要用到随机数生成srand和rand。另外,我们可以将地雷的位置设置为‘1’,安全的位置设置为‘0’,但是它们的分布不能让玩家直接看到,因此还需要另一个二维数组 来存放地雷。

第三步,要对某一个位置进行排查地雷,可以通过输入坐标的方式,不过需要显示出雷的情况,即如果是雷,玩家就被“炸死”,不是雷,就显示周围九宫格内雷的数量。

若所有的地雷都被排查出来,游戏胜利。

以上是基本的思路,但实际的实现还有很多问题存在,我们同时注意用函数简化过程。

游戏的开始设置一个游戏菜单,在这个菜单上可以选择开始游戏,也可以退出,由于程序实现的操作过程与普通游戏过程不同,我们还可以设置一个规则界面用以查看。以下为menu函数结构

void menu()  //游戏菜单
{
	printf("*************************\n");
	printf("*******1->开始游戏*******\n");
	printf("*******2->规则***********\n");
	printf("*******0->退出***********\n");
	printf("*************************\n");
	printf("请输入:");
}

主函数部分,就要实现输入1开始游戏,输入2显示菜单,输入0退出。为了使游戏可以多次游玩,所以加上循环结构。

int input ;
do
{
	menu();
	scanf("%d", &input);
	if (input == 1)
		game();
	else if(input==2)
	{
		//说明部分
	}
	else if (input == 0)
	{
		printf("已退出\n");
		return 0;
	}

	else
		printf("输入错误,重新选择\n");
} while (input);

接下来是程序的核心部分,实现game函数。首先是设置棋盘,我们同时希望自定义棋盘大小和地雷的数量,这里只需设置一个较大的数组,我们采用20*20的上线,输入一个num代表行、列数(这里也可以分别设置行、列数,我们直接统一为正方形棋盘,并没有多大的区别),地雷可以在这个范围内任意,但输入错误就改为默认值(这里的上线和错误输入时的默认值都可以自行设定)。由此设置两个数组:bomb用以存放地雷,show用以显示给玩家。

int num,count;
printf("请输入难度数值n(建议小于18),将出现n*n的棋盘\n");
scanf("%d", &num);
if(num>=18)
{
	printf("过大了,默认为9\n");
	num = 9;
}
printf("请输入想要设置的地雷数量(>0):\n");
scanf("%d", &count);
if (count <= 0||count>num*num)
{
	printf("错误!默认为n+1\n");
	count =  num+1;
}
char bomb[20][20];
char show[20][20];

下一步则是数组的初始化,将bomb先全部放‘0’,show全部放‘ * ’。这需要编写initboard函数

initboard(bomb, num, '0');
initboard(show, num, '*');
void initboard(char bomb[][20],int num,char set)//将棋盘初始化
{
	for (int i = 0; i < num + 2; i++)
		for (int j = 0; j < num + 2; j++)
			bomb[i][j] = set;
}

紧接着将show展示出来,编写一个showboard函数,这个函数将反复用到,同时我们将行列序号一并打出,方便之后位置的输入。

void showboard(char a[][20], int num)//打印棋盘
{
	int i, j;
	printf("-------------------------------\n");
	printf(" ");
	for(i=0;i<=num;i++)  //先进行列数的打印
		printf("%d ", i);
	printf("\n");
	for (i = 1; i <= num; i++)
	{
		if (i < 10)
		printf(" ");
		printf("%d ", i);   //打印行数
		for (j = 1; j <= num; j++)
		{
			printf("%c ", a[i][j]);
			if (j >= 10)  //当数量超过10时,需要多加空格保持排列整齐
				printf(" ");
		}
		printf("\n");
	}
	printf("-------------------------------\n");
}

下一步设置炸弹,通过随机数将不同位置的‘0’赋为‘1’

void setbomb(char bomb[][20], int num, int count)//设置雷
{
	srand((unsigned int)time(NULL));
	while (count>0)
	{
		int x = rand() % num + 1, y = rand() % num + 1; 
		if (bomb[x][y] == '0')  //只有为'0'的位置可以设置
		{
			bomb[x][y] = '1';  //每设置成功一个,雷的数量减一
			count--;
		}
	}
}

最后就是排查雷的部分,这一部分最复杂也最困难。我们首先需要实现两个操作,排查雷和标记雷,排中雷,则失败结束游戏,否则显示周围雷数,这就需要计算雷数的函数(注意字符与数的转换:‘1’-‘0’=1,‘2’-‘0’=2,以及1+‘0’=‘1’)

int amount(char bomb[][20], int x, int y)//计算周围雷的数量
{
	return bomb[x - 1][y - 1] + bomb[x][y - 1] + bomb[x + 1][y - 1] + bomb[x - 1][y] +
		bomb[x + 1][y] + bomb[x - 1][y + 1] + bomb[x][y + 1] + bomb[x + 1][y + 1]-8*'0';
}

然后将show数组中的相应位置替换

show[x][y] = amount(bomb, x, y) + '0';

接下来就有一个十分复杂的程序用以实现展开一片(这是在排查的位置为0时将周围的数一并显示,包括0以及边缘的数字,代码则在最后完整程序展示)

另一个标记雷的操作,是判断胜负的关键,我们同样输入坐标,将坐标为值变为‘#’(所有的改变都应该是在show数组上进行的),如果所有雷都标记成功,胜利,但若有错误的标记,判负,游戏中可以撤销标记,这里的实现只能是在对这一坐标执行排查的操作 。排查和标记,我们都希望能多次操作,可以使用while(scanf...)的形式,那么这里需要设置循环停止的条件(也就是切换操作),我这里设置第一项为0时停止,只需要输入0 0,就可以切换操作(条件也可以另设,只需在“规则”界面说明)。每进行一次操作,就将show数组打印一次。

以下为整个程序的完整代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include <stdlib.h>
#include<time.h>
void menu()  //游戏菜单
{
	printf("*************************\n");
	printf("*******1->开始游戏*******\n");
	printf("*******2->规则***********\n");
	printf("*******0->退出***********\n");
	printf("*************************\n");
	printf("请输入:");
}
void initboard(char bomb[][20],int num,char set)//将棋盘初始化
{
	for (int i = 0; i < num + 2; i++)
		for (int j = 0; j < num + 2; j++)
			bomb[i][j] = set;
}
void showboard(char a[][20], int num)//打印棋盘
{
	int i, j;
	printf("-------------------------------\n");
	printf(" ");
	for(i=0;i<=num;i++)  //先进行列数的打印
		printf("%d ", i);
	printf("\n");
	for (i = 1; i <= num; i++)
	{
		if (i < 10)
		printf(" ");
		printf("%d ", i);   //打印行数
		for (j = 1; j <= num; j++)
		{
			printf("%c ", a[i][j]);
			if (j >= 10)  //当数量超过10时,需要多加空格保持排列整齐
				printf(" ");
		}
		printf("\n");
	}
	printf("-------------------------------\n");
}
void setbomb(char bomb[][20], int num, int count)//设置雷
{
	srand((unsigned int)time(NULL));
	while (count>0)
	{
		int x = rand() % num + 1, y = rand() % num + 1; 
		if (bomb[x][y] == '0')  //只有为'0'的位置可以设置
		{
			bomb[x][y] = '1';  //每设置成功一个,雷的数量减一
			count--;
		}
	}
}
int amount(char bomb[][20], int x, int y)//计算周围雷的数量
{
	return bomb[x - 1][y - 1] + bomb[x][y - 1] + bomb[x + 1][y - 1] + bomb[x - 1][y] +
		bomb[x + 1][y] + bomb[x - 1][y + 1] + bomb[x][y + 1] + bomb[x + 1][y + 1]-8*'0';
}
void checkbomb(char bomb[][20], char show[][20],int num,int count)//排查雷
{
	int x , y , i = 0, j = 0, mark = 0;
	while (mark < count)
	{
		printf("请输入要排查的坐标行,列(输入0 0暂停):\n");
		while (scanf("%d %d", &x, &y))
		{
			if (x == 0)
				break;
			if (show[x][y] != '*' && show[x][y] != '#')
			{
				printf("此位置已被排查\n");
				continue;
			}
			if (x >= 1 && x <= num && y >= 1 && y <= num)
			{
				if (bomb[x][y] == '1')
				{
					printf("很遗憾,这是雷\n");
					printf("下面是雷的位置\n");
					showboard(bomb, num);
					printf("重新开始游戏\n");
					main();  //失败后,返回main函数
				}
				else
				{
					show[x][y] = amount(bomb, x, y) + '0';
					if (amount(bomb, x, y) == 0)  //成片出现0
					{
						int flag = 0, record = 0;//设置两个变量来记录
						while (1)
						{
							//这一循环历遍show数组,当某一’*‘与’0‘紧挨(即上下左右)
							//同时本身为应为’0‘,就应该显示出来
							for (i = 1; i <= num; i++)
								for (j = 1; j <= num; j++)
								{
									if (show[i][j] != '0' && amount(bomb, i, j) == 0)
									{
										if (show[i + 1][j] == '0' || show[i - 1][j] == '0'
											|| show[i][j + 1] == '0' || show[i][j - 1] == '0')
										{
											show[i][j] = '0';
											flag++;//为了全部排出,这一循环要一直进行
										}
									}
								}
							if (record < flag)
								record = flag;  //用record来跟踪flag,当flag没有再增加
							else                //说明没有新的’0‘出现,while循环停止
								break;
						}
						for (i = 0; i <= num; i++)  //0边缘的数
							for (j = 0; j <= num; j++)
							{
								if (show[i - 1][j - 1] == '0' || show[i][j - 1] == '0' ||
									show[i + 1][j - 1] == '0' || show[i - 1][j] == '0' ||
									show[i + 1][j] == '0' || show[i - 1][j + 1] == '0' ||
									show[i][j + 1] == '0' || show[i + 1][j + 1] == '0')
									show[i][j] = amount(bomb, i, j) + '0';
							}     //九宫格内出现0的位置都应该将数显示出来
					}
					showboard(show, num);
				}
			}
			else
				printf("没有这样的坐标,重新输入\n");
		}
		printf("请输入要标记为雷的坐标行,列(输入0 0暂停):\n");
		while (scanf("%d %d", &i, &j))
		{
			if (i == 0)
				break;
			if (show[i][j] != '*')
			{
				printf("此处不可进行该操作\n");
				continue;
			}
			if (i >= 1 && i <= num && j >= 1 && j <= num)
			{
				show[i][j] = '#';
				showboard(show, num);
				if (bomb[i][j] == '1')
					mark++;    //mark记录标记成功的数量
				if (mark == count)
				{
					int flag = 0;
					for (i = 1; i <= num; i++)
						for (j = 1; j <= num; j++)//对于错误的标记,在结束时赋为’!'
							if (show[i][j] == '#' && bomb[i][j] != '1')
							{
								bomb[i][j] = '!';
								flag = 1;
							}
					if (flag == 0)
					{
						printf("恭喜,你已排出所有雷!\n");
						showboard(bomb, num);
					}
					else
					{
						printf("存在错误的标记!\n");
						showboard(bomb, num);
					}
					break;
					}
				}
			else
				printf("坐标输入有误,重新输入\n");
		}
	}
}
void game()  //游戏实现
{
	int num,count;  //num为行和列,count是雷的数量
	printf("请输入难度数值n(建议小于18),将出现n*n的棋盘\n");
	scanf("%d", &num);
	if(num>=18)
	{
		printf("过大了,默认为9\n");
		num = 9;
	}
	printf("请输入想要设置的地雷数量(>0):\n");
	scanf("%d", &count);
	if (count <= 0||count>num*num)
	{
		printf("错误!默认为n+1\n");
		count =  num+1;
	}
	char bomb[20][20];
	char show[20][20];
	printf("   *********开始扫雷********\n");
	initboard(bomb, num, '0');
	initboard(show, num, '*');
	showboard(show,num);
	setbomb(bomb, num,count);
	checkbomb(bomb, show, num, count);
}
int main()
{
	int input ;
	do
	{
		menu();
		scanf("%d", &input);
		if (input == 1)
			game();
		else if(input==2)
		{
			printf("自定义棋盘大小后,通过输入坐标进行排查,若为雷游戏结束,展示雷的位置(棋盘中为1的位置),否则显示九宫格内的雷数\n");
			printf("之后输入坐标标记你认为是雷的位置,将暂时变为“#”\n");
			printf("注:以上两种操作均可多组输入,格式为\n1 1(操作第一行第一列)或2 3 4 5(分别操作第二行第三列和第四行第五列)\n");
			printf("暂停某一操作,需要输入0 0\n");
			printf("当标记完所有的雷,获胜,但若此时有标记错误的位置,判负,想要撤销标记,在排查步骤输入坐标即可\n");
		}
		else if (input == 0)
		{
			printf("已退出\n");
			return 0;
		}

		else
			printf("输入错误,重新选择\n");
	} while (input);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值