首页 > 编程语言 >扫雷程序详解

扫雷程序详解

时间:2023-07-30 10:02:41浏览次数:47  
标签:ROWS int void 程序 mine COLS char 详解 扫雷

文章目录

扫雷逻辑

创建一个项目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);

扫雷程序详解_数组_02


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

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");

扫雷程序详解_数组_03

棋盘打印完整代码:

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数组周围有几个雷那怎么实现呢看下图:

扫雷程序详解_#define_04


[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);
	}
}

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

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

下期见,拜拜~

我的CSDN:Y.Ge_-CSDN博客

大家可以关注我CSDN账号,优先发布!

标签:ROWS,int,void,程序,mine,COLS,char,详解,扫雷
From: https://blog.51cto.com/u_16182079/6898637

相关文章

  • VS选择Visual C++中的控制台项目和空项目、Windows桌面应用程序三者之间有什么区别?
    在VisualStudio中创建C/C++项目时,可以选择控制台项目、空项目和Windows桌面应用程序,它们有以下区别:控制台项目(ConsoleApplication):这种项目类型适用于命令行应用程序的开发。它提供一个命令行界面,可以在控制台中进行输入和输出操作,通常用于简单的控制台程序,如计算器、文件......
  • 图注意力网络论文详解和PyTorch实现
    前言 图神经网络(gnn)是一类功能强大的神经网络,它对图结构数据进行操作。它们通过从节点的局部邻域聚合信息来学习节点表示(嵌入)。这个概念在图表示学习文献中被称为“消息传递”。本文转载自P**nHub兄弟网站作者|EbrahimPichka仅用于学术分享,若侵权请联系删除欢迎关注公......
  • oracle 参数建议和详解
    隐藏参数_optimizer_adaptive_cursor_sharing建议关闭隐藏参数_optimizer_adaptive_cursor_sharing只有在开启了_optim_peek_user_binds后才有意义,它可以防止不合理的执行计划,但是在实际生产环境中,_optimizer_adaptive_cursor_sharing会带来各种问题,产生bug,因此建议关闭该参......
  • c语言预处理详解
    //externintADD(intx,inty);//声明引用外部文件//c语言预处理//文本文件翻译+链接二进制文件运行//test.cpp————————》test.exe————————》//编译器翻译环境链接器执行环境//test.obj(目标文件)////(linux系统)翻译器:/......
  • Vue3 Vite electron 开发桌面程序
    Electron是一个跨平台的桌面应用程序开发框架,它允许开发人员使用Web技术(如HTML、CSS和JavaScript)构建桌面应用程序,这些应用程序可以在Windows、macOS和Linux等操作系统上运行。Electron的核心是Chromium浏览器内核和Node.js运行时环境。Chromium内核提供了现代浏览器的功能,例如HTML......
  • nsq整体架构及各个部件作用详解
    文章目录        前言        nsq的整体架构图        部件:nsqd        部件:nsqlookupd        部件:nsq连接库        部件:nsqadmin 前言我们讲了nsq是什么,有什么用,它的内部组成部件,下载,单点搭建,集群搭建等等这一篇博客,我们开始......
  • 性能优化之详解各种指标
    前言上篇文章最后提到了我们可以通过performance的一些属性对性能做统计,我们会发现performance对象下有非常多的属性,远不止上篇文章提到的DOMContentLoaded与Load这两个事件。或许你在浏览器控制台见过它们这些身影:DCL、LCP、FP、FCP、L这里的DCL与L就是我们上篇文章介绍的DOMConte......
  • 操作无法完成.因为其中的文件夹或文件已在另程序打开
    看到这个标题,肯定会认为这个传奇技术什么的没有任何的关系吧,直接的说,的确是没有关系,如果使用或者架设了某一个传奇版本,在删除文件中,如果出现操作无法完成.因为其中的文件夹或文件已在另程序打开,请关闭该文件夹或文件,然后重试。那么就要看一看了。为了让大家更加的明白这个提示,我上......
  • 解决微信小程序使用switchTab跳转后页面不刷新的问题
    wx.switchTab({url:‘../index/index’,success:function(e){varpage=getCurrentPages().pop();if(page==undefined||page==null)return;page.onLoad();}})switchTab成功跳转后调用success,此时可以拿到跳转后页面......
  • 快读快写 原理详解
    快读快写原理详解目录快读快写原理详解代码快读readquickly快写writequickly代码解释快读第一部分第二部分第三部分第四部分第五部分快写第一部分第二部分第三部分第四部分第五部分参考文献C++的cincout和C的scanfprintf等IO函数已经够我们是用了,但是它们很慢,......