C语言实现一个美观的扫雷(核心算法篇)

C语言的一大特点就是算法与实现分离,我们将分为算法运算部分与输入输出部分实现

算法部分我们使用标准C语言实现,而输入输出部分我们使用 C for Windows 来实现

本篇为核心算法篇,外观及输入输出都在下篇美化篇实现

目录

如果不想要听我唠叨的话就请直接下载项目学习

需求分析

游戏的主体骨架架构

游戏帧的概念

核心部分的帧处理

扫雷游戏的需求

数据结构分析

矩阵中一个点存储的信息

矩阵

游戏初始化时需要填的入信息

基础接口分析

代码实现

游戏内部信息存储结构

接口函数的实现

接口内调用的函数实现

空间创建类函数

初始化相关函数

矩阵融合相关函数

数据查找相关函数

外部调用样例

游戏调用的框架

将获取的输出数据打印

整体思路解析


如果不想要听我唠叨的话就请直接下载项目学习

项目已开源icon-default.png?t=N7T8https://gitee.com/DeyDeyDream/step-on-mine-public

需求分析

游戏的主体骨架架构

游戏帧的概念

游戏的每一帧包含三个主要部分:输入、处理、输出

  1. 输入包括一切的所需求的设备输入
  2. 处理是对输入数据进行运算处理,也是核心玩法的判断部分
  3. 输出也包含了一切所需的输出,大体为显示输出与音频输出

如果你有其它的需求,也可以在这三个主要框架下进行自行添加,只要确定了每一帧需要进行的事件,我们就可以将这些事件循环起来从而进行游戏,当然了,也请别忘记了退出机制的处理

核心部分的帧处理

严格来说,核心部分并不包含对帧的统一处理,核心部分只需要分别对数据进行拿取,处理与输出就行了,帧的整体处理逻辑就交给外围的程序来统一支配就好啦

扫雷游戏的需求

我们的扫雷也是一个游戏,所以外围程序就套用游戏的行为骨架就行了,我们依次调用输出与输入并将其循环就能够进行游戏了

数据结构分析

扫雷游戏是在一个二维的矩阵中进行的,每个矩阵中都存放了不同的图形分别提示我们这个点的信息或是它周围的信息

矩阵中一个点存储的信息

一个点存储的信息有两类,如果翻开,则存储的信息为周围雷的数量,如果没翻开,则存储我们对这个点的标记信息,使用枚举表示,我们可以用一个 bool 变量来表示翻开状态,然后用一个联合体分别存储两个状态中这个块表示的信息

typedef enum BlockDataInfo {
	m_iNotOpenBlock,//未翻开的块
	m_iFlagBlock	//旗帜块
}BlockDataInfo;

//一个块存储的信息
typedef struct BlockData {
	bool m_bOpenState;					//是否点开块,点开显示数值,没点开显示图标
	union {
		BlockDataInfo m_enumBlockShow;	//显示非数值
		signed int m_nBlockNum;			//显示数值
	} m_unionData;
}BlockData;

typedef BlockData MatrixDataType, * MatrixData, ** lpMatrixData;

矩阵

矩阵我们需要定义一个结构体,其存放内容为矩阵的尺寸信息与一个空间连续的存放方块信息的数组

  • 由于我们可能需要面对不同的需求,所以这个数组的尺寸最好不要咱们自己来定义
  • 数组的长度为尺寸信息中的 宽 * 高
  • 所以我们这个数组就可以以线性存储的形式存储一个二维矩阵的内容
//一个矩阵
typedef struct MatrixStruct {
	size_t m_nMatrixWidth;	//宽度
	size_t m_nMatrixHeight;	//高度

	MatrixData data;		//存储形式为一维数组,存放数据为二维矩阵
}Matrix, *lpMatrix;

游戏初始化时需要填的入信息

初始化信息应定义一个结构体,为了使用简便,我们便将输入与输出的信息与结构都放入这个结构体内,而我们进行初始化调用时,我们只需要填入初始化相关的内容,输入时也只需要填入输入相关的内容,获取输出时也只需要在输出的结构中获取信息即可

//游戏初始化信息结构体
typedef struct MineInitInfoStruct {
	//请不要尝试释放这两个缓冲区内数据的空间并不设为NULL,这将导致无法预估的后果
	Matrix m_sOutBuffer;		//输出缓冲区
	Matrix m_sOutBufferPrev;	//上一次使用的输出缓冲区输出缓冲区    //仅仅与主缓冲区在数据的内容有区别
	//也请不要自行初始化这两个空间,否则你的内存空间内会增加两块无人管理的空间

	struct {
		Point m_xPushPoint;			//输入的点,使用之后会被销毁
		BlockDataInfo m_mMode;		//输入点的输入模式
	}m_mPushData;

	size_t m_nBoardWidth;		//初始化地图的宽度
	size_t m_nBoardHeight;		//初始化地图的高度
	size_t m_nMineNum;			//初始化地图内地雷的数量
	MODE m_iGameMode;				//游戏的模式	
	GAME_LIFE m_nGameLife;			//生命值
	GAME_LIFE m_nGameLifeSubNum;	//生命值减少标准来
	bool m_bIsWin;					//游戏胜利状态
}MineInitInfo, *lpMineInitInfo;

基础接口分析

我们需要实现的接口有三大类,注册与注销相关类型接口、数据输入类型接口、数据获取类型接口

  1. 注册与注销相关接口
    /// <summary>
    /// 注册游戏进程
    /// </summary>
    /// <param name="lpInfo">初始化信息结构体</param>
    /// <returns>错误代码</returns>
    IMPORT TError enableMine(lpMineInitInfo lpInfo);//注册游戏进程,开辟内存,初始化地图,返回值为错误代码,非0值为错误,需处理
    #define EnableMine(lpInfo) ERR_OUT(enableMine(lpInfo))	//自动错误检查宏
    
    /// <summary>
    /// 注销游戏进程
    /// </summary>
    /// <returns>错误代码</returns>
    IMPORT TError disableMine();
    #define DisableMine() ERR_OUT(disableMine())	//自动错误检查宏
    
    /// <summary>
    /// 重置游戏
    /// </summary>
    /// <returns>错误代码</returns>
    IMPORT TError restartGame();
    #define RestartGame() ERR_OUT(restartGame());
  2. 输入相关接口
    /// <summary>
    /// 输入数据并获取是否结束游戏
    /// </summary>
    /// <returns>错误代码</returns>
    IMPORT TError pushPositionMine();
    #define PushPositionMine() ERR_OUT(pushPositionMine())	//自动错误检测宏
    
  3. 输出获取相关接口
    /// <summary>
    /// 获取输出数据
    /// </summary>
    /// <returns>错误代码</returns>
    IMPORT TError getOutMine();	//输出缓冲区
    #define GetOutMine() ERR_OUT(getOutMine())	//自动错误检查宏
    

    或许大家在这之中看到了一些奇奇怪怪的标识符与返回值,不用担心,这是我自己定义的错误处理部分,如果调用出现错误,就会打印错误代码在屏幕上并暂停程序,而那个IMPORT标识符则表示这是一个需要导入的声明,用于对动态链接库内函数的声明与导入

    #define IMPORT _declspec(dllimport)

    另外我们也能够看到,除了初始化的接口之外,其它的接口几乎都没有传入参数,也没有能够获取的返回值,这是因为我们将返回与输入的所有信息都放入了初始化时创建的结构体内,所以我们调用接口时不用担心参数的问题,也不需要担心对游戏核心数据的管理问题了

