c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。

前言:相信各位同学都玩过扫雷,扫雷的基本规则就是扫完除雷外所有的格子就算赢,每个格子中的数字代表格子周围一圈的雷数量。今天,写下这篇文章,特别希望能帮助到那些c语言初学者。帮助到你们我能感到很开心。虽然我也是个初学者,知识浅薄,但我会很认真的教给你们扫雷的思路与实现。

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第1张图片

 一.基本思路

我们来梳理一下基本的思路

首先打印一个菜单是必不可少的

1.埋雷

首先我们要埋下我们的雷,所以我们得创建一个数组去埋雷.

2.打印棋盘

打印棋盘之前我们创立一个数组,再把埋雷数组的信息发送给棋盘数组,这样我们就能统计棋子周围雷的数量

3.判断输赢

扫到雷即为输,扫完除雷外所有位置即为赢

二.步步的实现

在实现代码之前,为了能快速的发现问题,我们可以创建多个源文件与一个头文件

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第2张图片

      game.h      用来声明函数与定义全局变量                                         

       game.c      专门用来定义函数  

         test.c        用来构建逻辑

创建完这些文件,我们可以更轻松的去完成扫雷。

1.打印菜单( test中实现)

一个好的菜单能让这个游戏看起来更高级更人性化,实现它很简单

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第3张图片

 4个printf函数     是不是很简单粗暴  哈哈

然后我们来写选择之后的反馈

void test(void)
{
	int input = 0;
	do
	{
		menu();
		printf("请输入你要输入的值\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始玩游戏\n");
			game();
			break;
		case 0:
			printf("程序正在退出\n");
			Sleep(1000);
			break;
		default:
			printf("你输入的值有误请重新输入\n");
			break;
		}
	} while (input);
}

相信很多人会问为什么要设置一个‘0’为退出选项,看到那个while函数是不是瞬间就明白了,0表示假,输入直接退出循环。

然后我们把他写到主函数中

int main()
{
	srand((unsigned int)time(NULL));//后文提到,先不管
	test();
	return 0;
}

(以上内容都在test.c中实现逻辑会清晰很多. )

不知道你们看没看到上面的game()函数,我另写这个函数是为了能更好的梳理菜单的逻辑。

2.初始化棋盘

在打印棋盘之前,我们是不是得先初始化棋盘。

我们可以把埋雷的棋盘初始化为字符'0'(后文会提到这样做的意义),扫雷的棋盘全初始化为‘*’(随意)。我们完全可以只写一个函数完成这两个棋盘的初始化。上代码

void Initboard(char mine[ROWS][COLS], int rows, int cols, char set)
{
	int x = 0;
	int y = 0;
	for (x = 0; x < rows; x++)
	{
		for (y = 0; y < cols; y++)
		{
			mine[x][y] = set;
		}
	}
}

(上文说了在game.c中完成大部分函数的定义)

在这之前我们是不是要在头文件中申明

再在test.h中使用它

但别忘了创建数组哦。

 c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第4张图片

 我们来扣下细节

第一个细节就是两个源文件中都必须包含头文件

#include"game.h"

第二个细节是定义行ROWS和列COLS        

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第5张图片

看靠边的这一行,如果我们定义成9*9的棋盘,在统计雷的过程中,我们的逻辑是不是去统计它周围一圈的雷,但是最后一行怎么办,这样统计不是数组越界了吗,所以我们定义一个11*11的棋盘,为的就是不让数组越界,我们可以直接在头文件中定义,方便修改c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第6张图片

第三个细节是伴随第二个细节产生的,我们定义了11*11的棋盘,但是我们是不是只用9*9就可以了,所以我们还得定义两个数。

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第7张图片

 用来放棋子

3.打印棋盘

在上文我们不是有多出来一圈没用的位置,我们完全可以做出优化,在棋子左右加上编号

 c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第8张图片

 函数代码如下

void printfboard(char mine[ROWS][COLS], int rows, int cols)
{
	int x = 0;
	int y = 0;
	printf("----------扫雷---------\n");
	for (x = 0; x <= rows; x++)
	{
		printf("%d ",x);
	}
	printf("\n");
	for (x =1; x <= rows; x++)
	{
		printf("%d",x);
		for (y = 1; y <= cols; y++)
		{
			printf(" %c", mine[x][y]);
		}
		printf("\n");
	}
}

4.埋雷

我们可以自在头文件中定义雷的数量来控制难度

埋雷  雷(‘1’)

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第9张图片

 埋雷这里的细节还是很多啊,读者们请耐心体会一下啊

1.随机数;

rand()是生成随机数的用它%rows(9)生成的是0-8的数,我们加上1生成1-9的数,是不是正好就是我们棋子的范围,我们还得在主函数中应用另一个函数

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

