C语言制作扫雷游戏(拓展版赋源码)

目录

引言:

三个新功能实现

1.可以选择难度或自定义

实现难点解析

代码实现(附源码)

扫雷.c

game.h

game.c

2.对选择位置进行标记或取消标记

一.框架

我们先理一下思路

如何构造框架

二.取消标记函数

三.标记函数

四.加入清屏,进行优化

源码

game.c

3.点的铺开(相比其他较难)

拓展版源码

game.h

game.c

扫雷.c

结语:


引言:

那么,本篇我们来对之前的基础版扫雷进行拓展,来实现一个相比于网页版的扫雷,只少了一个计时功能的扫雷游戏。总代码量约为450行左右,并不会很多

那么,没看过扫雷基础篇的请移步C语言制作扫雷游戏(基础版赋源码)-CSDN博客看完再来看这篇,因为该篇代码是基于基础版扫雷代码改编而来

那么,接下来,我们进入正文(红色部分是老代码,绿色为更改后代码,后面就不强调啦)

三个新功能实现

我推荐从上往下依次看这四个功能如何实现 ,因为一开始是基础版的源码,然后呢,我会按着一步步的顺序将基础版的源码进行优化与功能实现,可以把基础版的三个文件的源码复制到你的编译器中,然后跟着我的思路进行更改时,对比也比较清晰 ,从上往下看就不会遗漏有些代码的更改与变化,最后的完整代码是一步一步改下来的,那么我们开始

1.可以选择难度或自定义

首先,我们先进行最简单的一个功能的实现,那就是选择难度与自定义难度,那我们把它拆分成选择难度和自定义难度俩个块来进行处理

实现难点解析

首先是选择难度,这个十分简单,我们先来确定一个各个难度的长宽与雷的数量

在基础篇中,我们是只有一个9*9的格子,然后雷的数量是10个,那么,我们就当这个是easy难度,我是这么设置难度划分的

简单:9*9的格子,10个雷

中等:16*16的格子,40个雷

困难:30*16的格子,99个雷

那么,我们先看变量

在基础版中因为只有一个9*9的格子,所以我们只用了下图中红色区域的代码,用Chang来表示棋盘的长,用Kuan来表示棋盘的宽,Changs和Kuans表示数组的大小,然后用Leisum来表示棋盘中雷的个数

因为有了不同难度的选择,所以我们可以将宏定义把简单,中等,困难的长度,宽度和雷数都进行定义,然后在之后的程序中因为选择的不同来使用不用的宏名(宏定义定义的变量)即可,为了方便理解,可以像我这样在前面加上一个前缀E表示简单,M表示中等,H表示困难,如图

那么,大小方面搞定了,那么数组应该怎么创建呢,如果像基础版那个样子写的话,那数组创建又是个大问题了,这里就有2种方案了,我是用的第二种方案

一.根据输入的值,选择出难度后,然后创建一个数组,数组中的数字用宏名来顶替,就跟之前基础版的一样,但是,因为有第四种情况,就是自定义的情况,变量可创不了数组(后面传递数组时难以接收,因为你不知道接收时的形参函数部分的列里该写什么,但列又不能省略),所以又要为了第四段专门再写一段代码而且最主要的是,若创建的数组大小不一,那么在调用函数传递数组时候,就需要写多个函数了,会使代码及其冗杂,这就很影响代码的可读性了,而且因为自定义的存在,之后还是会创建第二种方法的数组,所以我选择的是第二种方法,当然,如果想要用第一种方法的话也可以尝试一下,就是game.c里的代码长度可能会很长

二.在选择前,先创建100*100的二维数组,这样不管选择哪个难度,都可以进行操作(因为自定义范围肯定会限制,太大不利于游玩,一般都限制在50*16左右)

如图,即可

那么问题都解决了,我们来进行代码实现

代码实现(附源码)

扫雷.c

我们先对扫雷.c 进行操作,对基础版扫雷的框架进行更改,因为有难度选择了,那我们先自定义一个Level函数用于输出选择难度的菜单,如图

菜单创建完后,接下来就是 game函数内部的更改了,代码如下,首先我们先创建四个变量

input 用于选择难度特判

