呆呆版—扫雷(详细讲解+代码)

大家好,今天我们来实现扫雷小游戏,对就是windows自带的扫雷小游戏,不过这里是用代码实现的。废话不多讲,我们开搞!

点击查看图片来源

首先,我们是从网上搞来一张扫雷的图片,它是这样的:
在这里插入图片描述

很经典对吧,好的,接下来我们进入学习阶段,我先介绍一下,我用的编译器是vs2019,大家如果是初学建议用这个,大佬看个乐呵,哈哈。

第一步:代码模块化+小菜单

在这里插入图片描述

我们创建三个文件,一个头文件game.h,两个源文件game.c和test.c。它们的作用如下:

//game.h主要用来放函数的声明、宏定义以及所用到的库里的头文件。
//game.c主要用来放实现函数的的代码。
//test.c主要进行函数的调用,检测代码的实现。

好了,我们上来打开test.c,写下如下的代码:

#include"game.h"

void menu()
{
	printf("****************************************\n");
	printf("****************************************\n");
	printf("*************   1.play   ***************\n");
	printf("*************   0.exit   ***************\n");
	printf("****************************************\n");
	printf("****************************************\n");

}
int main()
{
    int num = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &num);
		switch (num)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (num);
	return 0;
}

上面这部分是由三部分构成,熟悉C语言的同学应该知道,首先是主函数main,其次是一个do…while循环语句,再就是switch的运用了。这部分很简单,我接着下一个部分。

第二步:分析扫雷游戏

首先哈,我们先看一下我写的game.h里面的东西,我们对扫雷进行分析:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 9//行
#define COL 9//列

#define ROWS ROW+2//行+2
#define COLS COL+2//列+2
#define EASY_RET 10//雷的个数


//函数的声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp);//初始化每一个方块
void DisPlay(char board[ROWS][COLS], int row, int col);//显示界面
void SetMine(char board[ROWS][COLS], int row, int col);//设置雷的个数
ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//扫雷过程

好了,我们先对扫雷进行分析,扫雷游戏是有一个界面的,界面上有一个一个的小方块,我们假定有81个小方块,10个雷。那我们需要定义一个二维数组,这个二维数组最好是char类型的,因为我们要把方块放上一个’*',表示方块未被点开的状态。当然,我们发现,靠近边角的的方块我们也需要计算它们附近雷的个数,但是,如果数组定义为81个小方块,那就会出现越界访问的问题;所以我们给左右各加上一列,上下各加上一行;这就是ROWS和COLS的被定义出来的意义。除了二维数组,我们还需要进行,小方块初始化,雷的设置,扫雷的实现等等。接下来我们来搞game.c。

第三步:游戏逐步实现

大家都知道,我们在test.c里面放了一个switch语句,我们把它带过来:

switch (num)
		{
		case 1:
			game();//这就是我们要实现的部分
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

我们开始的菜单就是,num=1我们就是开始游戏,所以case 1我们进入游戏,回想一下,游戏开始是怎么样的,哦,小方块摆在你面前,你要进行选择,接下来,开始:

void game()
{
	//设置两个数组,一个是存放雷的信息,另一个是排查雷的信息
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
    
	//初始化数组,将两个数组运用函数进行初始化。
	//mine数组是存放雷的数组,当没有雷的时候,我们初始化为全是'0'
	InitBoard(mine, ROWS, COLS, '0');
	//show数组是排查雷的数组,未排查时初始化为'*'
	InitBoard(show, ROWS, COLS, '*');
    
    //显示一下我的每个数组,检查是否初始化成功
    //DisPlay(mine, ROW, COL);
	DisPlay(show, ROW, COL);


}

当然,我们不能瞎搞,所以得一步一步来,先定义两个二维数组,对二维数组进行初始化;好的,这里有同学就问了,数组初始化那不就用for循环,简简单单就搞定,哪还要写函数实现?先别急,我们一步一步来,定义数组用的是ROWS和COLS,是因为避免数组越界,我们为什么要写一个函数进行初始化,我们来看看这个初始化函数吧:

void InitBoard(char board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = ?;
		}
	}
}

就是嘛,你这还不一样是两for循环,大家注意,如果简单写两个循环,我们需要设定的值就乱了,我们一共有两个数组,一个数组放雷,没有雷初始化为’0’,另一个数组排查雷,没有排查时初始化为’*';这就需要我们对函数传一个值,把需要初始化成什么样的值传递过来,让我们初始化可以进行多个数组的初始化。改成下面这样:

void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = tmp;
		}
	}
}

好了,初始化搞完了,我们需要进行整个界面的显示,这个时候我们是怎么做的呢?

void DisPlay(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}

}

这时候有人又问,这显示没什么东西,就是一个二维数组的遍历呗,咔的一下写了出来。你说的确实没错,但是,我们虽然没有UI,我们也要尽量去考虑一下观感,所以我们要进行代码的调整:

