基于easyx库的GUI扫雷项目
文章目录
0.观前提醒
本程序为本人原创,没有参考任何其他资料或博客,图片素材基于原版扫雷图片
制作带GUI的扫雷游戏项目主要是为了练习自己的C语言基础和学习使用图形化库,我已经将此项目上传了,欢迎大家点击下面链接直接下载。写这篇博客一是为了记录自己曾经练习写代码的过程,二是分享给更多感兴趣的朋友。我将项目捏碎了给大家讲解了出来,相信只要具有一定的耐心,即使基础很差的朋友,也能随便写出来!
源码里的游戏本体有很多步骤可以放入函数进行执行的,这样代码看起来更加简洁。由于我写这个游戏的时候在高铁上,有些不方便,所以我并没有进行功能合并,有兴趣的朋友,自己new个函数将功能合并吧
1.扫雷游戏项目效果展示
2.扫雷游戏项目基本信息
项目名称:扫雷
开发语言:C语言
开发作者:牟建波
开发环境:Visual Studio2019、EasyX图形库、Windows
开发时间:2023-03-12
3.扫雷游戏项目设计思路
4.扫雷游戏实现原理
4.1 头文件解析
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <windows.h> //SetConsoleTextAttribute()函数头文件
#include <graphics.h> //easyx图形库
#include <time.h> //srand()函数头文件
头文件使用解释:
1.使用#include<windows.h>头文件,是为了使用其中的SetConsoleTextAttribute()进行字体颜色修改,方便后期给雷标记颜色区分
2.使用#include<graphics.h>头文件,是为了使用eaxys函数库,进行鼠标交互和图形绘制
3.使用#include<time.h>头文件,是为了使用srand()函数,通过时间播种生成随机数去对应雷
4.2 素材解析
扫雷项目中的素材,全部来自官方原版游戏截图和自己画的,我把素材全放在pic文件夹下了,如下图2中各种表情、方块、数字、地雷
在项目中,我们会使用easyx图形库对图像进行提取使用,所以尽量将素材和游戏放在同一目录下,避免一些奇怪的错误
4.3 变量与矩形布局解析
//三重矩阵布局:我的想法是用三个16*16的矩阵,分别用来表示雷区、地雷的数量、方块的状态,然后将他们进行一张棋盘重合
//这样的布局,可以降低开发难度,调用时也更加不易弄晕矩阵
int Minefield_matrix[16][16] = {
0 };//雷区矩阵
int Mine_Count_matrix[16][16] = {
0 };//地雷计数矩阵
int Block_Status_matrix[16][16] = {
0 };//方块状态矩阵
//公用循环变量:
//这里设定为全局变量,是因为后续会使用很多次,我懒得在里面加int,而且C89标准是不能在for里定义int i的
//所以为了照顾不同编译标准的读者,这里我将其定义为全局变量
int i = 0;
int j = 0;
//中转地雷数组:用于临时中转存储地雷
int Transfer_Mine_matrix[40] = {
0 };
//方块状态flag
int IsMine_flag = 0;//0表示非雷,1表示雷
//游戏胜利flag
int Success_flag = 0;//0表示失败,1表示成功
4.4 随机雷的实现
//生成随机雷
//思路:
//1.雷随机生成 16*16=256 从左到右 1~256
//2.随机生成40个[1,256]范围的随机数,通过对矩阵的位置,将对应的编号设置为雷
//3.为防止生成的随机数有重复的,每生成一个随机数,就将这个数存入Transfer_Mine_matrix[40]这个中转地雷数组中
//4.之后生成的随机数需与数组中的元素进行比较,若重复则不会保存在数组中
srand((unsigned int)time(0));//通过时间播种生成随机数用于表示雷
for (int Mine_Number = 0; Mine_Number < 40; Mine_Number++)
{
int Correct_Number_mine = 0;//合格的雷的随机数字
Correct_Number_mine = rand() % 256 + 1; //生成1~256范围内随机数
if (Correct_Number_mine < 0 || Correct_Number_mine > 256)//随机数不符合要求
{
Mine_Number--;
}
else//随机数符合要求
{
for (i = 0; i < 40; i++)
{
if (Transfer_Mine_matrix[i] == Correct_Number_mine)//如果合格的雷在中转地雷数组中存在
{
Mine_Number--;//不符合的雷去掉,雷数-1
break;
}
if (Transfer_Mine_matrix[i] == 0)//如果合格的雷没有出现过,则存入中转地雷数组中 中转地雷数组初始化是0
{
Transfer_Mine_matrix[i] = Correct_Number_mine;
break;
}
}
}
}
//测试雷的位置
for (int i = 0; i < 40; i++)
{
printf("%d ", Transfer_Mine_matrix[i]);
}
随机雷生成测试结果图:
4.5 生成雷位置矩阵的实现
//生成雷的矩阵
//思路:通过Transfer_Mine_matrix中转地雷数组中的随机数,通过对应关系写入雷区矩阵Minefield_matrix[16][16]中,雷的位置表示为1,非雷的位置表示为0
for (int length = 0; length < 40; length++)
{
int count = 0;//计数
for (i = 0; i < 16; i++)
{
for (j = 0; j < 16; j++)
{
count++;
if (Transfer_Mine_matrix[length] == count)
{
Minefield_matrix[i][j] = 1;
IsMine_flag = 1;//1表示雷,0表示非雷
break;
}
}
if (IsMine_flag == 1)
{
break;
}
}
IsMine_flag = 0;//初始化,以免影响下次循环
count = 0;//初始化,以免影响下次循环
}
//雷矩阵调试
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
if (Minefield_matrix[i][j] == 1)
{
colour(12);//是雷标记为红色
}
else
{
colour(15);//不是雷标记为白色
}
printf("%d ", Minefield_matrix[i][j]);//打印雷矩阵
}
colour(15);//让后续字体染色显示不为红色
printf("\n");
}
system("pause");
生成雷位置矩阵调试结果图:
4.6 生成雷数矩阵的实现
雷数矩阵处理的想法:
- 上图是我们对于一个矩阵四周遍历的常规过程,可以看出,我们进行遍历都是一边一边的进行遍历,由外向内进行遍历。当我们处理边界的是否就非常头疼了,这时我们可以使用循环不变量法
- 循环不变量法:比如上图,就是我们在考虑边界问题的时候是非常头疼的,如果我们每边都考虑全部遍历完,就会出现很多边界考虑问题,但是如果我们把遍历方式都设置成一种,那么就让边界问题变成了一种情况,也就是上图的只遍历开始到最后一位的前一位,让下一次循环遍历剩下的元素,从而叫做循环不变量法
- 但是我们在处理雷数矩阵的时候,要处理的时一个元素四周,所以直接使用循环不变量法不合适的。我们可以使用它的思想,进行雷数的计算
- 这里我的想法是,先处理矩阵的四个角,然后处理去角的四边,那么我们就剩下了一个15*15的正方形了,处理正方形我们就有了一个通用的方法进行计算,如下图
//通过对matrix矩阵每个元素的周围八个元素计算雷数,并将雷数存入number[16][16]矩阵中
//number矩阵中1~8表示雷数,9表示该元素为雷
//通过对Minefield_matrix雷区矩阵每个元素的周围八个元素进行计算雷数,并将雷存入Mine_Count_matrix地雷计数矩阵中
//Mine_Count_martix地雷计数矩阵中,1~8表示周围雷数量,9表示该元素为雷
for (i = 0; i < 16; i++)
{
int count = 0;
for (j = 0; j < 16; j++)
{
count = 0;
if (Minefield_matrix[i][j] == 1)//如果是雷设为9
{
Mine_Count_matrix[i][j] = 9;
continue;
}
else
{
//思路:
//1.先处理16*16矩阵的,四个边角,坐标为(0,0)、(0,15)、(15,0)、(15,15)
//2.再处理矩形的四边,在处理过程把矩阵看成一个17*17的矩阵,这样是为了方便计数设计,数组溢出并不会有什么问题,因为我们没有使用它
//3.最后我们会剩下一个规整15*15的正方形,然后处理每个方块的八个方向就可以了
if (i == 0 && j == 0)//左上角
{
if (Minefield_matrix[0][1] == 1)//左上角方块右侧
count++;
if (Minefield_matrix[1][0] == 1)//左上角方块下侧
count++;
if (Minefield_matrix[1][1] == 1)//左上角方块斜右下侧
count++;
}
else if (i == 0 && j == 15)//右上角
{
if (Minefield_matrix[0][14] == 1)//右上角方块左侧
count++;
if (Minefield_matrix[1][15] == 1)//右上角方块下侧
count++;
if (Minefield_matrix[1][14] == 1)//右上角方块斜左下侧
count++;
}
else if (i == 15 && j == 0)//左下角
{
if (Minefield_matrix[15][1] == 1)//左下角方块右侧
count++;
if (Minefield_matrix[14][0] == 1)//左下角方块侧
count++;
if (Minefield_matrix[14][1] == 1)//左下角方块斜右上侧
count++;
}
else if (i == 15 && j == 15)//右下角
{
if (Minefield_matrix[15][14] == 1)//右下角方块左侧
count++;
if (Minefield_matrix[14][15] == 1)//右下角