chang和kuan 用于自定义难度时输入长和宽

lei 用于自定义难度时雷数量的输入

1.接下来我们先使用do while语句,让用户根据提示输入信息,若输入有问题就其实用户重新输入,直到按要求输入位置

2.而后,我们创建俩个数组,只是数组的范围变化了一下,变为了100*100,代表的意思依旧如基础版一样,cun表示暗盘,show表示明盘

3.接下来再通过switch语句 ,根据input 的值跳转到对应的情况中,case 1,2,3都是同样的情况,只需要将函数中的参数变一下就可以了 ,这部分如何操作以及函数里每个参数分别代表什么在基础版里已经讲过了,就不过多赘述了,case 4 的自定义也是如此,只是需要多加上一步输入雷盘的长与宽与雷的个数,并根据输入情况进行反馈,直至符合要求后跳出do while循环,然后再进行同样的操作即可

下面就是扫雷.c的源码,其余部分不用变动

复制代码
void Lelve()
{
	printf("************************\n");
	printf("*** 1.easy(9*9 10)   ***\n");
	printf("*** 2.mid(16*16 40)  ***\n");
	printf("*** 3.hard(30*16 99) ***\n");
	printf("*** 4.自定义         ***\n");
	printf("************************\n");
}

void game()
{
	int input = 0;
	int chang = 0, kuan = 0, lei = 0;
	Lelve();
	printf("请选择:");
	do 
	{
		scanf("%d", &input);
		if (input < 1 || input>4)
		{
			printf("输入有误,请重新输入:");
			continue;
		}
		break;
	} while (1);
	char cun[100][100] = { 0 };
	char show[100][100] = { 0 };
	switch (input)
	{
	case 1:
		chu(cun, EKuans, EChangs, '0');
		chu(show, EKuans, EChangs, '*');

		//布置雷
		set(cun, EKuan, EChang, ELeisum);

		//打印棋盘
		print(show, EKuan, EChang);


		//排查雷
		Findlei(cun, show, EKuan, EChang, ELeisum);
		break;

	case 2:
		chu(cun, MKuans, MChangs, '0');
		chu(show, MKuans, MChangs, '*');

		//布置雷
		set(cun, MKuan, MChang, MLeisum);

		//打印棋盘
		print(show, MKuan, MChang);


		//排查雷
		Findlei(cun, show, MKuan, MChang, MLeisum);
		break;

	case 3:
		chu(cun, HKuans, HChangs, '0');
		chu(show, HKuans, HChangs, '*');

		//布置雷
		set(cun, HKuan, HChang, HLeisum);

		//打印棋盘
		print(show, HKuan, HChang);


		//排查雷
		Findlei(cun, show, HKuan, HChang, HLeisum);
		break;

	case 4:
		printf("请输入长度 宽度 雷的个数(长<=50 宽<=16 雷数<长*宽)\n");//过大控制台太小,屏幕会乱跳,不好操控
		do
		{
			scanf("%d %d %d", &chang, &kuan, &lei);
			if (chang > 50 || chang < 1 || kuan>50 || kuan < 1 || chang * kuan <= lei)
			{
				printf("输入有误,请重新输入:");
				continue;
			}
			break;
		} while (1);
		chu(cun, kuan+2, chang+2, '0');
		chu(show, kuan+2, chang+2, '*');

		//布置雷
		set(cun, kuan, chang, lei);


		//打印棋盘
		print(show, kuan, chang);



		//排查雷
		Findlei(cun, show, kuan, chang, lei);
		break;
	}
}

那么扫雷.c里的更改已经完成了,接下来就是通过更改game,h和game.c中的函数来实现即可,那么这边就只是改个参数的事情了,这边就直接放代码啦(只有Findlie函数部分略有变化,多了一个参数,里面也变了一点点,但很明了,看一眼就知道了,就不多赘述啦),自己比对一下函数部分哟

game.h

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define EChang 9//好处是,想更改棋盘大小时候直接改这里就好了,不用在程序里一个个改,方便很多
#define EKuan 9

#define MChang 16
#define MKuan 16

#define HChang 30
#define HKuan 16

#define EChangs EChang+2
#define EKuans EKuan+2