2.判断:

我们在生成随机数是数是随机生成的,所以我们是不是得判断这个地方是不是已经有雷了。有雷你还埋,雷的数量不就减少了吗。

5.统计雷与判断输赢

到了本文最难的地方了,我也在坚持写,你们也要坚持看哦!!!!!

我们要把埋雷的数组信息给到排查雷,我们就得弄一个函数,把它两都传进来建立联系

    Makesets(show, mine, row, col);

我们先来梳理一下

我们有三种情况 1.点到雷(直接炸死不解释)2.不是雷但周围有雷(统计并反馈数字)3.周围无雷

除雷外全部棋子被点完才算赢

我们先上代码再去解释

void Makesets(char show[ROWS][COLS], char mine[ROWS][COLS],int rows,int cols)
{
	//int count = col * row - EASY;
	int x = 0; int y = 0;
	int count;
	while (1)
	{
		count = wincount(show, row, col);
		if (count == EASY)
		{
			break;
		}
		printf("请输入你的坐标-->");
		scanf_s("%d %d", &x, &y);
		int ret = Getnumber(mine, x, y);
		printf("\n");
		if ((x >= 1 && x <= rows) && (y >= 1 && y <= cols))
		{

			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了\n");
				printfboard(mine, row, col);
				break;
			}
			if (mine[x][y] != '1')
			{
				show[x][y] = ret + '0';
			}
			if (show[x][y] == '0')
			{
				lockline(show, mine, x, y);
				
			}
			printfboard(show, row, col);

		}
		else
		{
			printf("你输入的坐标非法请重新输入\n");
		}
	}
	if(count==EASY)
	printf("恭喜!!!!你获得了胜利\n");
}

很清晰的看到上面有棋子的三种不同情况

第一种不解释

第二种周围有雷

我定义了一个函数去统计雷的个数Getnumber

int Getnumber(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1]
		+ mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');
}

我来说一下之前为什么用字符‘0’和‘1’去当雷呢,就是为了好统计

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第10张图片

 看这个ASCII表 字符0是48字符1是49  加上一圈周围的字符再减去8*  ‘0’返回的是不是整形的雷的个数,我再加上一个字符0,是不是返回的就是char类型的雷的个数,再返回到扫雷的数组中是不是就能出来雷的数目。


第三种 周围无雷

我利用了一个函数lockline,是想这样

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第11张图片

当周围无雷的时候,我是不是直接把周围也没有雷的棋子展开了,

所以定义了这个函数

void lockline(char show[ROWS][COLS],char mine[ROWS][COLS],int x,int y)
{
	show[x][y] = Getnumber(mine, x, y) + '0';

	if (show[x][y] == '0')
	{
		show[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = -1; i <= 1; i++)
		{
			for (j = -1; j <= 1; j++)
			{


				if (x + i > 0 && y + j > 0 && x + i <= row && y + j <= col&&show[x+i][y+j]=='*')
				{
					lockline(show, mine, x+i, y+j);
				}
			}
		}
	}

}

 我用了一个递归来实现,思路是周围都是‘0’我就递归出去,碰到数字我就递归回来,并且把‘0’全部换成了空格,像这样

c语言初阶学者的扫雷(函数,递归,循环,数组)基础知识的综合运用。_第12张图片

 限制就是:是不是字符‘0’;

接下来是判断输赢

输不用解释

赢:是不是我得把所有不是雷的棋子全部扫完就算赢,所以我只要统计‘*’的个数,如果个数是雷的个数我就赢了

