C语言实现扫雷游戏

扫雷游戏是在固定区域随机内存在一定数量的地雷,将除去地雷的区域全部扫开就算胜利,选择扫去区域为地雷被炸飞,游戏失败。

下面开始提取关键信息。

(一)游戏设定:

1.扫雷游戏应当有开始和结束的选项。

2.进入扫雷游戏后需要画出雷区范围。

3.选择需要扫描的区域。如果没雷,扫描区域要显示周围八个区域的地雷数;如果有雷,被炸飞,游戏结束;如果所有没雷的区域都被扫描完,玩家胜利,游戏结束。

4.游戏结束后,应当返回初始界面,重新开始选择开始或者结束游戏。

(二)游戏梳理

1.游戏开始界面选择

游戏每一次结束后都会返回初始界面,可以通过while()语句构成一个循环,玩家输入“0”才会退出循环结束游戏。初始界面需要写个目录函数来明确选项,让玩家明白游戏进程位置和后续选择的操作,给予玩家更好的游戏体验。通过scanf()获取输入数值,通过将输入数值来判断玩家选择。

//test.c
#include<stdio.h>

void menu()//目录函数
{
	printf("***********************\n");
	printf("******1.开始游戏*******\n");
	printf("******0.结束游戏*******\n");
	printf("***********************\n");
}
void game()//游戏函数
{
    printf("进入游戏\n");
}

int main()
{
	int input = 1;
	while (input)
	{
		menu();//目录,显示选项
		printf("请选择:>");
		scanf("%d", &input);//获取输入数值
		switch(input)
		{
		case 0:
			printf("游戏结束\n");
			break;
		case 1:
			game();//游戏函数
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	}
	
	return 0;
}

为了直观简洁,我们可以将input变量传入switch()语句中,通过input数值执行对应语句。如此这般,初始界面就完成了。

接下来对上面功能进行测试:

db88524ceeda4665bc3afef52e4b9aeb.png

经过初步测试,代码可以正常运行。

2.扫雷游戏实现

扫雷游戏是在一个x行y列的矩形区域内进行游戏。因此,我们需要先建立一个矩形雷区,可以通过创建一个二维数组来建立雷区,数值为0表示没雷,数值为1表示存在地雷。可以设定地雷数为10颗,然后随机在雷区内生成10颗地雷。然后将雷区显示出来,看雷区是否存在10个地雷。

关键步骤:

(1)建立一个二维数组作为雷区

(2)将雷区全部赋值为'0',表示将雷区全部清理为无雷区域

(3)在无雷区域内随机存放10颗雷

(4)显示雷区

代码实现:

//test.c
#include"game.h"

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

void game()
{
	char mine[ROW][COL] = { '0' };
	Board_Init(mine, ROW, COL, '0');//区域初始化函数,将雷区全部赋值为'0'
	Mine_Init(mine, ROW, COL);//雷区初始化函数,将雷区随机生成指定的地雷数
	Display(mine, ROW, COL);//显示函数,将雷区情况显示出来


}

int main()
{
	int input = 1;
	srand((unsigned int)time(NULL));//rand函数想要随机生成数字,想要调用srand函数。
	while (input)
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("游戏结束\n");
			break;
		case 1:
			game();
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	}

	return 0;
}
//game.c
#include"game.h"

void Board_Init(char board[ROW][COL], int row, int col, char a)//board为初始化区域的数组,row为行数,col为列数,a为数组需要初始化的内容
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = a;
		}
	}

}

void Mine_Init(char mine[ROW][COL], int row, int col)//mine为雷区函数,row为行,col为列
{
	int count = COUNT;//地雷数
	while (count > 0)
	{
		int x = (rand() % row) + 1;//生成地雷的行数的随机数
		int y = (rand() % col) + 1;//生成地雷的列数的随机数
        //rand()函数生成随机数的范围为0-32767,如随机生成1-10,需要余上10,可得到数字(0-9),再加上1为(1-10)
		if (mine[x][y] == '0')//判断生成的地雷区域是否已经存在地雷,没有才进入
		{
			count--;
			mine[x][y] = '1';//在生成地雷区域内放置地雷
		}
	}
}

void Display(char board[ROW][COL], int row, int col)//board为需要显示区域的数组,row为行数,col为列数
{
	printf("**********扫雷*********\n");
	for (int i = 0; i < row + 1; i++)
	{
		printf("%d ", i);//显示该区域的列数
	}
	printf("\n");
	for (int i = 0; i < row; i++)
	{
		printf("%d ", i + 1);//显示该区域的行数
		for (int j = 0; j < col; j++)
		{
			printf("%c ", board[i][j]);//打印该区域内的数值
		}
		if (i <= row)
		{
			printf("\n");
		}
	}
	printf("**********扫雷*********\n");
}
//game.h
#pragma once

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

