C语言扫雷游戏的简单实现!保姆级简单教程!看完就会!

1.扫雷游戏分析和设计

1.1扫雷游戏的功能说明

使用 控制台实现经典的扫雷游戏
游戏可以通过菜单实现继续玩或者退出游戏
扫雷的棋盘是9*9的格子
默认随机布置10个雷
可以排查雷
如果位置不是雷,就显示周围有几个雷
如果位置是雷,就炸死游戏结束
把除10个雷之外的所有雷都找出来,排雷成功,游戏结束
游戏的界面
                                            

       初始界面                               排雷界面                                                排雷失败界面


 1.2 游戏的分析和设计

 1.2.1 数据结构的分析

扫雷的过程中, 布置的雷和排查出的雷的信息都需要存储 ,所以我们需要⼀定的数据结构来 存储这些信息
因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们首先想到的就是 创建⼀个9*9的数组 来存放信息。
                                                             
                                                                           空棋盘
那如果这个位置布置雷,我们就存放1,没有布置雷就存放0。

                                                            

                                                                     布置好雷的棋盘

越界问题需注意!

假设我们排查(2,5)这个坐标时,我们访问周围的⼀圈8个黄色位置,统计周围雷的个数是1。
假设我们排查(8,6)这个坐标时,我们访问周围的⼀圈8个黄色位置,统计周围雷的个数时,最下面的三个坐标就会越界, 为了防止越界,我们在设计的时候,给数组扩大⼀圈,雷还是布置在中间的9*9的坐 标上,周围⼀圈不去布置雷就行 ,这样就解决了 越界的问题 。所以我们将存放数据的数组创建成 11*11 比较合适。

                                                  

                            排雷的假设界面                                      多加一圈防止越界

优化打印类型!初始化为同一类型!
再继续分析,棋盘上雷的信息(1)和非雷的信息(0),假设我们排查了某 一个位置后,这个坐标处不是雷,这个坐标的 周围有1 个雷,那我们需要将排查出的雷的数量信息记录存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪里呢?如果存放在布置雷的数组中, 1 1 就可能或产生混淆和打印上的困难
这里我们肯定有办法解决,比如:雷和非雷的信息不要使用数字,使用某些字符就行,这样就避免冲突了,但是这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便。
这里我们采用另外⼀种方案,我们专门给一个棋盘(对应一个数 mine)存放布置好的雷的信息,再给另外一个棋盘(对应另外一个数组show )存放排查出的雷的信息。这样就互不干扰了,把雷布置到 mine 数组,在 mine 数组中排查雷,排查出 的数据存放在show 数组,并且打印 show 数组的信息给后期排查参考。
同时为了保持神秘, show 数组开始时初始化为字 符 '*' ,为了保持两个数组的类型⼀致,可以使用同⼀套函数处理, mine 数组最开始也初始化为字符 '0' ,布置雷改成 '1'

                                               

                        mine数组初始化状态                              show数组初始化状态

对应的数组应该是:
char mine[11][11];//⽤来存放布置好的雷的信息
char show[11][11];//⽤来存放排查出的雷的个数信息

1.2.2 文件结构的设计

小江之前学习了多文件的形式对函数的声明和定义,这里我们实践⼀下,我们设计三个文件(其他方法也可以):

1. test.c //⽂件中写游戏的测试逻辑 
2. game.c //⽂件中写游戏中函数的实现等
3. game.h //⽂件中写游戏需要的数据类型和函数声明等

 2. 扫雷游戏的代码实现

2.1 test.c上扫雷游戏的简单逻辑实现和初始化棋盘和打印棋盘

首先,玩游戏必不可少的就是菜单和游戏选项;其次,游戏需要多次进行,少不了循环。

排雷游戏设置步骤:开始——>初始化棋盘——>打印棋盘——>布置雷——>排雷——>结束

#include"game.h"
//自定义头文件用“ ”   
//game.h中包含#include<stdio.h>等一系列头文件,变量,函数定义——>这样只用写一遍,3个文件就都能使用了
void menu()
{
	printf("*********************************\n");
	printf("**********    1.Play   **********\n");
	printf("**********    2.Exit   **********\n");
	printf("*********************************\n");
}
int main()
{
	//输入
	int input = 0;
	//选择是否玩游戏
	do
	{
		menu();
		printf("请选择是否进行游戏:");
		scanf("%d ", &input);
		switch (input)
		{
		case 1:
			game();//如果玩游戏,就进入game函数实现游戏,我们这里先注重逻辑,之后再完善game()
			break;
		case 0:
			printf("结束游戏\n");
			break;
		default:
			printf("输入错误,请重新输入:\n");
			break;
		}
	} while (input);//为了保证多次游戏和可以重来的机会,需要循环
	return 0;
}

2.1.1 test.c中game( )的初步逻辑实现

初步编写初始化棋盘函数和打印棋盘函数函数

