【C语言】C语言成长之路之简单扫雷的逻辑设计和实现੭ ᐕ)੭*⁾⁾

       Hello,大家好ฅ˙Ⱉ˙ฅ!今天小狮子我又为大家带来了一篇关于我最近的C语言阶段性学习成果,即和上一篇博客中的傻瓜三子棋并称C语言两大守门将(我自己取的,嘻嘻)的扫雷实现的文章。

目录

一、数组的创建与初始化

二、打印扫雷盘及展示扫雷盘状态

三、设置炸弹坐标

四、最后的玩家交互与判断胜负


       在扫雷游戏实现之前,我们要了解扫雷的基本逻辑:

1.扫雷开始时,玩家输入可以选择要排查的位置
2.如果当玩家找到一个未埋雷区,它会显示周围一圈的总共雷数
3.如果玩家成功找出隐藏的炸弹并排除所有未埋雷区则游戏胜利
4.而如果玩家在排雷时遇到炸弹游戏就会失败

      了解完大致思路和逻辑之后,我来介绍我写的简单版扫雷的代码实现:

【C语言】C语言成长之路之简单扫雷的逻辑设计和实现੭ ᐕ)੭*⁾⁾_第1张图片

       首先我准备了三个文件,分别是头文件game.h、源文件game.c和text.c文件。game.h用于引用各种头文件和定义常量,text.c文件用于实现游戏的初始化,game.c用于存放整个游戏环节中的各种具体实现的函数们。

       这是我的text.h的具体代码,大家在练习时可以参考一下❛‿˂̵✧:

#include "game.h"
void menu()
{
	printf("***       *扫雷*        ***\n");
	printf("***       1.play        ***\n");
	printf("***       0.exit        ***\n");
	printf("***************************\n\n");
}
void play()
{
	printf("\n扫雷游戏说明:玩家输入排查坐标\n");
	printf("如果玩家在排雷时踩到炸弹游戏就会失败\n");
	printf("如果玩家成功找出隐藏的炸弹并排除所有未埋雷区则游戏胜利\n");
	printf("且如果当玩家找到一个未埋雷区,它会显示周围一圈的总共雷数\n");
	printf("管你听没听懂,玩就完了!\n\n");
}
void game()
{
	//1、建立两个扫雷盘  一个用于处理数据  一个用于展示
	char hid[Hs][Ls];
	char show[Hs][Ls];
	//2、初始化两个扫雷盘
	chushi(hid, Hs, Ls, '0');
	chushi(show, Hs, Ls, '*');
	//3、设计可以打印出扫雷盘的函数
	display(show, H, L);
	//下面这个调用可以帮我们观察内盘  以便于我们后续的调试
	/*display(hid, H, L);*/
	//4、将炸弹放置进扫雷盘,我们只需要对内盘进行此操作
	mine_set(hid, H, L);
	display(hid, H, L);
	//5、最后的扫雷玩家与电脑交互
	sweep(hid, show, H, L);
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	play();
	do
	{
		menu();
		printf("请选择是否开始游戏:>");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			printf("开始游戏!\n\n");
			game();
			break;
		case 0:
			printf("退出游戏。\n");
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	return 0;
}

       这里我为玩家介绍了大致的游戏规则,放置了菜单和每次游戏的初始化,保证玩家能够想玩多少次就玩多少次,并且将game.c中设计的函数整合起来放在game()函数中。

       接下来是头文件game.h的创建和使用,当我们自己创建头文件时,我们可以把函数的设计文件和主函数所在文件分开来,并利用函数的声明将这两者结合起来,利于更加有条理地完成整个程序的设计。除此之外,我们还可以将引用的C语言自带头文件和自定义常量放进我们的头文件中,这样我们就可以完成各文件的整合运用。

下面是我的game.h,我会在下面的设计中用到它(●'◡'●):

#define  _CRT_SECURE_NO_WARNINGS 
#include 
#include 
#include 
#define H 9
#define L 9
#define Hs H+2
#define Ls L+2
#define M 10
void chushi(char board[Hs][Ls], int h, int l, char chu);
void display(char board[Hs][Ls], int h, int l);
void mine_set(char board[Hs][Ls], int h, int l);
void sweep(char hid[Hs][Ls], char show[Hs][Ls], int h, int l);

       最后也是最重要的——我用于存放设计的各个游戏功能函数的源文件game.c,我将会详细介绍其中的各个函数。

一、数组的创建与初始化

       玩家在扫雷时,我们需要将隐藏起来的雷区和展示给玩家的界面分开,那么我们在进行扫雷时就需要两个场景,一个用于存放坐标的埋雷状态并用于处理和运算,另一个用于展示给玩家看并给出游戏信息引导玩家进行游戏。因此我设置了两个11*11(防止访问数组元素时越界)的二维数组 hid 和 show 组成两个平面。并将它们初始化分别放入字符 ’0‘ 和 ’*‘。

       这是我的初始化函数实现:

void chushi(char board[Hs][Ls], int h, int l, char chu)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < h; i++)
	{
		for (j = 0; j < l; j++)
		{
			board[i][j] = chu;
		}
	}
}

        这里我们就一 一选中了两个数组中的元素并指定它们分别为 ’0‘ 和 ’*‘。