#define ROW 9//行数
#define COL 9//列数
#define COUNT 10//地雷数

void Board_Init(char board[ROW][COL], int row, int col, char a);//区域初始化函数
void Mine_Init(char mine[ROW][COL], int row, int col);//雷区初始化函数
void Display(char board[ROW][COL], int row, int col);//显示函数

为了方便阅读代码,新创建了game.c和game.h文件。

我们将扫雷游戏过程中的函数声明放进game.h文件中,将函数的具体实现放在game.c文件中。将需要通过#define定义的常量和扫雷游戏调用的库都放在game.h文件中声明和调用。test.c和game.c文件直接调用game.h文件就可以使用这些内容,十分方便。

通过#define来定义行数、列数和地雷数,是为了方便后续的调整。比如说,你有一天突然想扩大或者缩小雷区或者改变地雷数量,就可以直接在#define定义中修改,不需要到每个用过这些数值的函数里去修改,方便简洁且避免漏存在修改数值的行为。

在main()函数中新加入了一条srand((unsigned int)time(NULL));程序。srand函数内输入的数如果不是会变化的值,那么rand函数生成的随机数在每次重新进入程序都是一样的,因此需要在srand函数中放入时间戳,时间戳是随时间变化的,符合要求。time函数是用来生成时间戳的数值,返回类型是size_t,传入参数为指针类型,不知道用NULL(空指针)传入即可。srand()函数传入参数的类型为unsigned int,因此我们需要将time()函数的返回类型强制转换为unsigned int。

代码实现效果:

fd734d5045684c23bbe62e243f8fe541.png

 可以看出确实随机生成了10颗地雷。但是地雷不可以直接被看到,因此我们需要再创建一个显示数组。在游戏刚开始时,玩家看到雷区内全都是未知数,选择扫描后才会显示周围地雷数或者被地雷炸飞。可以将显示数组内全部赋值为'*'。为了方便计算被扫描后的区域显示周围的地雷数,我们将在原先的地雷数组上增加上下两行和左右两列。如游戏中地雷区域为9行9列,在代码定义地雷数组为11行11列,上下两行和左右两列不会在游戏中显示,地雷也只会放置在游戏显示的9行9列地雷区域中。代码中定义的显示数组和地雷数组同为11行11列。

改变点:

(1)雷区扩大两行两列

(2)地雷只会在除了雷区上下两行和左右两列的区域中放置(如11行11列的雷区,行为0-10,列为0-10的区域,地雷只会在行1-9和列1-9的区域中放置)

在扫雷游戏中,给玩家展现雷区内未知的样子。因此,在游戏结束之前只可以展示显示数组中的内容。玩家在没扫到雷区前会反复进行扫雷操作,因此,我们可以写个循环语句,只有被地雷炸飞或者将全部没雷的区域扫干净时,才可以跳出循环。在循环内写一个判断玩家胜利的函数,一旦成立就跳出循环。

核心步骤:

(1)建立一个二维数组作为雷区展示数组

(2)将雷区展示数组全部赋值为'*',表示区域内容未知

(3)显示雷区的展示数组

(4)搭建一个循环语句

(5)在循环语句中写一个玩家操作函数,可选择的区域只能在雷区显示数组去掉两行两列的范围内,且该区域没有被扫描过

(6)在循环语句中,玩家选择后就显示玩家扫描过的区域

(7)在循环语句中,判断玩家成功扫描完雷区或者被地雷炸飞才能退出循环

代码实现:

//test.c
#include"game.h"

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

void game()
{
	char mine[ROWS][COLS] = { '0' };//雷区数组
	char show[ROWS][COLS] = { '0' };//雷区展示数组
	Board_Init(mine, ROWS, COLS, '0');//雷区数组全部赋值为"0"
	Board_Init(show, ROWS, COLS, '*');//雷区展示数组全部赋值为"*"
	Mine_Init(mine, ROW, COL);//在雷区中随机埋指定数量的地雷
	Display(show, ROW, COL);//显示雷区展示数组内容
	int ret = 1;
	while (ret)
	{
		if (player(mine, show, ROW, COL) == 0)//执行玩家函数,并判断是否扫到雷,扫到雷返回0,等式成立
		{
			break;//跳出循环
		}
		Display(show, ROW, COL);
		ret = Is_win(show, ROW, COL);//雷区全部扫描完返回0,当0传入下一次while(ret)循环中会结束
	}

	if (0 == ret)//游戏成功时,ret会对于0,等式才会成立
	{
		printf("恭喜你排雷成功\n");
	}
}