#define MChangs MChang+2
#define MKuans MKuan+2

#define HChangs HChang+2
#define HKuans HKuan+2

#define ELeisum 10
#define MLeisum 40
#define HLeisum 99

void chu(char arr[][100], int hang, int lie, char fu);

void print(char arr[][100], int lie, int hang);

void set(char arr[][100], int hang, int lie, int ge);

void Findlei(char yin[][100], char xian[][100], int lie, int hang, int leisum);

game.c

game.c中,除了game.h的变化,就只有print函数(改了输出格式,因为有多位数了,不该输出格式会使打印出来的对不齐)和FIndlie函数里面发生了略微变化,但因为变化目的很明显,也很易懂,就直接放源码啦

复制代码
#define _CRT_SECURE_NO_WARNINGS
 
#include "game.h"

//初始化棋盘
void chu(char arr[][100],int hang,int lie,char fu)
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
			arr[i][j] = fu;
	}
}

//打印棋盘
void print(char arr[][100], int hang, int lie)
{
	for (int i = 0; i <= lie; i++)
		printf("%d ", i);
		printf("%2d ", i);
	printf("\n");
	for (int i = 1; i <= hang; i++)
	{
		printf("%d ", i);
		printf("%2d ", i);
		for (int j = 1; j <= lie; j++)
			printf("%c ", arr[i][j]);
			printf("%2c ", arr[i][j]);
		printf("\n");
	}
}

//布置雷
void set(char arr[][100],int hang,int lie,int ge)
{
	while (ge--)
	{
		int h = 1 + rand() % hang;
		int l = 1 + rand() % lie;
		if (arr[h][l] == '0')
		{
			arr[h][l] = '1';
		}
		else
			ge++;
	}
}