void game()
{
	//创建数组存放
	char mine[ROWS][COLS];//存放布置好的雷和非雷  ROWS代表行数  COLS代表列数
	char show[ROWS][COLS];//存放排查出的雷的信息
	//初始化棋盘
	//1.mine数组初始化为‘0’
	//2.show数组初始化为‘*’
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	DisplayBoard(mine, ROW, COL);//在这里我们可以先打印出来看看效果,但mine对应端棋盘应当隐藏起来,保证游戏“神秘化”
	DisplayBoard(show, ROW, COL);
	//布置雷

	//排查雷

}

2.1.2 game.h中的初步定义和初步函数声明

自定义行列数和初始化棋盘函数和打印棋盘函数

#define _CRT_SECURE_NO_WARNINGS 1//保证scanf()可以正常运行,否则vs2022报错或警告
//游戏的函数的实现
#include<stdio.h>

//用字符代替数字,方便以后更改,如果行列用数字表示,以后更改很麻烦,需要多处更改
//ROW———>9行
//COL———>9列 
#define ROW 9
#define COL 9

//ROWS———>11行
//COLS———>11列 
#define ROWS ROW+2
#define COLS COL+2

//函数的声明

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);

//布置雷


//排查雷

2.1.3 game.c中初始化棋盘和打印棋盘的实现

1.总共11*11,但是其中只有9*9需要打印出来,最外圈隐藏起来防止越界

2.打印出行数列数(0-9)更方便输入坐标

//游戏的实现
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i <rows; i++)//初始化11*11
	{
		int j = 0;
		for (j = 0; j <cols; j++)
		{
			board[i][j] = set;
		}
	}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("**********---扫雷游戏---**********\n");
	int i = 0;
	for (i = 0; i <= col; i++)//打印0-9列
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)//共11行,只打印1-9行
	{
		printf("%d ", i);//打印0-9行
		int j = 0;
		for (j = 1; j <= col; j++)//共11列,只打印1-9列
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

                                        

                                                            打印初始化棋盘的效果

别忘记隐藏DisplayBoard(mine, ROW, COL);

                          

                                                           隐藏后的初始化棋盘的效果

2.2 布置雷的实现

1.雷的个数

雷的个数要确定,用字符表示方便以后更改

2.雷的位置

雷每次游戏时需要重新随机布置——>需要随机数——>随机数函数

随机函数不了解的宝子,链接放这里了:http://t.csdn.cn/0ETFB

2.2.1 test.c中game( )的布置雷和test.c中main( )随机函数的初步实现

布置雷的范围在9*9之中,只需传参ROW(9)和 COL(9)即可,排雷同样

//void game()中
//布置雷
	SetMine(mine, ROW, COL);//将雷布置在mine中即可
	DisplayBoard(mine, ROW, COL);//打印一下看一下结果,正式游戏时需要隐藏

//int main()中
//输入
	int input = 0;
	srand((unsigned int)time(NULL));//放在main()中,只需调用一次
	//选择是否玩游戏

2.2.2 game.h的布置雷的函数声明和雷个数和随机数头文件的定义

#include<stdlib.h>
#include<time.h>

#define EASY_COUNT 10

//布置雷
void SetMine(char mine[ROWS][COLS],  int row, int col);

2.2.3 game.c中的布置雷的实现

涉及生成随机数的范围的知识点,上面的链接中有哦。

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	//布置雷需要每次都随机布置---->生成随机数
	int count = EASY_COUNT;
	//设置随机数,范围在1-9行,1-9列,每次游戏都需要重新设置
	while (count)
	{
		int x = 1 + rand() % row ;//任何数除以9余数都在0-8之中,余数+1得到范围1-9;
		int y = 1 + rand() % col ;
		//考虑到只有没有布置过雷的地方才能布置---->if()条件限制
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;//共10个雷每次布置完一个次数-1直至为0,结束循环
		}
	}
}

                      

                                                               布置好雷的棋盘的效果

 2.3排雷的实现

2.3.1 test.c中game( )的排雷初步实现

//排查雷
	FindMine(mine ,show , ROW, COL);//FindMine函数涉及到2个数组:从mine数组中排查,将排查出的信息放到show数组中

2.3.2 game.h中排雷的函数声明

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

2.3.3 game.c中排雷的实现

1.排雷有次数限制(总次数 = 行 * 列 — 雷个数)

如果被炸死,则结束游戏;如果直到排完都没被炸死则成功。

2.雷的分布显示

没被炸死时,给出坐标周围的雷个数,如下图所示:

                                   

3.周围的雷的个数统计——int count=GetMineCount(mine, x, y)

将统计的周围的雷的个数统计完后并相加得到总个数,(x,y)即为排查的坐标

x:行数

y:列数

x-1,y-1x-1,yx-1,y+1
x,y-1x,yx,y+1
x+1,y-1x+1,yx+1,y+1

但是,字符‘0’和‘1’怎么相加减呢?

根据ASCLL码表可知,48——>'0'         49——>'1'

                               

java数据类型转换_chondeng的博客-CSDN博客

 所以,‘1’ - ‘0’ =49 -48 =1          1 + ‘0’ =1 + 48 =‘1’

数字字符 - ‘0’ =对应数字