int main()
{
	int input = 1;
	srand((unsigned int)time(NULL));
	while (input)
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch(input)
		{
		case 0:
			printf("游戏结束\n");
			break;
		case 1:
			game();
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	}
	
	return 0;
}
//game.c
#include"game.h"

void Board_Init(char board[ROWS][COLS], int rows, int cols, char a)//board为初始化区域的数组,row为行数,col为列数,a为数组需要初始化的内容
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = a;
		}
	}

}

void Mine_Init(char mine[ROWS][COLS], int row, int col)//mine为雷区函数,row为行,col为列
{
	int count = COUNT;//地雷数
	while (count > 0)
	{
		int x = (rand() % row) + 1;//生成地雷的行数的随机数
		int y = (rand() % col) + 1;//生成地雷的列数的随机数
		//rand()函数生成随机数的范围为0-32767,如随机生成1-10,需要余上10,可得到数字(0-9),再加上1为(1-10)
		if (mine[x][y] == '0')//判断生成的地雷区域是否已经存在地雷,没有才进入
		{
			count--;
			mine[x][y] = '1';//在生成地雷区域内放置地雷
		}
	}
}

void Display(char board[ROWS][COLS], int row, int col)//board为需要显示区域的数组,row为行数,col为列数
{
	printf("**********扫雷*********\n");
	for (int i = 0; i < row + 1; i++)
	{
		printf("%d ", i);//显示该区域的列数
	}
	printf("\n");
	for (int i = 1; i < row + 1; i++)
	{
		printf("%d ", i);//显示该区域的行数
		for (int j = 1; j < col + 1; j++)
		{
			printf("%c ", board[i][j]);//打印该区域内的数值
		}
		if (i <= row)
		{
			printf("\n");
		}
	}
	printf("**********扫雷*********\n");
}


int player(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//mine为雷区数组,show为雷区展示数组,row为行,col为列
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请选择:>");
		scanf("%d %d", &x, &y);//玩家输入需要扫描的行x列y
		if (x > 0 && y > 0 && x <= row && y <= col && show[x][y] == '*')//判断输入位置是否在规定区域内且未被扫描过
		{
			if (mine[x][y] != '1')//如果该区域不存在地雷
			{
				for (int i = x - 1; i <= x + 1; i++)//
				{
					for (int j = y - 1; j <= y + 1; j++)
					{
						show[x][y] += (mine[i][j] - '0');//将输入位置周围区域加自己总共9个区域遍历并计算地雷数量
					}
				}
				show[x][y] = show[x][y] - '*' + '0';//由于show原先存储内容为'*’,因此需要减去一个 '*',字符'0'加上周围地雷数会等于字符地雷数,如1就会是字符'1'
				return 1;//返回1表示没扫到地雷
			}
			else//扫到地雷
			{
				printf("被雷炸,游戏结束\n");
				Display(mine, ROW, COL);//游戏结束前,显示一下全部的地雷分布
				return 0;//扫到地雷返回0
			}
		}
		else
		{
			printf("输入坐标错误,请重新选择\n");
		}
	}

}

int Is_win(char show[ROWS][COLS], int row, int col)
{
	int count = 0;
	for (int i = 1; i < row + 1; i++)
	{
		for (int j = 1; j < col + 1; j++)
		{
			if (show[i][j] == '*')
			{
				count++;
				if (count > COUNT)
				{
					return 1;//区域内仍然有无地雷区域未扫描,返回1表示游戏继续
				}
			}
		}
	}
	return 0;//返回0表示区域内无地雷区域全部扫描完,游戏胜利
}
//game.h
#pragma once

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

#define ROW 9//玩家操作的行数
#define COL 9//玩家操作的列数
#define ROWS ROW + 2//实际定义雷区的行数
#define COLS COL + 2//实际定义雷区的列数
#define COUNT 10

void Board_Init(char board[ROWS][COLS], int rows, int cols,char a);//区域初始化函数
void Mine_Init(char mine[ROWS][COLS], int row, int col);//雷区初始化函数
void Display(char board[ROWS][COLS], int row, int col);//显示函数
int player(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//玩家操作函数
int Is_win(char show[ROWS][COLS], int row, int col);//判断玩家胜利函数

为了方便测试我将地雷数设置为80个,且显示地雷分布情况。

e6a36e69ec1e45ee861548442116fac3.png

 62fdc7b433d148aaa0b03225dab87e25.png

58260db115b44c5f8f78f1f29b343a30.png 

aab5b78e6bc94f96a3b2525a1a0d0d7a.png 

 可以看出游戏可以正常运行。

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值