代码实现

游戏内部信息存储结构

#pragma region 不公开结构体相关

//游戏信息结构体
static struct GameInfoStruct {

	unsigned int m_uRandSeed;			//游戏随机数种子,产出随机数最大值为 32767
	lpMineInitInfo m_lpShellInfo;		//游戏初始化信息地址
	MineInitInfo m_mShellInfoBackups;	//游戏初始化信息副本

	Matrix m_sTrueMine;					//雷的部署位置
	Matrix m_sDisMatrix;				//棋盘上显示的图像ID

	size_t m_nBoundarySize;				//雷区周围预留区域的厚度
	size_t m_cNotOpenBlocksCount;		//已经打开的块的数量
	size_t m_cMineBlocksCount;			//雷块的数量

	GAME_LIFE m_nLife;					//生命值
	GAME_LIFE m_nLifeSubNum;			//生命值增量
	bool m_bIsWin;						//胜利判定

	MODE m_nMode;						//游玩模式
};
typedef struct GameInfoStruct GameInfoStruct, * lpGameInfoStruct;

//定义静态全局变量,这些变量与这个结构体都是不公开的
static lpGameInfoStruct lpGameInfo;

static enum BlockBlendMode {
	m_iUnopen,
	m_iOpen,
	m_iOpenMine,
	m_iPaste,
	m_iCut
};
typedef enum BlockBlendMode BlockBlendMode, *lpBlockBlendMode;

#pragma endregion

接口函数的实现

#pragma region 对外暴露接口

//游戏进程注册
/// <summary>
/// 注册游戏进程
/// </summary>
/// <param name="lpInfo">初始化信息结构体</param>
/// <returns>错误代码</returns>
static TError EnableMineFunc(lpMineInitInfo lpInfo)
{
	//输出重定向,错误处理准备
	fflush(stdout);

	//输入合法性判定
	#pragma region 输入合法性判定
	if (NULL != lpGameInfo)
		return IDERROR_ERR_KERNEL_INITSET_REINIT;
	if (NULL == lpInfo)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (0 == lpInfo->m_nBoardHeight || 0 == lpInfo->m_nBoardWidth)
		return IDERROR_ERR_KERNEL_BOARD_ILLEGAL_SMALLSIZE;
	if (lpInfo->m_nBoardHeight * lpInfo->m_nBoardWidth <= lpInfo->m_nMineNum)
		return IDERROR_ERR_KERNEL_INITSET_ILLEGAL_MINENUMS;
	if (0 == lpInfo->m_nGameLife)
		return IDERROR_ERR_KERNEL_INITSET_HAVENOTLIFE;
	if (0 >= lpInfo->m_nGameLifeSubNum)
		return IDERROR_ERR_KERNEL_INITSET_HAVENOTLIFESUBNUM;
	#pragma endregion

	#pragma region 游玩模式选择初始化
	switch (lpInfo->m_iGameMode)
	{
	case IDMODE_MODE_0:
	{
		//申请空间
		lpGameInfo = BuyOneGameInfoMem();
		lpGameInfo->m_lpShellInfo = lpInfo;

		const size_t MODE0_BoundarySize = 1;

		//初始化
		
		//游戏信息的初始化
		ERR_OUT(SetGameInfo(*lpInfo));
		//初始化游戏模式
		lpGameInfo->m_nMode = IDMODE_MODE_0;
		//游戏输入区域初始化
		lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.x = -1;
		lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.y = -1;
		lpGameInfo->m_lpShellInfo->m_mPushData.m_mMode = -1;
		//初始化雷区的尺寸
		lpGameInfo->m_sTrueMine.m_nMatrixWidth = lpInfo->m_nBoardWidth + MODE0_BoundarySize * 2;
		lpGameInfo->m_sTrueMine.m_nMatrixHeight = lpInfo->m_nBoardHeight + MODE0_BoundarySize * 2;
		lpGameInfo->m_nBoundarySize = MODE0_BoundarySize;
		//初始化显示区域的尺寸
		lpGameInfo->m_sDisMatrix.m_nMatrixWidth = lpGameInfo->m_sDisMatrix.m_nMatrixWidth = lpInfo->m_nBoardWidth;
		lpGameInfo->m_sDisMatrix.m_nMatrixHeight = lpGameInfo->m_sDisMatrix.m_nMatrixHeight = lpInfo->m_nBoardHeight;
		//输出缓冲区初始化
		lpInfo->m_sOutBuffer.m_nMatrixWidth = lpInfo->m_nBoardWidth;
		lpInfo->m_sOutBuffer.m_nMatrixHeight = lpInfo->m_nBoardHeight;
		lpInfo->m_sOutBufferPrev.m_nMatrixWidth = lpInfo->m_nBoardWidth;
		lpInfo->m_sOutBufferPrev.m_nMatrixHeight = lpInfo->m_nBoardHeight;
		//随机数种子生成
		lpGameInfo->m_uRandSeed = (unsigned int)(time(NULL) + GetTickCount64());
		srand(lpGameInfo->m_uRandSeed);
		//内存开辟
		//雷区的内存开辟
		lpGameInfo->m_sTrueMine.data = BuyOneMatrixDataMem(lpGameInfo->m_sTrueMine.m_nMatrixWidth, lpGameInfo->m_sTrueMine.m_nMatrixHeight);;
		//显示区的内存开辟
		lpGameInfo->m_sDisMatrix.data = BuyOneMatrixDataMem(lpGameInfo->m_sDisMatrix.m_nMatrixWidth, lpGameInfo->m_sDisMatrix.m_nMatrixHeight);
		//输出缓冲区域内存开辟
		lpGameInfo->m_lpShellInfo->m_sOutBuffer.data = BuyOneMatrixDataMem(lpInfo->m_nBoardWidth, lpInfo->m_nBoardHeight);
		lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data = BuyOneMatrixDataMem(lpInfo->m_nBoardWidth, lpInfo->m_nBoardHeight);

		ERR_OUT(SetMine(lpInfo->m_nBoardWidth, lpInfo->m_nBoardWidth, lpInfo->m_nMineNum, lpInfo->m_iGameMode));

		break;
	}
	default:
		//错误代码返回
		return IDERROR_ERR_KERNEL_NOTHAVETHEMODE;
	}
	#pragma endregion

	//无异常
	return IDERROR_ERR_KERNEL_NOTERROR;
}
/// <summary>
/// 注册游戏进程
/// </summary>
/// <param name="lpInfo">初始化信息结构体</param>
/// <returns>错误代码</returns>
TError enableMine(lpMineInitInfo lpInfo)
{
	return EnableMineFunc(lpInfo);
}

