【c语言实战项目】扫雷游戏


前言

基于上一次制作的c语言小游戏——三子棋,这次是一个更难一点的游戏:扫雷。其中运用到了c语言初级阶段几乎所有内容且难度有所提升,让我们一起来研究一下如何用c语言实现扫雷游戏逻辑。


一、扫雷游戏怎么玩?

众所周知,扫雷是在早期的windows系统中就存在的经典解密小游戏,其玩法也是十分有趣,在9*9的方形格子里防置有多个“ 炸弹 ”,鼠标点击某个格子,若此处就是炸弹埋放点,那么游戏就会失败,若不是,则会显示数字,这个数字代表了在离这个格子最近的一圈八个格子内有几个炸弹。
例如:
在这里插入图片描述
这个就代表在所选格子周围有两个炸弹。

二、如何用代码实现?

1. 打印游戏界面

由于玩游戏的过程和do while语句执行过程更贴近(直接执行,一轮结束后再来询问是否进行下一轮),所以我们选择do while语句作为游戏循环框架。


int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:_\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}

	} while (input);//0直接跳出,1继续玩。
	return 0;
}

运行结果正确
在这里插入图片描述

2.扫雷游戏逻辑

分析:根据经验我们知道扫雷类似于三子棋,也需要类似的 “ 二维数组棋盘 ” ,和三子棋有所不同的是,制作扫雷游戏主要有两个步骤:
1.布置雷——存放雷的棋盘
2.排查雷——排查雷的棋盘

既然是布置雷,那么就需要一个存放雷的棋盘,并且要为布置雷的地方做好标记,如果是用 1 表示有雷 0 表示没有雷,那么当玩游戏时,选中一处刚好它是没有雷的,但是它的周围是有一颗雷的,此时,用 1 作为雷的标记点就出现了第一个问题——这个 1 到底是雷还是周围雷的信息?

解决办法:

再给一个二维数组,一个数组用来存放布置好雷的信息(有雷放入字符 1 ,没有雷,放置字符 * 达到视觉上的遮盖效果);另外一个数组存放排查雷周围的信息(使用字符0-8)。为了保持两数组相同,都选择char型。

此时出现了第二个问题:如果刚好是最边上的不是雷呢?排查雷的信息按照原来的方式会出现【越界访问现象】
在这里插入图片描述
解决办法:
为了能统一排查雷的计算方法,应当将“ 棋盘 ”扩大两行两列,例如下图 9*9的数组扩大至11 * 11,这样子,如果访问9 * 9内任意一个元素,都可以利用扩大的数组循环访问得到它周围一圈雷的信息,
在这里插入图片描述

3.数组初始化

game.h:

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

//数组初始化
InitBoard(char board[ROWS][COLS], int rows, int cols,char set);

game.c:

InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

test.c:

//扫雷游戏框架
void game()
{
	char mine[ROWS][COLS];//存放布置雷的信息
	char show[ROWS][COLS];//存放周围雷的信息
	InitBoard(mine, ROWS, COLS,'0');//全都初始化为‘*’
	InitBoard(show, ROWS, COLS,'*');//全都初始化为‘0’
这里用了一个比较明智的写法是要初始化的字符‘0’和‘*’,也当作参数传给
了函数,这种写法的目的是为了在实现初始化的函数中只需要设置一次即可
实现对两个数组的初始化。详见game.c
}

4.打印扫雷棋盘

上面我们提到过为了优化排查边缘雷周围信息的问题,我们将 9 * 9的棋盘扩大至 11 * 11,所以,在这里要注意的是【打印棋盘】只需要打印里面的 9 * 9 ,外面一圈并不需要打印出来给用户看到。但是我们本来就是在 11 * 11 的数组上操作,所以传的参数格式才为:

	DisplayBoard(mine, ROW, COL);//只需要打印中间9*9的
	DisplayBoard(show, ROW, COL);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//数组仍是11*11所以传参ROWS COLS
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("-----扫雷游戏------\n");
	int i = 0;
	for (i = 0; i <= col; i++)//打印行号
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);//打印列号
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-----扫雷游戏------\n");

}

5.布置雷

	SetMine(mine, ROW, COL);//在11*11的棋盘上布置雷,mine传参ROWS COLS
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = 10;//10个雷
	while (count)//放10次,count为0时全部布置完,刚好结束while循环
	{
		//随即下标
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

设置随机数起点:

	srand((unsigned int)time(NULL));

6.排查雷

要在mine中排查,排查后的信息放在show中,然后打印出来。

如何排查?
1.输入排查的坐标
2.检查坐标是不是雷(是雷——炸死,游戏结束;不是雷——统计坐标周围有几个雷,并将结果存放到show中的对应坐标上,游戏继续)

//排查雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	printf("请输入要排查雷的坐标:_\n");
	int x = 0;
	int y = 0;
	int num = 0;
	while (num<=row*col-10)
	{
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//先判断坐标合法性
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,炸死了\n");
				DisplayBoard(mine, ROW, COL);//打印失败棋盘
				break;
			}
			else
			{
				//不是雷,统计坐标周围有几个雷
				int count = get_mine(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);//显示排查出的信息
				num++;	
			}
		}
		else
			printf("坐标不合法,请重新输入\n");
	}
	if (num == row * col - 10)
	{
		printf("恭喜你!排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}

}

> 代码重点是:统计非雷处坐标周围雷的信息。
> **** 假设棋盘中任一坐标为x,y,那么它周围一圈的坐标可以表示为(x-1,y)
> (x-1,y-1(x-1,y+1)(x,y-1)(x,y+1)(x+1,y)(x+1,y-1)(x+1,y+1> 
> **** 而且棋盘内容均为字符,此时mine布置雷的棋盘只有字符0 和字符1,而且
> 我们知道 0 -9对应 ASCII码差值均为 48,所以字符0减去字符0就等于数字0,字符
> 1减去字符0等于数字1> **** 所以,我们只要将八个坐标之和减去8倍的字符0,通过得出的数字即可知道这一周有几个雷
> 
> int get_mine(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x - 1][y + 1] +
		mine[x][y - 1] +
		mine[x][y + 1] +
		mine[x + 1][y] +
		mine[x + 1][y - 1] +
		mine[x + 1][y + 1]) - 8 * '0';
}

三、总结

      至此,一个能够实现扫雷游戏基本逻辑的小游戏便制作完成了,当然c语言版的扫雷
  仍有较大的优化空间,如:用递归算法实现一定条件下展开一片无雷区、标记功能等
  ,后期再来补充。这次制作最主要的是要学会一个项目从无到有的搭建过程,同时注
  意写代码时函数传参问题,除此之外,整个游戏大量使用字符,要注意数字和字符相
  互区别。
      本次c语言制作扫雷游戏就介绍到这里,如有不足,欢迎批评指正。
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值