扫雷实战训练(沙雕解析+全部源代码)

        前言(瞎逼逼

        先弟学c未半而中道实战,今扫雷三分,素来装(zhao)逼(da),此程(序)危只因之秋也。

        然……然则……算了,总之一句话,我也来凑个热闹,分享一些我的见解,或者说以我的视角,再说一遍这老掉牙,啊不,这经典的实战项目。

        面向编程的Ctrlcv工程师们,以及有需求的兄弟们可以移步gitee啦(源代码)

9b58ccc945df4d6f9822b7194e647617.jpg


前期准备

        一、游戏规则

        俗话说得好,知己知彼,方能百战百die(bushi)。

        如图所示,在我们进入游戏后,映入眼帘的便是那个一个个小白块,所组成的9*9的盘面,而再此之中又随机买了十颗地雷,当我们啊,一不小心的“踩”到了,boom!哦吼,死咯(小手一摊)。

        显然,通关条件便是绕开盘面上所有的雷,然后踩完所有的空白格,最后获得胜利。

  

dfaea950d0b44001b87f421342d28148.png63e38457da7541a29a229673aaccf10a.png

        二、分门别类

        分门别类,何为分门别类。你想想在我们日常生活当中,不同属性的房间承担着不同的功能,在厨房做饭,在卧室睡觉,在厕所解决三急,我们在大多数情况下,便会去相应的房间做相应的事,这样就不会出现吃喝拉撒睡都在卧室……当然,不是不行,只是“房子”还小的时候,还能凑合,房子慢慢变大的话,我们要干的事多了,难免不会出乱。

        这时候我们就应该对给类函数进行包装,变成一个个独立的.c文件,也就相当于个个功能属性不同的房间,分门别类。而且万丈高楼平地起,盖大楼从来不是一个人的事,分门别类后,我们要干什么,怎么干便愈发清晰明了。

        那么这时候聪明的孩子就会问啦,这是时候我们应该怎么办呢~

96e6e5bc49c4444b94e826ecdbb69b60.jpg

         诶!好吧,其实也没啥,只不过像平常一样,多造个.c文件,把要用的函数都塞进去。而且众所周知,有些东西在不是自己的,要用别人的东西的时候,我们是不是得说一声呀,因此我们还得整个.h文件。

2451d36752724b3f9c0eb363ab1680e3.png

        游戏搭建        

         一、搭建思路

e731b7905a38481ba39509c85255d319.jpg

         我的思路很简单,跟一般人也没什么区别,无外乎游戏开始,创建盘面,初始化盘面,布置雷,然后开始扫雷。   

        最后把雷扫了,要么把雷给踩了,而不变的便是游戏结束。

       

e72d310af99845c697087e2c6e72b1e5.png

         但倘若如此我也不必如此单领出来,主要是觉得这样的思路存在着些许问题,为什么是布置完雷,玩家才开始排查,这样会不会出现开局既踩雷的情况呢,我觉得并非不可能,而且真这么做了,玩家也遇到了,体验也不会好的吧。

        因此我做出了以下的些许调整,既在布置雷之前,就排一次雷,而且先不告诉玩家,在完成布置后,进行检测(地雷),返回一个数值,最后再告诉玩家,保证玩家永远不会第一步便踩雷。

f0819c25cda441f28b743d5abea090da.png

         二、基础框架

        这个嘛,很基础,想必大街也都会,而且大家多多少少都会有点自己的想法,所以我也就不过多讲述了,大家看一看就好了。

65edd95a0cf24414bee7ed3cdb842dfc.png
标题 csdn自带的代码框不是很好看,我就换成了我这个,有需要(这段代码)的朋友,可以直接去开头那部分找源代码标题

       

        三、框架主体

         这是我的代码,实现思路及过程,不过话说回来各位走过路过的大佬们,我想问你们件事,就是假如玩家在输入坐标时,输入了中文,对没错,中文,程序为什么没有像我设计的那样返回,而是无限的输出数字0。

6002439768ec4cf78864146cb522d04f.png

2b397258274a4aac80bd647d5e20e2f0.png

97cb275b26c344f0bde4805f23d668d2.png

ba8a7f7e62e243d6beb8e2856e040717.png

1、布置、初始化盘面

        (按照惯例我先把代码贴出来啦)

dced3c0f2e50473ca91c530d6c1a7227.png
函数的调用(main.c)标题

8ecec85d8c1c47c4b0d285219d9cbcbd.png
标题game.h 头文件的声明

b52c4046d0c2447298227e2eb4310c13.png
game.c的函数标题

        OK,兄弟们,来到最为关键的地方了,搭建游戏的核心,而按照我们一开始的思路,我们在开始后面,紧跟着的便是布置盘面了。

        诚然扫雷的盘面是一个9*9的盘面,我们固然可以直接整个arr[9][9]的二维数组,可是这样纯度,啊不,效率,啧,也不行……嗯,就是后续可操作空间变小了,按我那位不愿透露姓名的无中生同学的话来说,这么做无异于自断双腿,然后靠着双手爬过了终点,虽然是爬过了100米的赛道,但他没法跑的更远、更快。

        换而言之,后续要加入改难度的功能时,这无数的arr[9][9],就宛若💩山,不对,💩界珠穆朗玛峰! 所以我们将通过define的定义宏,将标识符同步替换成相应数字,到时候要做修改时,直接从这里该就好了。就比如这样。

     

b79789a0221f45fba77f37965681c00a.png

        然后就可以这样amazing啊,如此这般后续调整只需要简简单单调整一下row的数字,全局的数据因此而改变。 

        实现

a8456578ef6a434db32980f835e4fcae.png
某种意义上这是不是在数组里,塞入了变量了呢,哼,刚刚还在拒绝呢,这不很诚实的接受了嘛😏

         啊,这时眼力尖的兄弟就发现了:不是,哥们~你咋造两个数组啊~怎么还是11*11啊~

        ☝️ 🤓诶,有眼光 ,首先第一点,为啥是双数组,而非单数组

        你想想,地雷一般埋在哪里,地里是不是,我们一般走在哪里,地上是不是。假如说地雷丢在地上,怕是也没谁会踩上去的是吧,那是不是得做点伪装。

左边是实际埋雷的地方,右边则为玩家可看见的地方

        那么第二个问题就来了,为啥是11*11,而非9*9

        如图所示,假设红色即为我们点击的位置,橙色既为周围八个,1为地雷。而从图中我们可以轻易的看出位于左边的方框,有一个地雷,那么右边的呢。当然你可能会说这不一个地雷都没有吗,但按照左侧方框检测方式,检测周围八格时,你懂的,没有被定义、初始化的数,一般而言都是随机数,到时候玩家点击右侧时,就可能会看到,他点击方块旁边,看到有成千上万课地雷,保准一玩一个不吱声。

        当然你也可以说,制作不同的方法来检测嘛,起初我也是这么想的,麻烦不说,我的老师还说这是重复造轮子,是不可取的,是没有必要的。

        那么现在我们将在再回来看这图,现在在上下左右都加了一行一列,并把他们全都初始化了0,那么我们再回过头看我们刚刚点击的地方,是不是清晰明了了。

        啊……至于为啥要初始化字符0,说实话我忘了,反正老师说要这么做。

         2.踩雷(仅一次),查看雷

让玩家输入要排查的数字,要是数字超过数组范围,便让她重新输入一遍,若是没超出,就让一个@顶过去,等会布置雷时,就绕开他。

没啥好说的,简简单单的打印一遍

        3. 布置雷、计算雷

        布置

        1.简简单单,利用随机函数给xy一各个值,然后在对应arr[x][y]上由0变为1,即为埋地雷,刚才也说个要绕开嘛,所以凡是随机到了与@相同坐标时,重随一遍。
        2.而且别忘了时间函数,忘了的话这就不是随机了。 

        3.先前也说了我们在数组上下左右都加了一行,如果这里的xy不加一,就有可能出现雷埋墙里这种可能。

        计算

        

        这时候我们的雷区已经充满了0和1(地雷),你知道这说明什么,说明我的数组都是通(tong)讯(xing)录(lian),而且0超多,就说明我们可以进行计算啦。

        

         计算公式也很简单,就是简简单单将玩家所检测位置周围八个以此加上去,而详情在下图。

4.排查雷

源代码

#include "and.h"


void start()
{
	printf("****************************\n");
	printf("****************************\n");
	printf("*********   扫雷   *********\n");
	printf("*********1.开始游戏*********\n");
	printf("*********2.游戏设置*********\n");
	printf("*********3.结束游戏*********\n");
	printf("****************************\n");
	printf("****************************\n");
}

void set_up()
{
	printf("****************************\n");
}
//需求清单
	//1.难度选择
	//2.时间挑战
//还在做……

void game()
{
	printf("————游戏开始————\n");
	char mine[rows][lists] = { 0 };
	char land[rows][lists] = { 0 };
	init(mine, rows, lists, '0');//1.初始化雷区
	init(land, rows, lists, '#');//2.初始化游戏区
	look_over(land, row, list);//显示游玩区域
	int click1 = 0;
	int click2 = 0;
	do//防止程序出错的保险
	{
		printf("请选择你要排查的位置:");//输入排查部分
		scanf("%d%d", &click1, &click2);
	} while (click1 < 0 || click1 > row || click2 < 0 || click2 > list);
	land[click1][click2] = '@';//保证不会出现开局踩雷,或是凭空踩掉雷的保险
	place(mine, land, row, list);//布置雷,并避开@,确保不会凭空消失一个雷
	int  a = count(mine, click1, click2);//计算@旁边有多少颗雷
	land[click1][click2] = a + '0';	//并顶掉@
	look_over(land, row, list);//游戏初步开始
	check(mine, land, row, list);//排查雷
	look_over(mine, row, list);//游戏结算,给玩家看雷的位置
}
int main()
{
	srand((unsigned int)time(NULL));//时间函数
	int i = 0;
	do
	{	
		
		start();
		printf("请选择:");
		scanf("%d", &i);
		if (i == 1)//可以使用switch函数
		{
			game();
			printf("是否还有在来一局?\n");
			printf("1.是 2.否  >");
			scanf("%d", &i);
			if (i == 2)
			{
				break;
			}
		}
		else if (i == 2)
		{
			set_up();
			i = 0;
		}
		else if (i == 3)
		{
			break;
		}
		else
		{
			printf("选择错误\n");
			continue;
		}
	} while (i);
	printf("青山不改,绿水长流,拜拜了您内");
	return 0;
}

#define _CRT_SECURE_NO_WARNINGS
#define row 9 
#define list row//列
#define rows row+2
#define lists  list+2
#define landmine 10//地雷的数量

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void init(char arr[rows][lists],int x, int y,char set);
//初始化
void look_over(char arr[rows][lists],int x,int y);
//查看
void place(char arr1[rows][lists], char arr2[rows][lists], int x, int y);
//布置地雷
void check(char arr1[rows][lists], char arr2[rows][lists], int x, int y);
//排查雷
int count(char arr[rows][lists], int x, int y);
//计算雷的个数
#define _CRT_SECURE_NO_WARNINGS
#include "and.h"

void init(char arr[rows][lists], int x, int y,char set)
{
	for (int i = 0; i < x; i++)
	{
		for (int j = 0; j < y; j++)
		{
			arr[i][j] = set;
		}
	}
}

void look_over(char arr[rows][lists], int x, int y)
{
	for (int i = 0; i <= x ; i++)
	{
			if (i < 10)
			printf(" ");//对齐数组
			printf("%d", i);
			printf(" ");
	}
	    printf("\n");
	for (int i = 1; i <= x; i++)
	{
		if (i < 10)
			printf(" ");
		printf("%d ", i);
		for (int j = 1; j < y+1; j++)
		{
			printf(" %c ", arr[i][j]);
		}
		printf("\n");
	}
}

void place(char arr1[rows][lists], char arr2[rows][lists], int x, int y)
{
	int  add = landmine;
	int i = 0;
	while (i < add)
	{
		 x = rand() % row + 1;
		 y = rand() % list + 1;
		 if ((arr1[x][y] == '0') && (arr2[x][y] != '@'))
		{
			arr1[x][y] = '1';
			i++;
		}
	}
}

int count(char arr[rows][lists], int x, int y)
{
	return arr[x - 1][y] +//计算雷的个数
		arr[x - 1][y - 1] +//因为初始化时,输入的值均为字符零一
		arr[x][y - 1] +//而简单的相加又只适用于整形值,字符难以使用,而恰巧ascii码的
		arr[x + 1][y - 1] +//‘0’-‘0’=0,‘1’-‘0’=1
		arr[x + 1][y] +//既0的ascii码等于48,1等于49,相减,则恰巧等于 1
		arr[x + 1][y + 1] +//而减去8个字符0,约等于将字符零一,化为整形零一。
		arr[x][y + 1] +
		arr[x - 1][y + 1] - 8 * '0';
}

void check(char arr1[rows][lists], char arr2[rows][lists], int x, int y)
{
	int q = (row * list) - (landmine + 1);
	int w = 0;
	while(w<q)
	{
		//look_over(arr1, row, list);
		printf("请选择你要排查的位置:");
		int choice1 = 0;
		int choice2 = 0;
		scanf("%d%d", &choice1, &choice2);
		if (choice1 < 0 || choice1 > row|| choice2 < 0 || choice2 > list)
		{
			printf("很抱歉,不存在\n");
			look_over(arr2, row, list);
			continue;
		}
		if (arr1[choice1][choice2] == '1')
		{
			printf("——嘣!!!是地雷!游戏结束——\n");
			break;
		}
		else if (arr2[choice1][choice2] == '#')
		{
			int  a = count(arr1, choice1, choice2);
			arr2[choice1][choice2] = a+'0';
			//如果直接那cont得来的值等效,则会出现空白部分,因为本身count的值,为整形,而arr2是字符。
			//其本质就是电脑将count的值,转化为ascii码,而对应的ascii码,是指令操作符,既空白。
			//所以加上一个字符0,既加上48的ascii码,使其显示真正的得数。
			look_over(arr2, row, list);
			w++;
		}
		else  
		{
			continue;
		}
		if(w == q)
			printf("——恭喜你,游戏通关——\n");
	}
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值