//获取输出数据
/// <summary>
/// 获取输出数据
/// </summary>
/// <returns>错误代码</returns>
static TError GetOutMineFunc()
{

	//printf("GetOutMine() ");
	//system("pause");

	//输入合法性判定、输出数据输出到缓冲区

	//输入合法性
	#pragma region 数据合法性判定
	//未初始化与游戏信息相关
	if (NULL == lpGameInfo)
		return IDERROR_ERR_KERNEL_INITNOT;
	if (NULL == lpGameInfo->m_lpShellInfo)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_lpShellInfo->m_sOutBuffer.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (0 == lpGameInfo->m_lpShellInfo->m_sOutBuffer.m_nMatrixHeight || 0 == lpGameInfo->m_lpShellInfo->m_sOutBuffer.m_nMatrixWidth)
		return IDERROR_ERR_KERNEL_MATRIX_NULLMATRIX;
	#pragma endregion

	//输出缓冲区与过去输出缓存区的数据移动
	#pragma region 输出缓冲区与过去输出缓存区的数据移动
	//简单的将指针变量的内容调换
	MatrixData tempMatrixData = lpGameInfo->m_lpShellInfo->m_sOutBuffer.data;
	lpGameInfo->m_lpShellInfo->m_sOutBuffer.data = lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data;
	lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data = tempMatrixData;
	#pragma endregion

	//结束时调用该函数执行下列代码 
	if (lpGameInfo->m_bIsWin == true || lpGameInfo->m_nLife <= 0)
	{
		#pragma region 将雷区与显示区的数据块合并输出到输出缓冲区
		//将雷区与显示区的数据块合并输出到输出缓冲区
		Rect disRect = { 0 };
		disRect.right = lpGameInfo->m_sDisMatrix.m_nMatrixWidth;
		disRect.bottom = lpGameInfo->m_sDisMatrix.m_nMatrixHeight;

		Rect mineRect = { 0 };
		mineRect.left = lpGameInfo->m_nBoundarySize;
		mineRect.right = lpGameInfo->m_sTrueMine.m_nMatrixWidth - lpGameInfo->m_nBoundarySize;
		mineRect.top = lpGameInfo->m_nBoundarySize;
		mineRect.bottom = lpGameInfo->m_sTrueMine.m_nMatrixHeight - lpGameInfo->m_nBoundarySize;
		ERR_OUT(MatrixBlend(&(lpGameInfo->m_sDisMatrix), &(lpGameInfo->m_sTrueMine), disRect, mineRect, m_iOpen));
		#pragma endregion
	}

	//输出到缓冲区
	#pragma region 将数据从显示区拷贝到缓冲区
	Rect disRect = { 0 };
	Rect outRect = { 0 };
	//矩阵空间设置
	disRect.right = lpGameInfo->m_sDisMatrix.m_nMatrixWidth;
	outRect.right = lpGameInfo->m_lpShellInfo->m_sOutBuffer.m_nMatrixWidth;
	disRect.bottom = lpGameInfo->m_sDisMatrix.m_nMatrixHeight;
	outRect.bottom = lpGameInfo->m_lpShellInfo->m_sOutBuffer.m_nMatrixHeight;
	//输出
	ERR_OUT(MatrixBlend(&(lpGameInfo->m_lpShellInfo->m_sOutBuffer), &(lpGameInfo->m_sDisMatrix), outRect, disRect, m_iPaste));
	#pragma endregion

	return IDERROR_ERR_KERNEL_NOTERROR;
}
/// <summary>
/// 获取输出数据
/// </summary>
/// <returns>错误代码</returns>
TError getOutMine()
{
	return GetOutMineFunc();
}

//输入并判定
/// <summary>
/// 输入数据并获取是否结束游戏
/// </summary>
/// <returns>错误代码</returns>
static TError PushPositionMineFunc()
{
	//printf("PushPositionMine() ");
	//system("pause");

	#pragma region 输入合法性判定
	//未初始化与游戏信息相关
	if (NULL == lpGameInfo)
		return IDERROR_ERR_KERNEL_INITNOT;
	if (NULL == lpGameInfo->m_lpShellInfo)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_lpShellInfo->m_sOutBuffer.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (0 >= lpGameInfo->m_lpShellInfo->m_nGameLife)
		return IDERROR_ERR_KERNEL_PUSH_HAVENOTLIFE;
	if (0 == lpGameInfo->m_lpShellInfo->m_sOutBuffer.m_nMatrixHeight || 0 == lpGameInfo->m_lpShellInfo->m_sOutBuffer.m_nMatrixWidth)
		return IDERROR_ERR_KERNEL_MATRIX_NULLMATRIX;
	//输入坐标相关
	if (0 >= lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.x || MINE_SIZE_X_MAX <= lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.x ||
		0 >= lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.y || MINE_SIZE_Y_MAX <= lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.y)
		return IDERROR_ERR_KERNEL_PUSH_ILLEGALEPOSITION;
	#pragma endregion
	
	//如果一个块有雷,则这个块一定没有被翻开,其data值为 MINE_ID
	//翻开这个块
	//判断该坐标是否被翻开,如果没有被翻开,则表示该块为雷,改变生命值

	#pragma region 处理输入

	//数据拿取与拿取标记-删除被拿取之后的点的内容
	Point disPosition = lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint;
	Point truePosition = disPosition;
	lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.x = -1;
	lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.y = -1;

	BlockDataInfo pushMode = lpGameInfo->m_lpShellInfo->m_mPushData.m_mMode;
	lpGameInfo->m_lpShellInfo->m_mPushData.m_mMode = -1;

	//下标
	disPosition.x -= 1;
	disPosition.y -= 1;

	switch (pushMode)
	{
	case m_iNotOpenBlock:
	{
		#pragma region 翻开块模式
		ERR_OUT(OpenBlock(disPosition));

		//胜利或失败后再次获取雷区信息即可在输出缓冲区拿到雷的信息
		BlockData temp = { 0 };
		ERR_OUT(FindDisBlock(disPosition, &temp));
		if (false == temp.m_bOpenState)
		{
			//块未被翻开,证明此处为雷
			lpGameInfo->m_nLife -= lpGameInfo->m_nLifeSubNum;
			lpGameInfo->m_lpShellInfo->m_nGameLife -= lpGameInfo->m_lpShellInfo->m_nGameLifeSubNum;
			//将此处翻开
			ERR_OUT(PositionBlend(&(lpGameInfo->m_sDisMatrix), &(lpGameInfo->m_sTrueMine),
				disPosition, truePosition, m_iOpenMine));
		}
		else if (lpGameInfo->m_cNotOpenBlocksCount <= 0)
		{
			lpGameInfo->m_bIsWin = true;
			lpGameInfo->m_lpShellInfo->m_bIsWin = true;
		}
		#pragma endregion
	}
	break;
	case m_iFlagBlock:
	{
		//标记模式
		#pragma region 标记块模式
		//用positionBlend设置
		BlockData tempData = { 0 };
		//查看该块是否已被标记或打开,打开过不进行标记,未打开过如标记过取消标记,否则进行标记
		ERR_OUT(FindDisBlock(disPosition, &tempData));

		//已打开的块不做动作
		if (true == tempData.m_bOpenState)
			break;

		Matrix tempMatrix = { .m_nMatrixHeight = 1, .m_nMatrixWidth = 1, .data = &tempData };
		Point tempDataPos = { 0 };
		tempData.m_bOpenState = false; 
		tempData.m_unionData.m_enumBlockShow = m_iFlagBlock == tempData.m_unionData.m_enumBlockShow ? m_iNotOpenBlock : m_iFlagBlock;

		ERR_OUT(PositionBlend(&(lpGameInfo->m_sDisMatrix), &tempMatrix, disPosition, tempDataPos, m_iPaste));
		#pragma endregion
	}
	break;
	}

	#pragma endregion

	return IDERROR_ERR_KERNEL_NOTERROR;
}
/// <summary>
/// 输入数据并获取是否结束游戏
/// </summary>
/// <returns>错误代码</returns>
TError pushPositionMine()
{
	return PushPositionMineFunc();
}