int wincount(char show[ROWS][COLS], int rows, int cols)
{
	int i = 1;
	int j = 1;
	int count = 0;
	for (i = 1; i<=rows; i++)
	{
		for (j = 1; j<=cols;j++)
		{
			if (show[i][j] == '*')
			{
				count++;
			}
		}
	}
	return count;

欧克全部完成

三.最后一步组装

直接放代码把

void game(void)
{
	//初始化布置雷与排查雷的棋子
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	Initboard(mine, ROWS, COLS, '0');
	Initboard(show, ROWS, COLS, '*');
	//打印棋盘
	//printfboard(mine, row, col);
	printf("\n");
	printfboard(show, row, col);
	//形成棋子(埋雷与统计雷)
	printf("\n");
	//埋雷
	Minemine(mine,row,col);
	printfboard(mine, row, col);
   //统计雷并制造棋子
    Makesets(show, mine, row, col);

}

累死我了,哈哈,真希望我写的东西能帮助到你们

花了3个小时,字数7269(包含代码),希望同学们能给个点赞,我就心满意足了,同学们的点赞是给我莫大的鼓励

接下来是全部的源码(供参考)

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include"game.h"
void game(void)
{
	//初始化布置雷与排查雷的棋子
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	Initboard(mine, ROWS, COLS, '0');
	Initboard(show, ROWS, COLS, '*');
	//打印棋盘
	//printfboard(mine, row, col);
	printf("\n");
	printfboard(show, row, col);
	//形成棋子(埋雷与统计雷)
	printf("\n");
	//埋雷
	Minemine(mine,row,col);
	printfboard(mine, row, col);
   //统计雷并制造棋子
    Makesets(show, mine, row, col);

}
void menu(void)
{
	printf("        游戏菜单\n");
	printf("*************************\n");
	printf("*******  1.play   *******\n");
	printf("*******  0.exit   *******\n");
	printf("*************************\n");

}
void test(void)
{
	int input = 0;
	do
	{
		menu();
		printf("请输入你要输入的值\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始玩游戏\n");
			game();
			break;
		case 0:
			printf("程序正在退出\n");
			Sleep(1000);
			break;
		default:
			printf("你输入的值有误请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	srand((unsigned int)time(NULL));
	test();
	return 0;
}

game.h

#include
#include

#define EASY 10
#define ROWS 11
#define COLS 11

#define row ROWS-2
#define col COLS-2

void Initboard(char mine[ROWS][COLS],int rows,int cols, char set);
void printfboard(char mine[ROWS][COLS],int rows,int cols);
void Minemine(char show[ROWS][COLS], int rows, int cols);
void Makesets(char show[ROWS][COLS],char mine[ROWS][COLS],int rows,int cols);

game.c

#include
#include"game.h"
#include
#include
#define _CRT_SECURE_NO_WARNINGS 1

int wincount(char show[ROWS][COLS], int rows, int cols)
{
	int i = 1;
	int j = 1;
	int count = 0;
	for (i = 1; i<=rows; i++)
	{
		for (j = 1; j<=cols;j++)
		{
			if (show[i][j] == '*')
			{
				count++;
			}
		}
	}
	return count;

}


void lockline(char show[ROWS][COLS],char mine[ROWS][COLS],int x,int y)
{
	show[x][y] = Getnumber(mine, x, y) + '0';

	if (show[x][y] == '0')
	{
		show[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = -1; i <= 1; i++)
		{
			for (j = -1; j <= 1; j++)
			{


				if (x + i > 0 && y + j > 0 && x + i <= row && y + j <= col&&show[x+i][y+j]=='*')
				{
					lockline(show, mine, x+i, y+j);
				}
			}
		}
	}

}

int Getnumber(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1]
		+ mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');
}

void Initboard(char mine[ROWS][COLS], int rows, int cols, char set)
{
	int x = 0;
	int y = 0;
	for (x = 0; x < rows; x++)
	{
		for (y = 0; y < cols; y++)
		{
			mine[x][y] = set;
		}
	}
}

void printfboard(char mine[ROWS][COLS], int rows, int cols)
{
	int x = 0;
	int y = 0;
	printf("----------扫雷---------\n");
	for (x = 0; x <= rows; x++)
	{
		printf("%d ",x);
	}
	printf("\n");
	for (x =1; x <= rows; x++)
	{
		printf("%d",x);
		for (y = 1; y <= cols; y++)
		{
			printf(" %c", mine[x][y]);
		}
		printf("\n");
	}
}

void Minemine(char show[ROWS][COLS], int rows, int cols)
{
	int count = EASY;
	while(count)
	{
		int x = rand() % rows + 1;
		int y = rand() % cols + 1;
		if (show[x][y] == '0')
		{
			show[x][y] = '1';
			count--;
		}
		else
		{
			continue;
		}
	}
}

void Makesets(char show[ROWS][COLS], char mine[ROWS][COLS],int rows,int cols)
{
	//int count = col * row - EASY;
	int x = 0; int y = 0;
	int count;
	while (1)
	{
		count = wincount(show, row, col);
		if (count == EASY)
		{
			break;
		}
		printf("请输入你的坐标-->");
		scanf_s("%d %d", &x, &y);
		int ret = Getnumber(mine, x, y);
		printf("\n");
		if ((x >= 1 && x <= rows) && (y >= 1 && y <= cols))
		{

			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了\n");
				printfboard(mine, row, col);
				break;
			}
			if (mine[x][y] != '1')
			{
				show[x][y] = ret + '0';
			}
			if (show[x][y] == '0')
			{
				lockline(show, mine, x, y);
				
			}
			printfboard(show, row, col);

		}
		else
		{
			printf("你输入的坐标非法请重新输入\n");
		}
	}
	if(count==EASY)
	printf("恭喜!!!!你获得了胜利\n");
}

你可能感兴趣的:(c语言,开发语言)