void Findlei(char yin[][100], char xian[][100], int hang, int lie,int leisum)
{
	int x = 0, y = 0;
	int ge = 0;
	while (1)
	{
		printf("请输入要排查的坐标(行 列):");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
			printf("输入的坐标有误,请重新输入:");
		else if (xian[x][y] != '*')
			printf("该坐标已排查过,请重新输入:");
		else
		{
			if (yin[x][y] == '1')
			{
				system("cls");
				printf("很遗憾,你被炸死了,游戏结束\n");
				print(yin, hang, lie);
				break;
			}
			else
			{
				int fang[8][2] = { {0,1},{0,-1},{-1,0},{1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
				xian[x][y] = '0';
				for (int i = 0; i < 8; i++)
				{
					if (yin[x + fang[i][0]][y + fang[i][1]] == '1')
						xian[x][y]++;
				}
				ge++;
				system("cls");
				if (ge + Leisum == Kuan * Chang)
				print(xian, hang, lie);
				if (ge + leisum == hang * lie)
				{
					printf("恭喜你,排雷成功\n");
					break;
				}
			}
		}
	}
}

那么这部分完成后,现在的代码就多出可以选择难度和自定义的功能啦,那么,网页版有插旗子标记,我们也不能少,接下来,我们来讲对棋盘进行标记与取消标记


2.对选择位置进行标记或取消标记

这一部分在game.c里实现

那么这部分开始就比较要吃点逻辑了,我打算分三步来讲,先把框架讲清楚后,再分别讲标记与取消标记这俩个函数

一.框架

我们先理一下思路

总所周知,在我们原先的代码里,我们不管进不进行模式选择,到最后都会输出一次棋盘,然后再进入Findlei函数之中,

那么我们想要在排查雷之前,先询问一下意向,是排雷,还是标点,还是取消标点,这块部分,就可以在Findlei函数中实现,只需要放在排雷的前面执行即可;

然后因为在网页游戏中,玩扫雷标点是想标就标,想把标的点去掉就去掉,然后或者进行扫雷,扫雷前可以无限次的进行标点或者取消标点,只要标点的数量不超过雷的数量即可

那么大致要求已经说完了,我们来看看怎么打,首先来看下我的代码,如图,我们结合代码来分析(红绿代码更有辨识度,就用图片了)

如何构造框架

我们多创建2个变量

biaoji 代表目前盘上标记的个数

input 用于特判标记还是取消标记还是排雷

首先,依旧是跟一样的while语句,知识要在排雷前进行选择的操作,那么,我们就要给他菜单,即代码中的Choose函数

然后因为要进行提示,并且判断输入是否合理,合理的话就进行对应的操作,这个时候我们就用到了我们的do while循环来判断输入的合理性 和我们的switch语句来进入要执行对应操作的分支语句,以及通过biaoji的值的变化来判断是否符合要求。

执行完这些操作后,代码的框架就基本构筑完成了,代码中的Biaolei函数便是标记雷函数的具体实现,Clearlei函数便是取消标记雷的具体实现,因为取消标记雷更好实现,那我们先讲取消标记函数

二.取消标记函数

取消标记函数我们只需要做到在这个函数中先命名俩个变量

我就命名了x,y来表示坐标

然后进入while循环,提示输入取消标记的坐标,直至输入的坐标符合要求,就跳出循环,将对应的那个点位赋值为 ' * '即可,最后在进行一次打印就可以了,因为若进入了这个语句,就不会去排雷的部分,自然就不会打印了,所以需要在这边写上print函数来打印暗盘

代码如下,十分简单

三.标记函数

如果取消标记函数弄懂了,再看这个就会简单很多,这个函数就是需要多一层操作

先给代码,然后我来围绕代码来讲,应该更容易懂

首先,先创建三个变量

一个x,一个y,用来表示想要标记的坐标

还有一个type 用来表示判断想要标记的坐标类型是?还是!

然后套一层while循环,若输入不满足要求,就反馈相应信息要求重新输入,直到满足要求为止,跳出循环

跳出循环后,进入menubiao函数,给出提示,然后依旧与往常一样,用while循环套上,进行输入,直到输入满足条件跳出

然后用switch语句进行跳转,进入想要执行的语句中,然后通过x,y的坐标,将那个位置的明盘上的'*'赋值成对应符号

最后再进行一次输出,即可,至于为什么要输出一次明盘,原因和取消标记函数里一样

四.加入清屏,进行优化

这个功能到这里来说就已经实现完毕了,但我们在运行时,会发现输出会很乱,这时候,就要稍稍改变一下代码的顺序,再插入清屏,实现运行代码时的那种清爽感了,这里就不细讲啦,跟着下面的代码看,很容易就理解啦

那么,对选择位置进行标记或取消标记的这个功能也实现啦,我把实现这个功能后的源码发出来

源码

因为这个功能并没有改动game.c与扫雷.c,那俩个的码就不发啦

game.c

复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

//初始化棋盘
void chu(char arr[][100], int hang, int lie, char fu)
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
			arr[i][j] = fu;
	}
}

//打印棋盘
void print(char arr[][100], int hang, int lie)
{
	for (int i = 0; i <= lie; i++)
		printf("%2d ", i);
	printf("\n");
	for (int i = 1; i <= hang; i++)
	{
		printf("%2d ", i);
		for (int j = 1; j <= lie; j++)
			printf("%2c ", arr[i][j]);
		printf("\n");
	}
}

//布置雷
void set(char arr[][100], int hang, int lie, int ge)
{
	while (ge--)
	{
		int h = 1 + rand() % hang;
		int l = 1 + rand() % lie;
		if (arr[h][l] == '0')
		{
			arr[h][l] = '1';
		}
		else
			ge++;
	}
}


static void Choose()
{
	printf("******************\n");
	printf("*** 1.标记     ***\n");
	printf("*** 2.取消标记 ***\n");
	printf("*** 0.排雷     ***\n");
	printf("******************\n");
}

static void menubiao()
{
	printf("*********************\n");
	printf("**** 1.?(不确定) ****\n");
	printf("**** 2.!(肯定)  ****\n");
	printf("*********************\n");
}