int GetMineCount(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 + 1][y - 1] +
		mine[x + 1][y + 1] +
		mine[x + 1][y] +
		mine[x][y + 1] - 8 * '0');
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)//排雷次数限制
	{
		printf("请输入要排查的坐标:");//需循环多次输入---->while()循环
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);//显示mine对应的棋盘,看看雷的分布
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);//统计输入坐标周围雷的个数
				show[x][y] = count + '0';//将统计到的输入坐标周围雷的个数存放到show对应的棋盘中
				DisplayBoard(show, ROW, COL);
				win++;//每排一次雷次数+1,直到排查完没有雷的地方
			}
		}
		else   //坐标不满足范围
		{
			printf("输入非法坐标,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);//显示mine对应的棋盘,看看雷的分布
	}
}

                                                                   排雷的实现

2.4 总代码

2.4.1 test.c

#define _CRT_SECURE_NO_WARNINGS 1//保证scanf()可以正常运行,否则vs2022报错或警告
#include "game.h"//包含自定义的头文件用“ ”
//游戏的逻辑实现
void menu()
{
	printf("*********************************\n");
	printf("**********    1.Play   **********\n");
	printf("**********    2.Exit   **********\n");
	printf("*********************************\n");
}
void game()
{
	//创建数组存放
	char mine[ROWS][COLS];//存放布置好的雷和非雷
	char show[ROWS][COLS];//存放排查出的雷的信息
	//初始化棋盘
	//1.mine数组初始化为‘0’
	//2.show数组初始化为‘*’
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	//DisplayBoard(mine, ROW, COL);//mine对应端棋盘应当隐藏起来
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);//将雷布置在mine中即可
	//DisplayBoard(mine, ROW, COL);//打印一下看一下结果
	//排查雷
	FindMine(mine ,show , ROW, COL);//FindMine函数涉及到2个数组:从mine数组中排查,将排查出的信息放到show数组中
}
int main()
{
	
	//输入
	int input = 0;
	srand((unsigned int)time(NULL));//放在main()中,只需调用一次
	//选择是否玩游戏
	do
	{
		menu();
		printf("请选择是否进行游戏:");
		scanf("%d ", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("结束游戏\n");
			break;
		default:
			printf("输入错误,请重新输入:\n");
			break;
		}
	} while (input);//为了保证多次游戏和可以重来的机会,需要循环
	return 0;
}

2.4.2 game.h

#define _CRT_SECURE_NO_WARNINGS 1//保证scanf()可以正常运行,否则vs2022报错或警告
//游戏的函数的实现
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define EASY_COUNT 10

//函数的声明

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);

//布置雷
void SetMine(char mine[ROWS][COLS],  int row, int col);

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

2.4.3 game.c

//游戏的实现
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i <rows; i++)
	{
		int j = 0;
		for (j = 0; j <cols; j++)
		{
			board[i][j] = set;
		}
	}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("**********---扫雷游戏---**********\n");
	int i = 0;
	for (i = 0; i <= col; i++)//打印0-9列
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)//共11行,只打印1-9行
	{
		printf("%d ", i);//打印0-9行
		int j = 0;
		for (j = 1; j <= col; j++)//共11列,只打印1-9列
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	//布置雷需要每次都随机布置---->生成随机数
	int count = EASY_COUNT;
	//设置随机数,范围在1-9行,1-9列,每次游戏都需要重新设置
	while (count)
	{
		int x = 1 + rand() % row ;//任何数除以9余数都在0-8之中,余数+1得到范围1-9;
		int y = 1 + rand() % col ;
		//考虑到只有没有布置过雷的地方才能布置---->if()条件限制
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;//共10个雷每次布置完一个次数-1直至为0,结束循环
		}
	}
}
int GetMineCount(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 + 1][y - 1] +
		mine[x + 1][y + 1] +
		mine[x + 1][y] +
		mine[x][y + 1] - 8 * '0');
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)//排雷次数限制
	{
		printf("请输入要排查的坐标:");//需循环多次输入---->while()循环
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);//显示mine对应的棋盘,看看雷的分布
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);//统计输入坐标周围雷的个数,先初步定义
				show[x][y] = count + '0';//将统计到的输入坐标周围雷的个数存放到show对应的棋盘中
				DisplayBoard(show, ROW, COL);
				win++;//每排一次雷次数+1,直到排查完没有雷的地方
			}
		}
		else   //坐标不满足范围
		{
			printf("输入非法坐标,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);//显示mine对应的棋盘,看看雷的分布
	}
}
	
	

3. 扫雷游戏的扩展

是否可以选择游戏难度
简单 9*9 棋盘,10个雷
中等 16*16棋盘,40个雷
困难 30*16棋盘,99个雷
如果排查位置不是雷,周围也没有雷,可以展开周围的一片
是否可以标记雷
是否可以加上排雷的时间显示
在线扫雷游戏: https://www.msn.cn/zh-cn/play/microsoft-minesweeper/cg-msminesweeper
PS:小江目前是个新手小白,这个扫雷游戏也只是最初级的,肯定还有一些功能需要完善,欢迎大家在评论区讨论哦!有问题也可以讨论的!
  • 40
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值