二、打印扫雷盘及展示扫雷盘状态

       在玩家进行游戏时,他需要知道扫雷盘的实时状态以及自己扫雷的记录,那我们就需要设计一个打印的函数。后面可以知道,这个函数还有益于我们调试程序内容,即我们可以通过打印内盘的手段作弊得知炸弹的排布。

       这是我的打印函数,经过之前的练习,我们已经可以很好的利用for循环和打印函数来完成这一动作:

void display(char board[Hs][Ls], int h, int l)
{
	int i = 0;
	int j = 0;
	printf("--------------扫雷游戏--------------\n");
	printf("温馨提示:下面自带的数字是坐标哦!\n");
	printf("请以'*'、'#'和' '分别代表扫雷和标记以及取消标记。\n");
	printf("\n");
	for (i = 0; i < h + 1; i++)
	{
		printf("    %-3d", i);
		for (j = 1; j < l + 1; j++)
		{
			if (i == 0)
			{
				printf("%-3d", j);
			}
			else
			{
				printf("%-3c", board[i][j]);
			}
		}
		printf("\n\n");
	}
	printf("\n");
	printf("--------------扫雷游戏--------------\n");
	printf("\n");

三、设置炸弹坐标

       在设置炸弹坐标时,我们很容易想到它的本质是为内盘元素进行随机坐标赋值,这时候我们就要用到srand()rand()函数进行随机值的选定,并设置一个while循环以应放的地雷数为限制条件。

       下面是我的代码实现:

void mine_set(char board[Hs][Ls], int h, int l)
{
	int i = 0;
	int j = 0;
	int count = M;
	while (count)
	{
		i = rand() % h + 1;
		j = rand() % l + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}

四、最后的玩家交互与判断胜负

       我们用经典的scanf()函数来实现玩家的输入与人机交互,并用多层if()分支结构并建立两个函数 sum()rec() 完成数据处理和判断。其中对于字符的运算扫雷扩散的递归是本次文章的重难点,要求我们灵活运用我们对于ASCII码值以及字符与整形之间的关系的理解。

void sweep(char hid[Hs][Ls],char show[Hs][Ls], int h, int l)
{
	int i = 0;
	int j = 0;
	char bra = 0;//用于决定玩家的操作性质
	int count = M;
	int win = 0;
	while (win < h * l - M)//保证玩家能够获胜
	{
		printf("请输入排查坐标:>\n");//提醒玩家按操作步骤进行
		printf("行: ");
		scanf("%d", &i);
		printf("列: ");
		scanf("%d", &j);
		printf("请选择排雷或设置标记:");
		getchar();
		scanf("%c", &bra);
		if (bra == '#')
		{
			show[i][j] = '#';
			display(show, h, l);
			continue;
		}
		else if (bra == '*')
		{
			if (show[i][j] != '*')
			{
				continue;
			}
			if (i >= 1 && i <= h && j >= 1 && j <= l)
			{
				if (hid[i][j] == '1')
				{
					printf("\a恭喜你!你踩到炸弹了!(喜)\n\n");
					printf("下面是这次的炸弹排布,‘1’表示炸弹。\n");//失败后展示我们的内盘
					display(hid, h, l);
					break;
				}
				else
				{
                    //计算周围一圈的炸弹数并在本坐标值为0时展开周围坐标,为玩家提供有效信息
					rec(hid, show, i, j, &win);
					display(show, h, l);
				}
			}
			else
			{
				printf("输入的坐标错误,请重新输入!\n");
			}
		}
		else if (bra == ' ')
		{
			show[i][j] = '*';
			display(show, h, l);
			continue;
		}
		else
		{
			printf("你干嘛~\n");
		}
        //判断胜利
		if (win == h * l - M)
		{
			printf("玩家胜利!谢谢您的游玩!\n爱你哦!");
			rew();//对胜利玩家的赞赏,可以自行设计
		}
	}
}

       上面的代码中有函数 rec() 用于完成show数组元素的赋值和递归,这个函数中,我们需要再次判断坐标的合法性(因为递归时每一次也要继续放入一个坐标,为防止越界访问和死递归,我们需要用if语句来判断坐标的合法性),并不断为新的show数组元素赋值,然后累加变量win,判断获胜。

这是具体的 rec() 代码实现:

void rec(char hid[Hs][Ls], char show[Hs][Ls], int i, int j,int* win)
{
	if (i >= 1 && i <= H && j >= 1 && j <= L && hid[i][j] != '1' && show[i][j] == '*')
	{
		show[i][j] = sum(hid, i, j);
		*win += 1;
		if (sum(hid, i, j) == '0')
		{
			rec(hid, show, i - 1, j - 1, win);
			rec(hid, show, i - 1, j, win);
			rec(hid, show, i - 1, j + 1, win);
			rec(hid, show, i, j - 1, win);
			rec(hid, show, i, j + 1, win);
			rec(hid, show, i + 1, j - 1, win);
			rec(hid, show, i + 1, j, win);
			rec(hid, show, i + 1, j + 1, win);
		}
		else
			return;
	}
}

       可以看到我在对所选坐标不是炸弹时的展示数字(即所选坐标周围一圈的炸弹数量)进行计算时又自定义了一个函数sum(),这个函数帮我们计算坐标周围的炸弹数量,只需要在game.c这个文件中使用,因此不需要在头文件中声明

这是我的sum()函数实现:

1、直接计数

char sum(char hid[Hs][Ls], int x, int y)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			if(hid[x+i][y+j] == '1')
				count++;
		}
	}
	return count + '0';
}