//标记雷
void Biaolei(char arr[][100], int hang, int lie)
{
	int x = 0, y = 0;
	int type = 0;
	while (1)
	{
		printf("请输入要标记的坐标:");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
		{
			printf("输入的坐标有误,请重新输入\n");
			continue;
		}
		else if (arr[x][y] == '?' || arr[x][y] == '!')
		{
			printf("该位置已被标记,请重新输入\n");
			continue;
		}
		else if (arr[x][y] != '*')
		{
			printf("该坐标已被排查,无法标记,请重新输入\n");
			continue;
		}
		break;
	}
	menubiao();
	do
	{
		printf("请输入要标记的类型:");
		scanf("%d", &type);
		if (type != 1 && type != 2)
		{
			printf("不存在该选项,请重新输入\n");
			continue;
		}
		break;
	} while (1);

	switch (type)
	{
	case 1:
		arr[x][y] = '?';
		print(arr, hang, lie);
		break;
	case 2:
		arr[x][y] = '!';
		print(arr, hang, lie);
		break;
	}
}

//取消标记雷
void Clearlei(char arr[][100], int hang, int lie)
{
	int x = 0, y = 0;
	int type = 0;
	while (1)
	{
		printf("请输入要取消标记的坐标:");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
		{
			printf("输入的坐标有误,请重新输入\n");
			continue;
		}
		else if (arr[x][y] != '?' && arr[x][y] != '!')
		{
			printf("该位置未被标记,请重新输入\n");
			continue;
		}
		break;
	}
	arr[x][y] = '*';
	print(arr, hang, lie);
}

//排查,标记雷
void Findlei(char yin[][100], char xian[][100], int hang, int lie, int leisum)
{
	int x = 0, y = 0;
	int ge = 0;
	int biaoji = 0;
	int input = 0;

	while (1)
	{
		do
		{
			system("cls");
			print(xian, hang, lie);
			Choose();
			printf("请选择:");
			scanf("%d", &input);
			if (input != 1 && input != 2 && input != 0)
			{
				printf("输入有误,请重新输入\n");
			}
			if (biaoji >= leisum && input == 1)
			{
				printf("标记个数已超出雷数,请重新选择\n");
				continue;
			}
			if (biaoji <= 0 && input == 2)
			{
				printf("已经没有可以取消标记的点了,请重新选择\n");
				continue;
			}
			switch (input)
			{
			case 1:
				Biaolei(xian, hang, lie);
				biaoji++;
				break;
			case 2:
				Clearlei(xian, hang, lie);
				biaoji--;
				break;
			default:
				break;
			}
		} while (input);
		
		do
		{
			printf("请输入要排查的坐标(行 列):");
			scanf("%d %d", &x, &y);
			if (x<1 || x>hang || y<1 || y>lie)
			{
				printf("输入的坐标有误,请重新输入\n");
				continue;
			}
			else if (xian[x][y] == '?' || xian[x][y] == '!')
			{
				printf("该位置已被标记,请重新输入\n");
				continue;
			}
			else if (xian[x][y] != '*')
			{
				printf("该坐标已排查过,请重新输入\n");
				continue;
			}
			break;
		} while (1);

		if (yin[x][y] == '1')
		{
			system("cls");
			printf("很遗憾,你被炸死了,游戏结束\n");
			print(yin, hang, lie);
			break;
		}
		else
		{
			int fang[8][2] = { {0,1},{0,-1},{-1,0},{1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
			xian[x][y] = '0';
			for (int i = 0; i < 8; i++)
			{
				if (yin[x + fang[i][0]][y + fang[i][1]] == '1')
					xian[x][y]++;
			}
			ge++;
			system("cls");
			print(xian, hang, lie);
			if (ge + leisum == hang * lie)
			{
				printf("恭喜你,排雷成功\n");
				break;
			}
		}
	}
}

那么,这个功能也结束啦,最后一个功能,也是最难的一个功能,那便是实现像网页版那样的点一个点,铺开一片。


3.点的铺开(相比其他较难)

这个部分的实现用到了函数的递归,也涉及一点的算法,与前俩个部分相比,这个功能的实现呈指数型上升,因为搜索这个知识具体来讲的话在这篇文章里很难讲清楚,所以这里我主要是讲思路,然后dfs,bfs这种搜索有空我会专门出一篇来讲解,所以,如果初学者代码没看懂也没关系, 我用的是**深度搜索(即dfs)**来实现这个功能的

首先,因为我们实现这个为了使内存损耗小,需要用到bool类型的数组,所以我们需要先在game.h的头文件内加入#include <stdbool.h>这一行代码

ok,说回正题,这个部分,虽然是这三个功能中最难实现的,但代码却是最简短的,如图,上图是自定义函数,下图是Findlei函数中略微改变的部分。

那么,我来简单说下这部分的思路,首先将方位数组(就那个fang数组)放在全局变量中,这样可以保证进入zhanlei函数时也可以使用,然后将当前坐标的访问标记成1,然后算出当前坐标附近有没有雷,如果有雷,就不进入函数,如果没雷,就进入zhanlei函数,然后通过八个方位来进行深搜,然后深搜过程中也不能忘了排雷个数的++,然后当程序运行完后,就展开一片了,函数功能也就实现了

当然,如果直接这样就结束,肯定也会有bug,就比如如果雷的个数在展开的那个函数中加到了排雷要求数,他判断排雷成功后,会直接输出明盘,但其实明盘那时候还并不是扫雷的最终结果,所以要输出暗盘,所以只需要再补上这么一行就可以啦

那么,到此,扫雷的所有功能讲解就全部讲完啦,接下来,最终版源码附上

拓展版源码

game.h

复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define EChang 9//好处是,想更改棋盘大小时候直接改这里就好了,不用在程序里一个个改,方便很多
#define EKuan 9

#define MChang 16
#define MKuan 16

#define HChang 30
#define HKuan 16

#define EChangs EChang+2
#define EKuans EKuan+2

#define MChangs MChang+2
#define MKuans MKuan+2

#define HChangs HChang+2
#define HKuans HKuan+2

#define ELeisum 10
#define MLeisum 40
#define HLeisum 99

void chu(char arr[][100], int hang, int lie, char fu);

void print(char arr[][100], int lie, int hang);

void set(char arr[][100], int hang, int lie, int ge);

void Findlei(char yin[][100], char xian[][100], int lie, int hang, int leisum);

game.c

复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

//初始化棋盘
void chu(char arr[][100],int hang,int lie,char fu)
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
			arr[i][j] = fu;
	}
}

