C语言扫雷(极简版精讲)

本文详细解析了C语言扫雷游戏的113行核心代码,涉及菜单、主函数、游戏函数等,重点讲解了如何使用二维数组、random函数结合时间戳生成随机雷的位置,以及关键函数如初始化、放置雷、扫描雷数和排雷的实现过程。
摘要由CSDN通过智能技术生成

目录

前言    

游戏规则

(扫雷游戏在线玩 - Minesweeper)

核心逻辑

深度剖析

  menu菜单函数

  main主函数

  game游戏函数

难点解答:数组多创建一圈

  init初始化函数

  put置雷函数

 难点解答:用rand函数和时间戳生成随机数

时间戳的概念

  Count扫描雷个数的函数

  display展示函数

  find排雷函数

完整代码

运行结果


前言       

        写这篇文章前呢,我先看了一下大佬们写的C语言扫雷代码,近300行的代码可以说是应有尽有。为了让这篇文章有些不同之处呢,我便偏向核心逻辑,选了113行的核心代码,将扫雷的核心部分剖析开来。希望此文章可以助你成功入门。

游戏规则

        (扫雷游戏在线玩 - Minesweeper

        键盘输入坐标,选择要排查的雷的坐标。如果是雷,游戏失败,反之格子里的数字表示该格子周围雷的个数,如:

     1d7be726484e4496a1b44c7364ac5326.png

最终我们将所有非雷的格子都排查完以后,可获得游戏胜利。

核心逻辑

  1. 我们创建两个二维数组,一个用于显示(即玩家看到的),一个用来存放雷的信息。
  2. 随机布置雷,放在存雷信息的数组。
  3. 排查用户输入的坐标,如果是雷则结束游戏,如果不是雷则扫描周围雷的个数,并在被排查的格子里显示出周围雷的个数,当用户排完后结束游戏。

深度剖析

  menu菜单函数

void menu()
{
	printf("       扫雷       \n");
	printf("    1.开始游戏    \n");
	printf("    2.退出游戏    \n");
}

  main主函数

main()
{
	int n=0;
	me:
	menu();//引用菜单函数
	scanf("%d", &n);
	if (1 == n)//反着写的话,如果不注意写成‘=’的话就会报错
	{
		game();//这个函数先不用管它,它里面引用了一系列函数,后面再讲
	}
	else if (2 == n)
	{
		printf("感谢游玩");
	}
	else
	{
		printf("输入不合法,请重新输入");
		goto me;
	}
}

  game游戏函数

        难点解答:数组多创建一圈

        讲一下为什么数组是11x11的二维数组,因为我们排查雷的时候要排查周围一圈的格子。我们在9x9的扫雷游戏上再扩一圈全部是非雷的格子,这样我们在排查到边或者角的格子时不用考虑超出问题,在输入坐标时也更为方便。

void game()
{
	char boom[11][11] = { '0'}, show[11][11] = {'0'};//创建两个数组
	init(show,'*');//init函数用来初始化,这里传什么则未排查的格子显示的就是什么
	init(boom, '0');//boom存放雷,‘0’为非雷,‘1’为雷,这里先清空雷
	put(boom);//用于放置雷的函数
	find(boom, show);//排雷函数
}

  init初始化函数

void init(char a[11][11],char set)
{
	for (int i = 0; i < 11; i++)
	{
		for (int j = 0; j < 11; j++)
		{
			a[i][j] = set;
		}
	}
}

  put置雷函数

        难点解答:用rand函数和时间戳生成随机数

        rand函数生成的是一个伪随机数,它会提前获取一个数,再将此数经过一个复杂算法,该算法每次调用时都会返回一个明显不相关的数字序列。而获取的数是由srand函数提供的,比如现在我们为srand函数传入数字1,如下图:

4249f0def068487f9d9c410b7f334f0e.png

        我们看似得到了一串随机的数字,但不信你去你电脑上运行一下,是不是和我的结果完全一样呢?因此,这是提前计算好的数字,并不能做到每时每刻都在变化。嗯?每时每刻变化的是什么?是时间啊!

        时间戳的概念

        时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。是每时每刻都在变化的。而我们要获取到时间戳也很简单,代码如下:

#include<stdio.h>
#include<time.h>//头文件
main()
{
	printf("%d",time(NULL));
}

        没错,我们将时间戳传进srand函数,那得到的就是真随机数了。 现在我们需要的是1~9的随机数作为x和y布置雷(最外圈不布置),那我们将数字取9的余数,得到0~8的随机数后加1即可。

void put(char a[11][11])
{
	srand((unsigned)time(NULL));
	for (int i = 1; i <= 10; i++)
	{
		int x = (rand() % 9)+1;
		int y = (rand() % 9)+1;
		if( '1'==a[x][y])//防止重复布置
		{
			i--;
		}
		a[x][y] ='1';
	}
}

  Count扫描雷个数的函数

        因为我们的‘1’表示雷,‘0’表示非雷,故将格子周围存雷数组里的数字加起来,得到的就是周围雷的个数,再将其传到show数组即可。(boom里的是字符型,别忘了转为整形)

int Count(char boom[11][11], int x, int y)
{
	return boom[x-1][y]+boom[x-1][y-1]+boom[x][y-1]+
		boom[x+1][y-1]+boom[x+1][y]+boom[x+1][y+1] +
		boom[x][y+1]+boom[x-1][y+1]-8*'0';
}

  display展示函数

        此函数用于将二维数组里9x9的部分输出

void display(char a[11][11])
{
	for (int i = 0; i <= 9; i++)
	{
		printf("%d ", i);//打印列号
	}
	printf("\n");
	for (int i = 1; i < 10; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (int j = 1; j < 10; j++)
		{
			printf("%c ", a[i][j]);//打印行号
		}
		printf("\n");
	}
}

  find排雷函数

void find(char boom[11][11],char show[11][11])
{
	int i = 0,x = 0, y = 0,count=0;
	display(show);
	printf("请输入要排查的坐标\n");
	while (i < 71)//当排查了71次还未被炸死即胜利(9*9-10)
	{
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
		{
			if (boom[x][y] == '1')
			{
				printf("很遗憾,排雷失败\n");
				display(boom);//排雷失败即展示放置雷的函数
				break;
			}
			else
			{
				system("CLS");//清屏函数,头文件为stdlib
				show[x][y] = Count(boom, x, y)+'0';//整形转字符型
				display(show);
				printf("请输入要排查的坐标\n");
				i++;
			}
		}
	}
}

完整代码

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void menu()
{
	printf("       扫雷       \n");
	printf("    1.开始游戏    \n");
	printf("    2.退出游戏    \n");
}
void init(char a[11][11],char set)
{
	for (int i = 0; i < 11; i++)
	{
		for (int j = 0; j < 11; j++)
		{
			a[i][j] = set;
		}
	}
}
void put(char a[11][11])
{
	srand((unsigned)time(NULL));
	for (int i = 1; i <= 10; i++)
	{
		int x = (rand() % 9)+1;
		int y = (rand() % 9)+1;
		if( '1'==a[x][y])
		{
			i--;
		}
		a[x][y] ='1';
	}
}
int Count(char boom[11][11], int x, int y)
{
	return boom[x-1][y]+boom[x-1][y-1]+boom[x][y-1]+
		boom[x+1][y-1]+boom[x+1][y]+boom[x+1][y+1] +
		boom[x][y+1]+boom[x-1][y+1]-8*'0';
}
void display(char a[11][11])
{
	for (int i = 0; i <= 9; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i < 10; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (int j = 1; j < 10; j++)
		{
			printf("%c ", a[i][j]);
		}
		printf("\n");
	}
}
void find(char boom[11][11],char show[11][11])
{
	int i = 0,x = 0, y = 0,count=0;
	display(show);
	printf("请输入要排查的坐标\n");
	while (i < 71)
	{
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
		{
			if (boom[x][y] == '1')
			{
				printf("很遗憾,排雷失败\n");
				display(boom);
				break;
			}
			else
			{
				system("CLS");
				show[x][y] = Count(boom, x, y)+'0';
				display(show);
				printf("请输入要排查的坐标\n");
				i++;
			}
		}
	}
}
void game()
{
	char boom[11][11] = { '0'}, show[11][11] = {'0'};
	init(show,'*');
	init(boom, '0');
	put(boom);
	find(boom, show);
}
main()
{
	int n=0;
	me:
	menu();
	scanf("%d", &n);
	if (1 == n)
	{
		game();
	}
	else if (2 == n)
	{
		printf("感谢游玩");
	}
	else
	{
		printf("输入不合法,请重新输入");
		goto me;
	}
}

运行结果

968754fa61e9428386ab8bc00e130769.png

排雷失败

 5369ca719eb24bbd9aad836c9ad808ba.png

排雷成功 

6bc74eb961e5485690ed62540c97ee1b.png

        “纸上得来终觉浅,绝知此事要躬行”,学会了就自己动手试试吧!

————(如有问题,欢迎评论区提问)————

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bit_Le

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值