void DisPlay(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("-----扫雷小游戏-----\n");//加上一个小修饰
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);//打印一下列的序号
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);//打印一下行的序号
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-----扫雷小游戏-----\n");

}

这样搞一下,肯定比之前那个显示效果好一点:

在这里插入图片描述

好了,接下来,还要实现雷的设置,和排雷:

第四步:雷的设置,排雷(核心)

好了,我们先思考一下,设置雷是不是要考虑随机性,所以这里我们要用到srand,rand两个关于随机数的库函数。其次,排雷的时候,点开一个方块之后,中间有一个数字,表示周围有几个雷,这是我们要实现的,最后还要判断输赢,不能一直排,那就没有意义了。接下来,看代码:

void game()
{
	//设置雷
	SetMine(mine, ROW, COL);
	//扫雷
	ClearMine(mine, show, ROW, COL);

}
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int ret = EASY_RET;
	while (ret)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			ret--;
		}
	}
}

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

ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int m = 0;
	int n = 0;
	int w = 0;//排查到的非雷的个数

	while (w < row * col - EASY_RET)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &m, &n);
		if (m >= 1 && m <= row && n >= 1 && n <= col)//判断坐标合法性
		{
			if (show[m][n] != '*')
			{
				printf("该坐标已经被排查,不能重复排查\n");//判断坐标是否二次排查
			}
			else
			{
				if (mine[m][n] == '1')
				{
					printf("Boom,你被炸死了!\n");
					DisPlay(mine, ROW, COL);
					break;
				}
				else
				{
					w++;
					//统计mine数组m,n坐标附近雷的个数
					int count = get_mine_count(mine, m, n);
					show[m][n] = count + '0';
					DisPlay(show, ROW, COL);
				}
			}
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	if (w == row * col - EASY_RET)
	{
		printf("恭喜你,排雷成功\n");
		DisPlay(mine, ROW, COL);
	}
}

好了,接下来有很多人就问,为什么要在设置雷的函数里面,传的值是ROW、COL呢?这个很简单,因为我们至始至终都是在那81个格子里面玩的,外面那两行两列只是用来防止数组越界,所以不需要在那两行布置雷。

剩下的就很简单,解释一下部分的条件,row * col - EASY_RET意思是非雷的个数,然后就是get_mine_count函数是用来统计周围雷的个数,最后的-'0’是把字符型转换为整型。

最后,本次的扫雷小游戏到这就结束了,接下来,附上完整代码,感谢大家!!!

点击查看图片来源

game.h

#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_RET 10


//函数的声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp);//初始化每一个方块
void DisPlay(char board[ROWS][COLS], int row, int col);//显示界面
void SetMine(char board[ROWS][COLS], int row, int col);//设置雷的个数
ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//扫雷过程

game.c

#include"game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = tmp;
		}
	}
}

void DisPlay(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("-----扫雷小游戏-----\n");
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-----扫雷小游戏-----\n");

}

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int ret = EASY_RET;
	while (ret)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			ret--;
		}
	}
}

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

ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int m = 0;
	int n = 0;
	int w = 0;//非雷的个数

	while (w < row * col - EASY_RET)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &m, &n);
		if (m >= 1 && m <= row && n >= 1 && n <= col)
		{
			if (show[m][n] != '*')
			{
				printf("该坐标已经被排查,不能重复排查\n");
			}
			else
			{
				if (mine[m][n] == '1')
				{
					printf("Boom,你被炸死了!\n");
					DisPlay(mine, ROW, COL);
					break;
				}
				else
				{
					w++;
					//统计mine数组m,n坐标附近雷的个数
					int count = get_mine_count(mine, m, n);
					show[m][n] = count + '0';
					DisPlay(show, ROW, COL);
				}
			}
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	if (w == row * col - EASY_RET)
	{
		printf("恭喜你,排雷成功\n");
		DisPlay(mine, ROW, COL);
	}
}

test.c

#include"game.h"

void menu()
{
	printf("****************************************\n");
	printf("****************************************\n");
	printf("*************   1.play   ***************\n");
	printf("*************   0.exit   ***************\n");
	printf("****************************************\n");
	printf("****************************************\n");

}


void game()
{
	//1.设置两个数组,一个是存放雷的信息,另一个是排查雷的信息
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	//2.初始化数组,将两个数组运用函数进行初始化。
	//mine数组是存放雷的数组,当没有雷的时候,我们初始化为全是'0'
	InitBoard(mine, ROWS, COLS, '0');
	//show数组是排查雷的数组,未排查时初始化为'*'
	InitBoard(show, ROWS, COLS, '*');

	//4.设置雷
	SetMine(mine, ROW, COL);

	//3.显示一下我的每个数组,检查是否初始化成功
	//DisPlay(mine, ROW, COL);
	DisPlay(show, ROW, COL);

	//5.扫雷
	ClearMine(mine, show, ROW, COL);

}


int main()
{
	int num = 0;
	//设置随机值
	srand((size_t)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &num);
		switch (num)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (num);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值