//打印棋盘
void print(char arr[][100], int hang, int lie)
{
	for (int i = 0; i <= lie; i++)
		printf("%2d ", i);
	printf("\n");
	for (int i = 1; i <= hang; i++)
	{
		printf("%2d ", i);
		for (int j = 1; j <= lie; j++)
			printf("%2c ", arr[i][j]);
		printf("\n");
	}
}

//布置雷
void set(char arr[][100],int hang,int lie,int ge)
{
	while (ge--)
	{
		int h = 1 + rand() % hang;
		int l = 1 + rand() % lie;
		if (arr[h][l] == '0')
		{
			arr[h][l] = '1';
		}
		else
			ge++;
	}
}


static void Choose()
{
	printf("******************\n");
	printf("*** 1.标记     ***\n");
	printf("*** 2.取消标记 ***\n");
	printf("*** 0.排雷     ***\n");
	printf("******************\n");
}

static void menubiao()
{
	printf("*********************\n");
	printf("**** 1.?(不确定) ****\n");
	printf("**** 2.!(肯定)  ****\n");
	printf("*********************\n");
}

//标记雷
void Biaolei(char arr[][100],int hang,int lie)
{
	int x = 0, y = 0;
	int type = 0;
	while (1)
	{
		printf("请输入要标记的坐标:");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
		{
			printf("输入的坐标有误,请重新输入\n");
			continue;
		}
		else if (arr[x][y] == '?' || arr[x][y] == '!')
		{
			printf("该位置已被标记,请重新输入\n");
			continue;
		}
		else if (arr[x][y] != '*')
		{
			printf("该坐标已被排查,无法标记,请重新输入\n");
			continue;
		}
		break;
	}
	menubiao();
	do
	{
		printf("请输入要标记的类型:");
		scanf("%d", &type);
		if (type != 1 && type != 2)
		{
			printf("不存在该选项,请重新输入\n");
			continue;
		}
		break;
	} while (1);

	switch (type)
	{
	case 1:
		arr[x][y] = '?';
		print(arr, hang, lie);
		break;
	case 2:
		arr[x][y] = '!';
		print(arr, hang, lie);
		break;
	}
}