//游戏进程注销,内存释放
/// <summary>
/// 注销游戏进程
/// </summary>
/// <returns>错误代码</returns>
static TError DisableMineFunc()
{
	//输入合法性判定
	#pragma region 输入合法性判定
	//未初始化与游戏信息相关
	if (NULL == lpGameInfo)
		return IDERROR_ERR_KERNEL_INITNOT;
	if (NULL == lpGameInfo->m_lpShellInfo)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_lpShellInfo->m_sOutBuffer.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_sDisMatrix.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	if (NULL == lpGameInfo->m_sTrueMine.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	#pragma endregion

	#pragma region 内存释放
	//矩阵区块的内存释放
	free(lpGameInfo->m_sTrueMine.data);		//雷区内存释放
	lpGameInfo->m_sTrueMine.data = NULL;
	free(lpGameInfo->m_sDisMatrix.data);	//显示区域内存释放
	lpGameInfo->m_sDisMatrix.data = NULL;

	//输出缓冲区域内存释放
	free(lpGameInfo->m_lpShellInfo->m_sOutBuffer.data);
	lpGameInfo->m_lpShellInfo->m_sOutBuffer.data = NULL;
	free(lpGameInfo->m_lpShellInfo->m_sOutBufferPrev.data);
	lpGameInfo->m_lpShellInfo->m_sOutBuffer.data = NULL;

	//主游戏结构的内存释放
	free(lpGameInfo);
	lpGameInfo = NULL;
	#pragma endregion

	//无异常
	return IDERROR_ERR_KERNEL_NOTERROR;
}
/// <summary>
/// 注销游戏进程
/// </summary>
/// <returns>错误代码</returns>
TError disableMine()
{
	return DisableMineFunc();
}

//重置游戏,使用后请重新获取输出数据以刷新
/// <summary>
/// 重置游戏
/// </summary>
/// <returns>错误代码</returns>
static TError RestartGameFunc()
{
	#pragma region 输入合法性检测
	if (NULL == lpGameInfo)
		return IDERROR_ERR_KERNEL_INITNOT;
	if (NULL == lpGameInfo->m_sDisMatrix.data)
		return IDERROR_ERR_KERNEL_NULLPOINTER;
	#pragma endregion

	#pragma region 重置游戏
	//游戏输入区域初始化
	lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.x = -1;
	lpGameInfo->m_lpShellInfo->m_mPushData.m_xPushPoint.y = -1;
	lpGameInfo->m_lpShellInfo->m_mPushData.m_mMode = -1;

	//内部存储信息重置
	SetGameInfo(lpGameInfo->m_mShellInfoBackups);
	//显示区域刷新
	MatrixDataType tempNullData = { 0 };
	tempNullData.m_bOpenState = false;
	tempNullData.m_unionData.m_enumBlockShow = m_iNotOpenBlock;

	FromateMatrix(lpGameInfo->m_sDisMatrix, tempNullData);
	#pragma endregion

}
/// <summary>
/// 重置游戏
/// </summary>
/// <returns>错误代码</returns>
TError restartGame()
{
	return RestartGameFunc();
}
#pragma endregion

接口内调用的函数实现

空间创建类函数

#pragma region 内存申请相关函数

/// <summary>
/// 获取一个矩阵的内存空间
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <returns>矩阵内存空间的地址</returns>
static MatrixData BuyOneMatrixDataMem(size_t width, size_t height)
{
	assert(width + height);
	void* tempMem = calloc(width * height, sizeof(MatrixDataType));
	assert(tempMem);
	return (MatrixData)tempMem;
}

/// <summary>
/// 获取一个游戏信息结构的内存空间
/// </summary>
/// <returns></returns>
static lpGameInfoStruct BuyOneGameInfoMem()
{
	lpGameInfoStruct lpInfoTemp;
	lpInfoTemp = (struct GameInof*)calloc(1, sizeof(struct GameInfoStruct));
	assert(lpInfoTemp);
	return lpInfoTemp;
}

#pragma endregion

初始化相关函数

#pragma region 初始化相关函数

/// <summary>
/// 格式化矩阵
/// </summary>
/// <param name="matrixDest">目标格式化矩阵</param>
/// <param name="setSrc">格式化参照数据</param>
/// <returns>错误代码</returns>
static TError FromateMatrix(Matrix matrixDest, MatrixDataType setSrc)
{
	//输入合法性测试
	#pragma region 输入合法性测试
	//结构存在性测试
	assert(matrixDest.data);
	//输入矩阵过小
	if (0 >= matrixDest.m_nMatrixHeight || 0 >= matrixDest.m_nMatrixWidth)
		return IDERROR_ERR_KERNEL_MATRIX_MATRIXTOOSMALL;
	#pragma endregion

	//清除矩阵的内容
	#pragma region 格式化
	int i = 0;
	for (i = 0; i < matrixDest.m_nMatrixWidth * matrixDest.m_nMatrixHeight; i++)
	{
		//遍历结构,逐一归零
		matrixDest.data[i] = setSrc;
	}
	#pragma endregion
	
	return IDERROR_ERR_KERNEL_NOTERROR;
}

//发牌算法
/// <summary>
/// 发牌布雷
/// </summary>
/// <param name="width">雷区高度</param>
/// <param name="height">雷区宽度</param>
/// <param name="count">雷的数量</param>
/// <param name="offset">雷区边缘对齐量,左上角</param>
/// <returns>错误代码</returns>
static TError Licensing(size_t width, size_t height, size_t count, Point offset)
{
	size_t size = width * height;

	#pragma region 输入合法性测试
	//结构存在性测试
	assert(lpGameInfo);
	assert(lpGameInfo->m_sTrueMine.data);
	//雷的数量合法性
	if (count >= size)
		return IDERROR_ERR_KERNEL_INITSET_ILLEGAL_MINENUMS;
	//棋盘大小合法性
	if (MINE_SIZE_X_MAX <= width || MINE_SIZE_Y_MAX <= height)
		return IDERROR_ERR_KERNEL_BOARD_ILLEGAL_LARGESIZE;
	#pragma endregion

	#pragma region 临时空间创建
	Matrix tempMatrix = { 0 };
	bool* arrSet = (bool*)calloc(size, sizeof(bool));
	assert(arrSet);
	tempMatrix.data = arrSet;
	tempMatrix.m_nMatrixWidth = width;
	tempMatrix.m_nMatrixHeight = height;
	#pragma endregion

	//进行发牌算法
	//发牌算法:在空间尾部放入需发出的牌,向空余空间发送牌

	#pragma region 拿牌与洗牌
	int i = 0;
	//部牌
	for (i = size - count; i < size; i++)
	{
		arrSet[i] = true;
	}

	int endPoint = size - count;

	//洗牌
	while (endPoint < size)
	{
		int tempPoint = 0;
		//随机生成,重复点不再生成
		do {
			tempPoint = rand() % endPoint;	//随机数获取
		} while (arrSet[tempPoint] != false);
		arrSet[endPoint] = false;
		arrSet[tempPoint] = true;	//空元素填充
		//尾指针移动
		endPoint++;
	}
	#pragma endregion

	//将洗完的牌组发送到游戏数据信息中
	
	//试运行输出洗牌后结果
	//for (i = 0; i < size; i++)
	//{
	//	if (i % lpGameInfo->m_sDisMatrix.m_nMatrixWidth == 0)
	//		printf("\n");
	//	//printf("%d:%d ", i, arrSet[i]);
	//	printf("%.1d ", arrSet[i]);
	//}
	//printf("\n");
	//system("pause");

	#pragma region 发牌,放入临时结构
	int line = 0;
	int pos = 0;
	tempMatrix.data = (MatrixData)BuyOneMatrixDataMem(size, 1);
	MatrixDataType* curData = (MatrixDataType*)tempMatrix.data;
	for (i = 0; i < size; i++)
	{
		if (true == arrSet[i])
		{
			//为真时布雷
			curData->m_bOpenState = true;
			curData->m_unionData.m_nBlockNum = (signed int)MINE_ID;
		}
		else
		{
			//为假时放入未翻开块
			curData->m_bOpenState = false;
			curData->m_unionData.m_enumBlockShow = m_iNotOpenBlock;
		}
		curData++;
	}
	#pragma endregion

	#pragma region 将牌组从临时结构中迁移出来
	//移动区域选定
	Rect rectSrc = { 0 };
	rectSrc.left = 0;
	rectSrc.right = width;
	rectSrc.top = 0;
	rectSrc.bottom = height;
	//移动区域选定
	Rect rectDest = { 0 };
	rectDest.left = offset.x;
	rectDest.right = lpGameInfo->m_sTrueMine.m_nMatrixWidth - offset.x;
	rectDest.top = offset.y;
	rectDest.bottom = lpGameInfo->m_sTrueMine.m_nMatrixHeight - offset.y;

	MatrixBlend(&lpGameInfo->m_sTrueMine, &tempMatrix, rectDest, rectSrc, m_iPaste);
	#pragma endregion

	#pragma region 临时内存空间释放
	free(tempMatrix.data);
	tempMatrix.data = NULL;
	curData = NULL;
	#pragma endregion

return IDERROR_ERR_KERNEL_NOTERROR;
}

//布雷,根据模式
/// <summary>
/// 布雷
/// </summary>
/// <param name="width">雷区宽度</param>
/// <param name="height">雷区高度</param>
/// <param name="nMineNum">雷的数量</param>
/// <param name="mode">布雷模式</param>
/// <returns>错误代码</returns>
static TError SetMine(size_t width, size_t height, size_t nMineNum, MODE mode)
{
	#pragma region 输入合理性测试
	//结构存在性测试
	assert(lpGameInfo);
	assert(lpGameInfo->m_sTrueMine.data);
	//布雷数量合理性检测发
	if (width * height <= nMineNum)
		return IDERROR_ERR_KERNEL_INITSET_ILLEGAL_MINENUMS;
	if (MINE_SIZE_X_MAX <= width || MINE_SIZE_Y_MAX <= height)
		return IDERROR_ERR_KERNEL_BOARD_ILLEGAL_LARGESIZE;
	#pragma endregion

	#pragma region 模式分发
	switch (mode)
	{
	case IDMODE_MODE_0:
	{
		//case 结构中声明结构体需加大括号
		Point offset = { 0 };
		offset.x = lpGameInfo->m_nBoundarySize;
		offset.y = lpGameInfo->m_nBoundarySize;
		ERR_OUT(Licensing(width, height, nMineNum, offset));
		break;
	}
	default:
		return IDERROR_ERR_KERNEL_NOTHAVETHEMODE;
	}

	MatrixDataType tempNullData = { 0 };
	tempNullData.m_bOpenState = false;
	tempNullData.m_unionData.m_enumBlockShow = m_iNotOpenBlock;

	FromateMatrix(lpGameInfo->m_sDisMatrix, tempNullData);
	#pragma endregion

	return IDERROR_ERR_KERNEL_NOTERROR;
}

/// <summary>
/// 设置游戏信息
/// </summary>
/// <param name="info">信息参数结构体</param>
/// <returns>错误代码</returns>
static TError SetGameInfo(MineInitInfo info)
{
	//输入合法性判定
	#pragma region 输入合法性判定
	assert(lpGameInfo);
	if (0 == info.m_nBoardHeight || 0 == info.m_nBoardWidth)
		return IDERROR_ERR_KERNEL_BOARD_ILLEGAL_SMALLSIZE;
	if (info.m_nBoardHeight * info.m_nBoardWidth <= info.m_nMineNum)
		return IDERROR_ERR_KERNEL_INITSET_ILLEGAL_MINENUMS;
	if (0 == info.m_nGameLife)
		return IDERROR_ERR_KERNEL_INITSET_HAVENOTLIFE;
	if (0 >= info.m_nGameLifeSubNum)
		return IDERROR_ERR_KERNEL_INITSET_HAVENOTLIFESUBNUM;
	#pragma endregion

	#pragma region 游戏内数据写入
	lpGameInfo->m_cMineBlocksCount = info.m_nMineNum;
	lpGameInfo->m_cNotOpenBlocksCount = info.m_nBoardWidth * info.m_nBoardHeight - info.m_nMineNum;
	lpGameInfo->m_bIsWin = false;
	lpGameInfo->m_nLife = info.m_nGameLife;
	lpGameInfo->m_nLifeSubNum = info.m_nGameLifeSubNum;
	lpGameInfo->m_mShellInfoBackups = info;
	#pragma endregion

	return IDERROR_ERR_KERNEL_NOTERROR;
}

#pragma endregion

矩阵融合相关函数

#pragma region 矩阵融合相关函数

/// <summary>
/// 参与数据为关闭时粘贴数据
/// </summary>
/// <param name="dest">目标点数据指针</param>
/// <param name="src">参与点数据指针</param>
/// <returns>是否进行了数据转移</returns>
static bool CompareBlendUnopenFunc(MatrixDataType* dest , MatrixDataType* src)
{
	assert(dest);
	assert(src);

	if (src->m_bOpenState == false)
	{
		(*dest) = (*src);
		return true;
	}
	else
	{
		return false;
	}
}

/// <summary>
/// 参与数据为雷时粘贴数据
/// </summary>
/// <param name="dest">目标点数据指针</param>
/// <param name="src">参与点数据指针</param>
/// <returns>是否进行了数据转移</returns>
static bool CompareBlendOpenMineFunc(MatrixDataType* dest, MatrixDataType* src)
{
	assert(dest);
	assert(src);

	if (src->m_bOpenState == false && src->m_unionData.m_nBlockNum == MINE_ID)
	{
		(*dest) = (*src);
		return true;
	}
	else
	{
		return false;
	}
}

/// <summary>
/// 参与数据为打开时粘贴数据
/// </summary>
/// <param name="dest">目标点数据指针</param>
/// <param name="src">参与点数据指针</param>
/// <returns>是否进行了数据转移</returns>
static bool CompareBlendOpenFunc(MatrixDataType* dest, MatrixDataType* src)
{
	assert(dest);
	assert(src);

	if (src->m_bOpenState == true)
	{
		(*dest) = (*src);
		return true;
	}
	else
	{
		return false;
	}
}

/// <summary>
/// 以粘贴拷贝的模式对指定数据进行融合
/// </summary>
/// <param name="dest">目标点数据指针</param>
/// <param name="src">参与点数据指针</param>
/// <returns>是否进行了数据转移</returns>
static bool CompareBlendPasteFunc(MatrixDataType* dest, MatrixDataType* src)
{
	assert(dest);
	assert(src);

	(*dest) = (*src);
	return true;
}

/// <summary>
/// 以剪切的模式对指定数据进行融合
/// </summary>
/// <param name="dest">目标点数据指针</param>
/// <param name="src">参与点数据指针</param>
/// <returns>是否进行了数据转移</returns>
static bool CompareBlendCutFunc(MatrixDataType* dest, MatrixDataType* src)
{
	assert(dest);
	assert(src);

	(*dest) = (*src);

	src->m_bOpenState = false;
	src->m_unionData.m_enumBlockShow = m_iNotOpenBlock;

	return true;

}

/// <summary>
/// 获取融合模式回调函数
/// </summary>
/// <param name="mode">融合模式</param>
/// <returns>错误代码</returns>
static bool(*GetCompareBlendModeFunc(BlockBlendMode mode))(MatrixDataType*, MatrixDataType*)
{
	switch (mode)
	{
	case m_iUnopen:
	#pragma region 融合模式,Unopen模式
		return CompareBlendUnopenFunc;
	#pragma endregion
		break;
	case m_iOpen:
	#pragma region 融合模式,Open模式
		return CompareBlendOpenFunc;
	#pragma endregion
		break;
	case m_iOpenMine:
	#pragma region 融合模式,OpenMine模式
		return CompareBlendOpenFunc;
	#pragma endregion
		break;
	case m_iPaste:
	#pragma region 融合模式,Paste模式
		return CompareBlendPasteFunc;
	#pragma endregion
		break;
	case m_iCut:
	#pragma region 融合模式,Open模式
		return CompareBlendCutFunc;
	#pragma endregion
		break;
	default:
		return NULL;
		break;
	}
}

/// <summary>
/// 对目标矩阵进行融合
/// </summary>
/// <param name="lpDest">目标矩阵</param>
/// <param name="lpSrc">参与矩阵</param>
/// <param name="rectDest">目标矩阵块</param>
/// <param name="rectSrc">参与矩阵块</param>
/// <param name="blendMode">融合模式回调函数</param>
/// <returns>错误代码</returns>
static TError MatrixBlend(lpMatrix lpDest, lpMatrix lpSrc, Rect rectDest, Rect rectSrc, BlockBlendMode blendMode)
{
	#pragma region 输入合法性测试
	//信息结构存在性测试
	assert(lpDest);
	assert(lpSrc);
	assert(lpDest->data);
	assert(lpSrc->data);

	//区块输入合法测试
	if (rectDest.left > rectDest.right || rectDest.top > rectDest.bottom)
		return IDERROR_ERR_KERNEL_MATRIX_RECTFROMATEERROR;
	if (rectSrc.left > rectSrc.right || rectSrc.top > rectSrc.bottom)
		return IDERROR_ERR_KERNEL_MATRIX_RECTFROMATEERROR;
	//区块数据正值测试 negative
	if (0 > rectDest.left || 0 > rectDest.right || 0 > rectDest.top || 0 > rectDest.bottom ||
		0 > rectSrc.left || 0 > rectSrc.right || 0 > rectSrc.top || 0 > rectSrc.bottom)
		return IDERROR_ERR_KERNEL_MATRIX_RECTFROMATEERROR;
	//测试目标区块空间是否足够容纳源内容
	if (rectDest.right - rectDest.left < rectSrc.right - rectSrc.left ||
		rectDest.bottom - rectDest.top < rectSrc.bottom - rectSrc.top)
		return IDERROR_ERR_KERNEL_MATRIX_CANNOTACCOMMODATE;
	//测试输入区块合法性
	if (rectDest.right > lpDest->m_nMatrixWidth ||
		rectDest.bottom > lpDest->m_nMatrixHeight ||
		rectSrc.right > lpSrc->m_nMatrixWidth ||
		rectSrc.bottom > lpSrc->m_nMatrixHeight)
		return IDERROR_ERR_KERNEL_MATRIX_RECTINFOILLEGAL;
	//空矩阵检测
	if (0 >= lpDest->m_nMatrixHeight ||
		0 >= lpDest->m_nMatrixWidth ||
		0 >= lpSrc->m_nMatrixHeight ||
		0 >= lpSrc->m_nMatrixWidth)
		return IDERROR_ERR_KERNEL_MATRIX_MATRIXTOOSMALL;
	#pragma endregion

	#pragma region 融合设置
	bool (*fpCompareBlendFunc)(MatrixDataType*, MatrixDataType*) = GetCompareBlendModeFunc(blendMode);
	if (NULL == fpCompareBlendFunc)
	{
		return IDERROR_ERR_KERNEL_MATRIX_NOTHAVEBLENDMODE;
	}
	#pragma endregion

	//模块转移
	#pragma region 区块融合
	{
		int i = 0;
		//标志Y坐标
		MatrixDataType* tempYS = lpSrc->data;
		MatrixDataType* tempYD = lpDest->data;

		//偏移量YD
		tempYD += lpDest->m_nMatrixWidth * rectDest.top;

		//偏移量YS
		tempYS += lpSrc->m_nMatrixWidth * rectSrc.top;

		for (i = rectSrc.top; i < rectSrc.bottom; i++)
		{
			int j = 0;
			//标志X坐标
			MatrixDataType* tempXD = tempYD;
			MatrixDataType* tempXS = tempYS;
			//偏移量XD
			tempXD += rectDest.left;

			//偏移量XS
			tempXS += rectSrc.left;

			for (j = rectSrc.left; j < rectSrc.right; j++)
			{
				//结构体内容拷贝

				fpCompareBlendFunc(tempXD, tempXS);

				tempXD++;
				tempXS++;
			}

			tempYD += lpDest->m_nMatrixWidth;
			tempYS += lpSrc->m_nMatrixWidth;
		}
	}
	#pragma endregion

	return IDERROR_ERR_KERNEL_NOTERROR;
}

/// <summary>
///  对指定点进行融合
/// </summary>
/// <param name="lpDest">目标矩阵</param>
/// <param name="lpSrc">参与矩阵</param>
/// <param name="positionDest">目标点</param>
/// <param name="positionSrc">参与点</param>
/// <param name="blendMode">融合模式回调函数</param>
/// <returns>错误代码</returns>
static TError PositionBlend(lpMatrix lpDest, lpMatrix lpSrc, Point positionDest, Point positionSrc, BlockBlendMode blendMode)
{
	//输入合法性检测
	#pragma region 输入合法性检测
	//信息结构存在性测试
	assert(lpDest);
	assert(lpSrc);
	assert(lpDest->data);
	assert(lpSrc->data);
	//矩阵合法性相关
	if (0 >= lpDest->m_nMatrixHeight ||
		0 >= lpDest->m_nMatrixWidth ||
		0 >= lpSrc->m_nMatrixHeight ||
		0 >= lpSrc->m_nMatrixWidth)
		return IDERROR_ERR_KERNEL_MATRIX_MATRIXTOOSMALL;
	//坐标相关
	if ((0 > positionDest.x || 0 > positionDest.y || 0 > positionSrc.x || 0 > positionSrc.y) ||
		(lpDest->m_nMatrixWidth < positionDest.x || lpDest->m_nMatrixHeight < positionDest.y ||
		lpSrc->m_nMatrixWidth < positionSrc.x || lpSrc->m_nMatrixHeight < positionSrc.y))
		return IDERROR_ERR_KERNEL_MATRIX_POSITIONILLEGAL;
	#pragma endregion

	#pragma region 融合设置
	bool (*fpCompareBlendFunc)(MatrixDataType*, MatrixDataType*) = GetCompareBlendModeFunc(blendMode);
	if (NULL == fpCompareBlendFunc)
	{
		return IDERROR_ERR_KERNEL_MATRIX_NOTHAVEBLENDMODE;
	}
	#pragma endregion

	//融合
	MatrixData tempDestData = lpDest->data + positionDest.y * lpDest->m_nMatrixWidth + positionDest.x;
	MatrixData tempSrcData = lpSrc->data + positionSrc.y * lpSrc->m_nMatrixWidth + positionSrc.x;

	fpCompareBlendFunc(tempDestData, tempSrcData);

	return IDERROR_ERR_KERNEL_NOTERROR;
}

#pragma endregion

数据查找相关函数

#pragma region 数据查找相关函数

/// <summary>
/// 查询一个显示区坐标的内容信息
/// </summary>
/// <param name="position">查询点</param>
/// <param name="lpResult">坐标信息的回调变量</param>
/// <returns></returns>
static TError FindDisBlock(Point position, BlockData* lpResult)
{
	//输入合法性检测
	#pragma region 输入合法性检测
	//信息结构存在性测试
	assert(lpGameInfo);
	assert(lpGameInfo->m_sDisMatrix.data);
	//输入信息正确性测试
	assert(!(0 > position.x || 0 > position.y));
	assert(!(lpGameInfo->m_sDisMatrix.m_nMatrixWidth <= position.x ||
			 lpGameInfo->m_sDisMatrix.m_nMatrixHeight <= position.y));
	assert(lpResult);
	if (0 >= lpGameInfo->m_sDisMatrix.m_nMatrixWidth || 0 >= lpGameInfo->m_sDisMatrix.m_nMatrixHeight)
		return IDERROR_ERR_KERNEL_MATRIX_MATRIXTOOSMALL;
	#pragma endregion

	MatrixData lpFindData = lpGameInfo->m_sDisMatrix.data + lpGameInfo->m_sDisMatrix.m_nMatrixWidth * position.y;
	lpFindData += position.x;

	*lpResult = *lpFindData;

	return IDERROR_ERR_KERNEL_NOTERROR;
}

/// <summary>
/// 查询一个雷区坐标是否有雷
/// </summary>
/// <param name="position">查询点</param>
/// <param name="lpResultFind">是否有雷的回调变量</param>
/// <returns>错误代码</returns>
static TError FindTrueBlock(Point position, bool* lpResultFind)
{
	//输入合法性检测
	#pragma region 输入合法性检测
	//信息结构存在性测试
	assert(lpGameInfo);
	assert(lpGameInfo->m_sTrueMine.data);
	//输入信息正确性测试
	assert(!(0 > position.x || 0 > position.y));
	assert(!(lpGameInfo->m_sTrueMine.m_nMatrixWidth <= position.x ||
		lpGameInfo->m_sTrueMine.m_nMatrixHeight <= position.y));
	if (0 >= lpGameInfo->m_sDisMatrix.m_nMatrixWidth || 0 >= lpGameInfo->m_sDisMatrix.m_nMatrixHeight)
		return IDERROR_ERR_KERNEL_MATRIX_MATRIXTOOSMALL;
	#pragma endregion

	MatrixData lpFindData = lpGameInfo->m_sTrueMine.data + lpGameInfo->m_sTrueMine.m_nMatrixWidth * position.y;
	lpFindData += position.x;

	if ((signed int)MINE_ID == lpFindData->m_unionData.m_nBlockNum)
		*lpResultFind = true;

	return IDERROR_ERR_KERNEL_NOTERROR;
}

/// <summary>
/// 翻开一个块
/// </summary>
/// <param name="position">需要翻开的点</param>
/// <returns>错误代码</returns>
static TError OpenBlock(Point position)
{
	//输入合法性检测
	#pragma region 输入合法性检测
	//信息结构存在性测试
	assert(lpGameInfo);
	assert(lpGameInfo->m_sDisMatrix.data);
	//坐标相关
	if (0 >= lpGameInfo->m_sDisMatrix.m_nMatrixWidth || 0 >= lpGameInfo->m_sDisMatrix.m_nMatrixHeight)
		return IDERROR_ERR_KERNEL_MATRIX_MATRIXTOOSMALL;
	assert(!(0 > position.x || 0 > position.y ||
		lpGameInfo->m_sDisMatrix.m_nMatrixWidth < position.x ||
		lpGameInfo->m_sDisMatrix.m_nMatrixHeight < position.y));
	#pragma endregion

	//position形式为下标

	//坐标为显示矩形的坐标,在显示矩形坐标位置向周围八格寻找雷,在该位置上标周围八格明雷的数量,并打开该块
	//如果该块为雷则不翻开,如不为雷则显示周围雷的数量或者向周围扩散寻找

	#pragma region 翻开指定块
	//如当前块已翻开,则不做更改,直接返回
	BlockData temp = { 0 };
	FindDisBlock(position, &temp);
	if (true == temp.m_bOpenState)
		return IDERROR_ERR_KERNEL_NOTERROR;

	//翻开当前块

	//检测周围八格的TrueMine块的雷的数量
	size_t mineCount = 0;
	int i = 0;
	for (i = -1; i <= 1; i++)
	{
		int j = 0;
		for (j = -1; j <= 1; j++)
		{
			bool isMine = false;
			Point tempPosition = { .x = position.x + j + lpGameInfo->m_nBoundarySize, .y = position.y + i + lpGameInfo->m_nBoundarySize };
			ERR_OUT(FindTrueBlock(tempPosition, &isMine));

			if (true == isMine)
			{
				//如果中间的位置为雷,则不翻牌并直接返回以表示此处为雷
				if (0 == i && i == j)
					return IDERROR_ERR_KERNEL_NOTERROR;
				else
					mineCount++;
			}
		}
	}

	//翻开当前块,并表明数值
	MatrixData tempData = lpGameInfo->m_sDisMatrix.data + lpGameInfo->m_sDisMatrix.m_nMatrixWidth * position.y;
	tempData += position.x;
	tempData->m_bOpenState = true;
	tempData->m_unionData.m_nBlockNum = mineCount;

	if (0 == mineCount)
	{
		//如果周围无雷,则向四周扩张寻找一次,并翻开不为雷的块
		for (i = -1; i <= 1; i++)
		{
			int j = 0;
			for (j = -1; j <= 1; j++)
			{
				//绕过自身
				if (0 == i && i == j)
					continue;
				Point tempPosition = { .x = position.x + j, .y = position.y + i };
				if (0 <= tempPosition.x &&
					0 <= tempPosition.y &&
					lpGameInfo->m_sDisMatrix.m_nMatrixWidth > tempPosition.x &&
					lpGameInfo->m_sDisMatrix.m_nMatrixHeight > tempPosition.y)
				{
					//坐标存在,递归进行
					ERR_OUT(OpenBlock(tempPosition));
				}
			}
		}
		//system("pause");
	}

	lpGameInfo->m_cNotOpenBlocksCount--;
	#pragma endregion

	return IDERROR_ERR_KERNEL_NOTERROR;
}

#pragma endregion

外部调用样例

游戏调用的框架

void PlayMine()
{
	//开始游戏的初始化
	MineInitInfo gameInfo = { 0 };
	gameInfo.m_iGameMode = IDMODE_MODE_0;
	gameInfo.m_nBoardWidth = 5;
	gameInfo.m_nBoardHeight = 5;
	gameInfo.m_nMineNum = 24;
	gameInfo.m_nGameLife = 2;
	gameInfo.m_nGameLifeSubNum = 1;
	gameInfo.m_bIsWin = false;
	

	//游戏进程注册并初始化
	EnableMine(&gameInfo);
	
	//打印输出
	//判定输入
	//继续循环

	do {
		//输出接口,拿到输出的数据
		GetOutMine();
		//打印输出的数据
		//自定义输出函数
		DisplayBoard(gameInfo.m_sOutBuffer);

		unsigned char flagMode = false;

		//获取输入数据
		printf("请输入 (X,Y) :>");
		scanf("%d%*c%d%*c", &(gameInfo.m_mPushData.m_xPushPoint.x), &(gameInfo.m_mPushData.m_xPushPoint.y));
		printf("请输入是否为标记行为 (T/F) :>");
		scanf("%c", &(flagMode));
		if ('T' == flagMode || 't' == flagMode || 'Y' == flagMode || 'y' == flagMode)
		{
			gameInfo.m_mPushData.m_mMode = m_iFlagBlock;
		}
		else
		{
			gameInfo.m_mPushData.m_mMode = m_iNotOpenBlock;
		}

		PushPositionMine();

		if (true == gameInfo.m_bIsWin)
		{
			break;
		}
	} while (gameInfo.m_nGameLife > 0);

	GetOutMine();
	DisplayBoard(gameInfo.m_sOutBuffer);

	//游戏进程注销
	DisableMine();

	//游戏结束时的是否胜利输出
	if (true == gameInfo.m_bIsWin)
	{
		printf("WIN\n");
	}
	else
	{
		printf("GameOver\n");
	}

	system("pause");
}

将获取的输出数据打印

//显示函数,根据输出数据的值进行输出,输出的数据分类使用switch
void DisplayBoard(const Matrix data)
{
	system("cls");

	MatrixDataType* curBlock = data.data;

	int i = 0;
	printf("\n");
	for (i = 0; i < data.m_nMatrixWidth + 1; i++)
	{
		printf("|---");
	}
	printf("|\n");
	printf("| X |");
	for (i = 0; i < data.m_nMatrixWidth; i++)
	{
		printf("·%1.1d·|", i + 1);
	}
	printf("\n");
	for (i = 0; i < data.m_nMatrixWidth + 1; i++)
	{
		printf("|---");
	}
	printf("|\n");
	for (i = 0; i < data.m_nMatrixHeight; i++)
	{
		printf("|·%1.1d·| ", i + 1);
		int j = 0;
		for (j = 0; j < data.m_nMatrixWidth; j++)
		{
			//根据是否翻开的状态确定打印方式
			if (curBlock->m_bOpenState == true)
			{
				if (curBlock->m_unionData.m_nBlockNum == 0)
				{
					//翻开为0打印 ' '
					printf(" ");
				}
				else
				{
					//翻开为数字打印 数字
					if (curBlock->m_unionData.m_nBlockNum != (signed int)MINE_ID)
						printf("%1.1d", curBlock->m_unionData.m_nBlockNum);
					else
						printf("*");
				}
			}
			else
			{
				//未翻开打印 '·'
				//标记打印 '#'
				//未知信息打印 '?'
				switch (curBlock->m_unionData.m_enumBlockShow)
				{
				case m_iNotOpenBlock:
					printf("·");
					break;
				case m_iFlagBlock:
					printf("#");
					break;
				default:
					printf("?");
					break;
				}
			}
			//printf("%x", curBlock->data.m_nBlockNum);
			printf(" | ");

			curBlock++;
		}
		printf("\n");
		for (j = 0; j < data.m_nMatrixWidth + 1; j++)
		{
			printf("|---");
		}
		printf("|\n");
	}
	//system("pause");
}

整体思路解析

外围的代码已经解释过了,我在注释中也有更加详尽的描述

我们主要解释一下核心逻辑与思路

当我们点击一个块时,我们将输入数据,输入完成后调用push接口将输入数据输送到内核中,然后内核分析输入坐标与输入的模式判断是否翻开一个块,

如果翻开一个块时这个块为雷就将扣除一定生命值,如果这个块周围指定区域没有任何的雷则向周围指定区域扩散翻开,以此递归,如果为标记一个块则在此块上进行标记而不做其它操作并结束操作

输出的数据会放在输出缓冲区,我们拥有两个输出缓冲区,一个存放着当下使用get接口获取的信息,标有prev的缓冲区存放上次调用get接口所获取的数据

想要精准访问一个矩阵中的某个坐标的数据,我们可以使用类似于数组的访问模式,但无法直接用数组的方式访问,那么我们就可以使用 (行号 * 宽度 + 列号)的方式表示矩阵点的具体坐标,请使用者在使用时根据矩阵结构体内数据合法访问使用,不要越界访问

贴下外壳的渲染图

为此我还需要学习许多的东西,后面再肝吧!

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值