2、间接计数

char sum(char hid[Hs][Ls], int x, int y)
{
	int i = 0;
	int j = 0;
	int sum = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			sum += hid[x + i][y + j];
		}
	}
    sum -= 8 * '0';
	return sum;
}

       到此为止我们就将整个扫雷的基本逻辑完成并放于game.c中,现在我们利用函数声明将它们都放在game()函数中:

void game()
{
	//1、建立两个扫雷盘  一个用于处理数据  一个用于展示
	char hid[Hs][Ls];
	char show[Hs][Ls];
	//2、初始化两个扫雷盘
	chushi(hid, Hs, Ls, '0');
	chushi(show, Hs, Ls, '*');
	//3、设计可以打印出扫雷盘的函数
	display(show, H, L);
	//下面这个调用可以帮我们观察内盘  以便于我们后续的调试
	/*display(hid, H, L);*/
	//4、将炸弹放置进扫雷盘,我们只需要对内盘进行此操作
	mine_set(hid, H, L);
	/*display(hid, H, L);*/
	//5、最后的扫雷玩家与电脑交互
	sweep(hid, show, H, L);
}

       扫雷的大概运行机制和玩法我们都已经完成实现,想必你也能够发现这样的扫雷还可以有很多的优化,接下来的代码优化小狮子在这里就交给你们了,希望大家在学习C语言的时候能够多多动手多多思考,这样才能天天进步哟❤️~

       So much for this article!(放洋屁)非常感谢大家能够看到这里!

       在这次的文章中我们一起学习了基础C语言的  简单扫雷实现  ,希望我们在今后的学习中能一如此刻般有同道中人相伴,每天都有每天的热情去进行学习,言尽于此,与大家共勉ฅ( ̳• · • ̳ฅ)!

       最后,希望大家能像上次一样为我的文章打分,谢谢大家!◝(⑅•ᴗ•⑅)◜..°♡

你可能感兴趣的:(【C语言】C语言成长之路,c语言,开发语言)