//取消标记雷
void Clearlei(char arr[][100], int hang, int lie)
{
	int x = 0, y = 0;
	int type = 0;
	while (1)
	{
		printf("请输入要取消标记的坐标:");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
		{
			printf("输入的坐标有误,请重新输入\n");
			continue;
		}
		else if (arr[x][y] != '?' && arr[x][y] != '!')
		{
			printf("该位置未被标记,请重新输入\n");
			continue;
		}
		break;
	}
	arr[x][y] = '*';
	print(arr, hang, lie);
}

//展开
bool vis[100][100];
int fang[8][2] = { {0,1},{0,-1},{-1,0},{1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
void zhanlei(char yin[][100], char xian[][100], int hang, int lie, int x, int y, int* ge)
{
	if (xian[x][y] != '0')
		return;
	for (int i = 0; i < 8; i++)
	{
		int x1 = x + fang[i][0];
		int y1 = y + fang[i][1];
		if (!vis[x1][y1] && yin[x1][y1] != '1' && x1 >= 1 && x1 <= hang && y1 >= 1 && y1 <= lie)
		{
			(*ge)++;
			xian[x1][y1] = '0';
			vis[x1][y1] = 1;
			for (int i = 0; i < 8; i++)
			{
				if (yin[x1 + fang[i][0]][y1 + fang[i][1]] == '1')
					xian[x1][y1]++;
			}
			zhanlei(yin, xian, hang, lie, x1, y1, ge);
		}
	}
}

//排查雷
void Findlei(char yin[][100], char xian[][100], int hang, int lie,int leisum)
{
	int x = 0, y = 0;
	int ge = 0;
	int biaoji = 0;
	int input = 0;

	while (1)
	{
		do
		{
			system("cls");
			print(xian, hang, lie);
			Choose();
			printf("请选择:");
			scanf("%d", &input);
			if (input != 1 && input != 2 && input != 0)
			{
				printf("输入有误,请重新输入\n");
			}
			if (biaoji >= leisum && input == 1)
			{
				printf("标记个数已超出雷数,请重新选择\n");
				continue;
			}
			if (biaoji <= 0 && input == 2)
			{
				printf("已经没有可以取消标记的点了,请重新选择\n");
				continue;
			}
			switch (input)
			{
			case 1:
				Biaolei(xian, hang, lie);
				biaoji++;
				break;
			case 2:
				Clearlei(xian, hang, lie);
				biaoji--;
				break;
			default:
				break;
			}
		} while (input);

		do
		{
			printf("请输入要排查的坐标(行 列):");
			scanf("%d %d", &x, &y);
			if (x<1 || x>hang || y<1 || y>lie)
			{
				printf("输入的坐标有误,请重新输入\n");
				continue;
			}
			else if (xian[x][y] == '?' || xian[x][y] == '!')
			{
				printf("该位置已被标记,请重新输入\n");
				continue;
			}
			else if (xian[x][y] != '*')
			{
				printf("该坐标已排查过,请重新输入\n");
				continue;
			}
			break;
		} while (1);

		if (yin[x][y] == '1')
		{
			system("cls");
			printf("很遗憾,你被炸死了,游戏结束\n");
			print(yin, hang, lie);
			break;
		}
		else
		{
			xian[x][y] = '0';
			vis[x][y] = 1;
			for (int i = 0; i < 8; i++)
			{
				if (yin[x + fang[i][0]][y + fang[i][1]] == '1')
					xian[x][y]++;
			}
			ge++;
			if(xian[x][y]=='0')
				zhanlei(yin, xian, hang, lie, x, y, &ge);
			system("cls");
			print(xian, hang, lie);
			if (ge + leisum == hang * lie)
			{
				system("cls");
				print(yin, hang, lie);
				printf("恭喜你,排雷成功\n");
				break;
			}
		}

	}
}

扫雷.c

复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "game.h"
void menu()
{
	printf("********************\n");
	printf("*****  1.play  *****\n");
	printf("*****  0.exit  *****\n");
	printf("********************\n");
}

void Lelve()
{
	printf("************************\n");
	printf("*** 1.easy(9*9 10)   ***\n");
	printf("*** 2.mid(16*16 40)  ***\n");
	printf("*** 3.hard(30*16 99) ***\n");
	printf("*** 4.自定义         ***\n");
	printf("************************\n");
}

void game()
{
	//初始化函数
	int input = 0;
	int chang = 0, kuan = 0, lei = 0;
	Lelve();
	printf("请选择:");
	do 
	{
		scanf("%d", &input);
		if (input < 1 || input>4)
		{
			printf("输入有误,请重新输入:");
			continue;
		}
		break;
	} while (1);
	char cun[100][100] = { 0 };
	char show[100][100] = { 0 };
	switch (input)
	{
	case 1:
		chu(cun, EKuans, EChangs, '0');
		chu(show, EKuans, EChangs, '*');

		//布置雷
		set(cun, EKuan, EChang, ELeisum);

		//打印棋盘
		print(show, EKuan, EChang);


		//排查雷,标记雷
		Findlei(cun, show, EKuan, EChang, ELeisum);
		break;

	case 2:
		chu(cun, MKuans, MChangs, '0');
		chu(show, MKuans, MChangs, '*');

		//布置雷
		set(cun, MKuan, MChang, MLeisum);

		//打印棋盘
		print(show, MKuan, MChang);


		//排查雷
		Findlei(cun, show, MKuan, MChang, MLeisum);
		break;

	case 3:
		chu(cun, HKuans, HChangs, '0');
		chu(show, HKuans, HChangs, '*');

		//布置雷
		set(cun, HKuan, HChang, HLeisum);

		//打印棋盘
		print(show, HKuan, HChang);


		//排查雷
		Findlei(cun, show, HKuan, HChang, HLeisum);
		break;

	case 4:
		printf("请输入长度 宽度 雷的个数(长<=50 宽<=16 雷数<长*宽)\n");//过大控制台太小,屏幕会乱跳,不好操控
		do
		{
			scanf("%d %d %d", &chang, &kuan, &lei);
			if (chang > 50 || chang < 1 || kuan>50 || kuan < 1 || chang * kuan <= lei)
			{
				printf("输入有误,请重新输入:");
				continue;
			}
			break;
		} while (1);
		chu(cun, kuan+2, chang+2, '0');
		chu(show, kuan+2, chang+2, '*');

		//布置雷
		set(cun, kuan, chang, lei);

		//打印棋盘
		print(show, kuan, chang);


		//排查雷
		Findlei(cun, show, kuan, chang, lei);
		break;
	}
}

void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls");
			printf("游戏开始\n");
			game();
			break;
		case 0:
			system("cls");
			printf("欢迎下次游玩\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

结语:

那么本篇文章的内容就到此为止啦,希望对你有所帮助,谢谢观看啦,如果觉得有帮助可以转发给朋友一起看哟,毕竟一个人走的快,一群人走的远嘛,有竞争对手才有动力嘛

相关推荐
gopyer20 分钟前
180课时吃透Go语言游戏后端开发3:Go语言中其他常用的数据类型
开发语言·游戏·golang·游戏后端开发
GilgameshJSS36 分钟前
STM32H743-ARM例程9-IWDG看门狗
c语言·arm开发·stm32·单片机·嵌入式硬件·学习
Hello_Embed44 分钟前
STM32 智能垃圾桶项目笔记(一):超声波模块(HC-SR04)原理与驱动实现
c语言·笔记·stm32·单片机·嵌入式软件·嵌入式项目
菠萝地亚狂想曲44 分钟前
极简文件列表
c语言
海琴烟Sunshine1 小时前
Leetcode 26. 删除有序数组中的重复项
java·算法·leetcode
PAK向日葵1 小时前
【算法导论】NMWQ 0913笔试题
算法·面试
PAK向日葵1 小时前
【算法导论】DJ 0830笔试题题解
算法·面试
PAK向日葵1 小时前
【算法导论】LXHY 0830 笔试题题解
算法·面试
麦麦麦造2 小时前
DeepSeek突然发布 V3.2-exp,长文本能力加强,价格进一步下探
算法
wdfk_prog2 小时前
[Linux]学习笔记系列 -- lib/sort.c 通用的排序库(Generic Sorting Library) 为内核提供标准的、高效的排序功能
linux·运维·c语言·笔记·stm32·学习·bug