扫雷程序详解

扫雷逻辑

创建一个项目test.c 储存游戏逻辑

基础架构

我们先说基础架构,明白扫雷的架构原理,明白原理后扫雷并不难;

打印菜单

打印菜单不需要返回值,创建void函数,

“printf”函数需要调用库函数 #include <stdio.h>

后面可以把这个库函数放入 game.h,这样调用game.h就可以了方便很多
打印菜单:

void menu()
{
	printf("***************")
	printf("*** 1. play ***")
	printf("*** 0. exit ***")
	printf("***************")
}

函数输入“1”开始游戏,输入“0”退出游戏;

初始化数组

数组结构
设置两个数组 “mine”, “show”,一个用来随机输入雷,一个用来排雷,如果用一个数组雷用“1”表示非雷为“0”,排雷会显示周围雷的个数,这样显示雷的数会和雷1“”冲突,如果说给雷换成其他符号,那样数组太杂,还是不方便,所以用两个数组表示
,最开始“mine”数组没有雷所以要初始化成全是0,“show”数组最开始没有排查所以初始化成一种符号,这里用“*”演示,又因为当排雷时是要把所选位置一圈都检测一遍,如果所选位置是边上位置时会越界,所以把数组扩大一圈,防止出现越界。
创建数组:

char mine[ROWS][COLS];//'0'
char show[ROWS][COLS];//'*'

ROWS 是 ROW + 2 “ROW”(行)
COLS 是 COL + 2 “COL”(列)
如果写mine[11][11]这种后续想要改变行列比较麻烦,所以用’ROW’'COLS’表示;
初始化数组(int初始化):

InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');

数组初始化之后就要打印棋盘;

打印棋盘

之前设置了行列位“ROW”“COL”,打印棋盘多出来的那一圈是为了防止越界不需要展示,所以不用“ROWS”“COLS”;
打印棋盘:

DisplavBoard(mine,ROWS,COLS):
DisplayBoard(show,ROWS,COLS);

打印mine是为了给我们自己看调试用的,代码完成时,要注释掉“DisplavBoard(mine,ROWS,COLS):”

布雷

布雷要知道在那个数组,多少行多少列的范围所以要有“mine”“ROW”“COL”
布雷:

SetMine(mine,ROW,COL);

排雷

排雷需要两个数组才能进行,多少行多少列的范围要有“show”“mine”“ROW”“COL”
排雷:

FindMine(mine,show,ROW,COL);

这些是扫雷的架构,看懂这些后面就很好理解;

函数入口(main函数)

随机数值

扫雷的雷肯定是要随机的,如果固定那几个地方是雷,那就太没意思了,想要随机就需要用到"srand()“,还得让随机,那么就需要一个能一直变化的变量"time” 给time一个空指针:“time(NULL)”;再把"time(NULL)“强制转换成int类型”(unsigned int) time(NULL));“最后加上"srand()”,一个设置随机数值的函数就创建好了:

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

然后就是进行菜单的选择:

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	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;
}

输入“1”进入游戏;
输入“0”退出游戏;
输入其他的数字提示错误重新输入;
玩完之后进while循环,这样可以反复的玩,不用重新进入;
完整逻辑代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"


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

void game()
{
	//数组
	char mine[ROWS][COLS];//'0'
	char show[ROWS][COLS];//'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	
	//棋盘打印
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	
	//布雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	
	//排雷
	FindMine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	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;
}

游戏的函数声明

创建game.h 储存游戏的函数声明

头文件

.h后缀也就是头文件,头文件有什么作用呢?

1.方便开发:包含一些文件需要的共同的常量,结构,类型定义,函数,变量申明;

2.使函数的作用域从函数声明的位置开始,而不是函数定义的位置(实践总结)

3 .提供接口:对一个软件包来说可以提供一个给外界的接口(例如: stdio.h)。
通过阅读上文游戏逻辑,可以知道有4个函数需要声明:

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

//打印棋盘的
void DisplayBoard(char board[ROWS][COLS], int rows, int cols);

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

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

不仅如此还有一些库函数需要放在头文件里方便开发:

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

调用了“ROW”“COL”但是它们的数值还没有设置,这篇博客做一个简易的扫雷就9*9吧:

#define ROW 9
#define COL 9

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

布雷的函数咱们写了,但是并没有写数值:

#define EASY_COUNT 10

头文件完整代码:

#pragma once
#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 rows, int cols);

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

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

这样咱们头文件就写好了,声明完函数就要实现函数了;

扫雷的实现

初始化棋盘

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

括号里大家会有问题的是“char set”,这是什么?
在游戏逻辑的时候两个数组要初始化两种字符:

char mine[ROWS][COLS];//'0'
char show[ROWS][COLS];//'*'
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');

一个“0”,一个“*”;所以用set表示初始化该数组,初始化成该数组对应字符;
所以初始化数组:

#define _CRT_SECURE_NO_WARNINGS 1

#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 rows, int cols);

棋盘
既然是棋盘就需要有行列的序号:

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

棋盘打印完了,也可以加上扫雷游戏几个字:

int i = 0;
	printf("--------扫雷游戏-------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

在这里插入图片描述

棋盘打印完整代码:

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

布雷

void DisplayBoard(char board[ROWS][COLS], int rows, int cols);

在头文件里咱们设置过雷的数量了需要调用一下:

int count = EASY_COUNT;

然后做一个循环,每放一颗雷就“count–”,
坐标 x,y的范围由“ROW”“COL”决定:

int x = rand() % row + 1;
int y = rand() % col + 1;

因为是随机的x,y,有可能会出现重复,所以要确定mine[x][y]的位置是“0”,也就是没雷才能放:

if (mine[x][y] == '0')

这样就完成了布雷,布雷完整代码:

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;

	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

排雷

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)

首先排查坐标,坐标不合法重新输入:

while (1)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
		{}
		else
		{
			printf("坐标非法,重新输入\n");
		}

坐标合法,排查这个位置是不是雷,是雷(“1”)扑街,然后显示mine棋盘别让玩家死不瞑目,如果不是雷,统计周围有几个雷:

if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}

数字字符和数字之间的转化规律,字符“0”的ASCII为48
字符1,2,3,4… ASCII依次累加1;
所以字符“1”-字符“0”= ASCII“49”- ASCII“48” = 数字“1”;
字符“2”-字符“0”= ASCII“50”- ASCII“48” = 数字“2”
知道了转化规律

int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;

统计周围的数值,count + ‘0’ 转化成字符放入show[x][y];
GetMineCount是统计mine数组周围有几个雷那怎么实现呢看下图:
在这里插入图片描述
[x][y]周围有8个格子,让8个格子加在一起减去 8*'0’就知道附近多少雷了:

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

完整排雷代码:

static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][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("请输入要排查的坐标:>");
		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);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

完整代码实现:

#define _CRT_SECURE_NO_WARNINGS 1

#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)
{
	int i = 0;
	printf("--------扫雷游戏-------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++) 
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("--------扫雷游戏-------\n");
}

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;

	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][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("请输入要排查的坐标:>");
		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);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

至此一个扫雷程序就写完了,但是正常的扫雷每开一个格子都是会连带着一片空白格一起开;但是这个程序无法实现,我也在研究怎样才能做出正常的扫雷,

关注我,第一时间了解我的动态。

下期见,拜拜~

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y.Ge

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值