当前位置: 首页 > news >正文

图形库实战丨C语言扫雷小游戏(超2w字,附图片素材)

目录

效果展示

游玩链接(无需安装图形库及VS)

开发环境及准备

1.VS2022版本

2.图形库

游戏初始化

1.头文件

2.创建窗口

3.主函数框架

开始界面函数

1.初始化

1-1.设置背景颜色及字体

1-2.处理背景音乐及图片素材 

1-3.处理背景图位置

2.选择模式 

2-1.获取鼠标信息

2-2.处理颜色变化

2-3.判断鼠标按下的键

普通模式

1.随机生成地雷

1-1.清空map数组,并播随机数种子。

1-2.随机布雷

2.确定数字

3.判断输赢

画图 

递归函数 

困难模式

生成日志 

1.获取时间

2.输出内容

完整源代码 

回顾编程过程


源代码三连博主+私信回复“扫雷”领取

效果展示

游玩链接(无需安装图形库及VS)

怎么样?还不错吧,快去链接处下载吧!

EXE链接:下载点我

开发环境及准备

1.VS2022版本

其他版本也可以,别太老就行。

2.图形库

本代码用到图形库,需要安装。

图形库简介:EasyX 是EasyX Graphics Library 是针对 Visual C++ 的免费绘图库,支持 VC6.0 ~VC2022,简单易用,学习成本极低,应用领域广泛。

安装网址:EasyX Graphics Library for C++

游戏初始化

1.头文件

在游戏过程中,需要用到很多头文件,包括但不仅限于:时间函数,图形库,……

#include <iostream>//C++头文件
#include <time.h>//时间函数
#include <stdio.h>//标准输出输入库
#include <graphics.h> //图形库 
#include <mmsystem.h>//windows SDK 播放函数
#include <fstream>  //文件库
#include <windows.h>//windows api
#include <string>

2.创建窗口

因为窗口大小是以像素计数的,我们得先提前设定好格子的长和宽像素个数,我们用50就够了。

而扫雷一般是有10x10一百个格子的所以我们定义N为每行格子数,M为每格像素边长。

#define N 10  //格子数
#define M 50  //一个格子的像素

那么窗口的边长就是:每格像素长×格子数=N×M。

initgraph(N * M, N * M);//初始化绘图窗口宽500高500像素

3.主函数框架

我们先把主函数写好,再去实现相应的功能。

根据扫雷的不同难度,游戏划分为“15雷模式”和“35雷模式”,增加了挑战性趣味性

注意:writeLogMsg是生成日志的函数,当初是方便我调试的,但对游戏体验没有影响。

int main() 
{writeLogMsg("===天天扫雷=开始执行===");writeLogMsg("===开始界面=开始执行===");//initgraph(N * M, N * M,EW_SHOWCONSOLE);//, EW_SHOWCONSOLE初始化绘图窗口宽500高500像素initgraph(N * M, N * M);//初始化绘图窗口宽500高500像素//测试日志//int log[4] = {0,1,2,3};//writeLog(log, 9);writeLogMsg("===开始界面=开始执行===");StartWindow();//调用开始界面函数。writeLogMsg("===开始界面=结束执行===");//1.设置开始页面:背景透明、字体、背景音乐、背景图片,2.加载待使用的图片列表。3.判定鼠标位置和点击事件确定,用户选择的是15雷模式还是35雷模式if (gamemodel == 0) {normalModel();//15雷模式}else {BTmodel();//35雷模式}closegraph();writeLogMsg("===天天扫雷=结束执行===");
}

开始界面函数

1.初始化

1-1.设置背景颜色及字体

因为我们有图片,所有不需要有背景颜色,即把背景颜色设置为透明色。

setbkmode(TRANSPARENT); //设置背景 透明风格

字体推荐楷体,当然你也可以自行调整,比如“宋体”“仿宋”……

settextstyle(40, 18, L"楷体"); //设置开始界面字体大小 L设置字符集

1-2.处理背景音乐及图片素材 

音乐为天空之城,当然你也可以自己修改。

mciSendString(L"open ./天空之城.mp3 alias bgm", 0, 0, 0); //L为设置字符集,./表示当前文件夹
mciSendString(L"play bgm repeat", 0, 0, 0);//重复播放bgm

图片素材名称不要打错了,加载图片的函数用法如下。

//加载图片
/* 
从图片文件获取图像(bmp / gif / jpg / png / tif / emf / wmf / ico)
void loadimage(IMAGE * pDstImg,			// 保存图像的 IMAGE 对象指针LPCTSTR pImgFile,		// 图片文件名int nWidth = 0,			// 图片的拉伸宽度int nHeight = 0,		// 图片的拉伸高度bool bResize = false	// 是否调整 IMAGE 的大小以适应图片
);
*/

 加载:

loadimage(&image[0], L"./image/blank.jpg", M, M);
loadimage(&image[1], L"./image/1.jpg", M, M);
loadimage(&image[2], L"./image/2.jpg", M, M);
loadimage(&image[3], L"./image/3.jpg", M, M);
loadimage(&image[4], L"./image/4.jpg", M, M);
loadimage(&image[5], L"./image/5.jpg", M, M);
loadimage(&image[6], L"./image/6.jpg", M, M);
loadimage(&image[7], L"./image/7.jpg", M, M);
loadimage(&image[8], L"./image/8.jpg", M, M);
loadimage(&image[9], L"./image/lei.jpg", M, M);
loadimage(&image[10], L"./image/tag.jpg", M, M);
loadimage(&image[11], L"./image/start.jpg", N * M, N * M);
loadimage(&image[12], L"./image/0.jpg", M, M);

1-3.处理背景图位置

放一张图片到窗口的函数是putimage,用法及参数如下:

/*
// 绘制图像
void putimage(int dstX,				// 绘制位置的 x 坐标int dstY,				// 绘制位置的 y 坐标IMAGE *pSrcImg,			// 要绘制的 IMAGE 对象指针DWORD dwRop = SRCCOPY	// 三元光栅操作码
);
*/

 把开始界面的背景图放到窗口正中间,也就是(0,0)的位置。

putimage(0, 0, &image[11]);//将image[11]显示在屏幕上,坐标0,0

2.选择模式 

2-1.获取鼠标信息

这个函数我试了好多个,目前我的版本GetMouseMsg( )是可以的。

MOUSEMSG msg = { 0 };//鼠标事件信息
while (1) 
{msg = GetMouseMsg();//获取鼠标位置,存到msg里面
}

2-2.处理颜色变化

当鼠标放到某一个模式上时,我们让那个模式的字体颜色变红,更加真实美观。

//判断鼠标位置是否在15雷模式上,如果在设置颜色为红色
if ((msg.x > 180 && msg.x < 320) &&( msg.y >250 && msg.y < 290))
{settextcolor(RGB(255, 0, 0));		//设置字体颜色红色outtextxy(180, 250, L"15雷模式");//输出 15雷模式 在x180,y250//outtextxyS
}
//判断鼠标位置是否在35雷模式上,如果在设置颜色为红色
else if ((msg.x > 180 && msg.x < 320) &&( msg.y >330 && msg.y < 370))
{settextcolor(RGB(255, 0, 0));		//设置字体颜色红色outtextxy(180, 330, L"35雷模式");//输出 35雷模式 在x180,y330
}
//判断鼠标位置是否在退出上,如果在设置颜色为红色
else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450)
{settextcolor(RGB(255, 0, 0));		//设置字体颜色红色outtextxy(180, 410, L"退出");//输出 退出 在x180,y410
}
//判断鼠标位置是否在背景部分,如果在设置颜色为黑色
else
{settextcolor(RGB(0, 0, 0));		//设置字体颜色为黑色outtextxy(180, 250, L"15雷模式");outtextxy(180, 330, L"35雷模式");outtextxy(180, 410, L"退出");
}

2-3.判断鼠标按下的键

根据不同的坐标,执行不同的模式或者退出程序。

其中判断是否按下左键的函数是WM_LBUTTONDOWN,按下返回真,否则返回假。

switch (msg.uMsg)
{case WM_LBUTTONDOWN://当点击鼠标左键后,判定鼠标坐标位置。//如果以下坐标区域则设置为正常模式0if (msg.x > 180 && msg.x < 320 && msg.y >250 && msg.y < 290) {gamemodel = 0;//将游戏模式设为0(15雷模式)return;}//如果以下坐标区域则设置为风控模式1else if (msg.x > 180 && msg.x < 320 && msg.y >330 && msg.y < 370) {gamemodel = 1;//将游戏模式设为1(35雷模式)return;}//如果以下坐标区域则退出游戏else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450) {exit(0);//退出游戏(终止)}
}

普通模式

1.随机生成地雷

1-1.清空map数组,并播随机数种子。

srand((unsigned)time(NULL));//播种随机数种子
for (int i = 0; i < N + 2; i++) 
{for (int j = 0; j < N + 2; j++){map[i][j] = 0;//把该数组的位置归0}
}	

1-2.随机布雷

while (flag < 15)//控制总雷数不超过15个
{x = rand() % 10 + 1;//随机生成在第几行(1-10行)y = rand() % 10 + 1;//随机生成在第几列(1-10列)//随机方格坐标string msgXY = "may[" + to_string(x) + "][" + to_string(y )+ "]"+"当前数值value is "+to_string(map[x][y]);if (map[x][y] != -1) //随机获取坐标为map[x][y]的值,判定是否不等于-1(-1表示雷),如果等于-1则表示该位置已经是雷。{map[x][y] = -1;//如果不等于-1,则赋值该位置为-1(布雷)flag++;//总雷数增加1个writeLogMsg("may[" + to_string(x) + "][" + to_string(y) + "]" + "当前数值value is " + to_string(map[x][y]) + "埋雷成功!!!累计埋雷个数===>" + to_string(flag));}
}

2.确定数字

遍历整个数组,看八个方向,有一个雷就+1。

for (int i = 1; i <= N; i++) //扫描数组10X10显示部分
{//writeLogMsg("===扫描第【"+to_string(i)+"】行开始执行===");for (int j = 1; j <= N; j++){squ_num++;if (map[i][j] != -1)//如果这个数组的位置不是-1即表示不是地雷{//writeLogMsg("==方格编号【"+ to_string(squ_num) +"】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷!!!开始扫描周围雷总数。当前map【】【】 valuse is==="+to_string(map[i][j]));for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子{for (int n = j - 1; n <= j + 1; n++){if (map[m][n] == -1){map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)									}						}	}//writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷,扫描周围雷总数共计map[i][j]值为==>" + to_string(map[i][j]) + "个");				}else{//writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】是雷,不扫锚周围!map[i][j]值为==>" + to_string(map[i][j]) );}}//writeLogMsg("===扫描第【" + to_string(i) + "】行结束执行===");
}

3.判断输赢

这里用到了一个函数DrawGraph( )是用来画图显示的,下面会讲。

while (1)
{whileCount++;writeLogMsg("===根据map[i][j]存放数值设置对应图片开始执行===");DrawGraph();//更新雷的图片,数字对应的图片writeLogMsg("===根据map[i][j]存放数值设置对应图片结束执行===");type = MouseClick();if (type == -1) //判断用户是否点到雷了{DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败!", MB_ICONINFORMATION |MB_OK) == IDOK) break;//输出提示框}if (win == 0)//判断用户是否赢了,胜利条件为除了雷的所有方块点完即可{DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功!", MB_ICONINFORMATION |MB_OK) == IDOK) break;//输出提示框}writeLogMsg("===type = MouseClick()当前type值===》" + to_string(type));writeLogMsg("===win当前win值===》" + to_string(win));writeLogMsg("===设置图片循环次数===>" + to_string(whileCount));
}

画图 

这个函数是程序的精髓,特别难理解,一定要多推敲推敲!

int count_num=0;//计数器方格数
for (int i = 1; i <= N; i++)
{for (int j = 1; j <= N; j++) {count_num++;//writeLogMsg("DrawGraph==>当前扫描第【"+ to_string(count_num) +"】map【" + to_string(i) + "】行第【" + to_string(j) + "】map[i][j]值为 ==> " + to_string(map[i][j]));switch (map[i][j]){case 9:putimage((i - 1) * M, (j - 1) * M, &image[9]); break;case 10:putimage((i - 1) * M, (j - 1) * M, &image[0]); break;case 11:putimage((i - 1) * M, (j - 1) * M, &image[1]); break;case 12:putimage((i - 1) * M, (j - 1) * M, &image[2]); break;case 13:putimage((i - 1) * M, (j - 1) * M, &image[3]); break;case 14:putimage((i - 1) * M, (j - 1) * M, &image[4]); break;case 15:putimage((i - 1) * M, (j - 1) * M, &image[5]); break;case 16:putimage((i - 1) * M, (j - 1) * M, &image[6]); break;case 17:putimage((i - 1) * M, (j - 1) * M, &image[7]); break;case 18:putimage((i - 1) * M, (j - 1) * M, &image[8]); break;case 29:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 30:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 31:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 32:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 33:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 34:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 35:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 36:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 37:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 38:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;default:putimage((i - 1) * M, (j - 1) * M, &image[12]); break;}}
}

递归函数 

通过不断地递归(调用自己),来实现点一大片的情况。

void loadingPlay(int x, int y) 
{map[x][y] += 10;win--;for (int i = x - 1; i <= x + 1; i++) {for (int j = y - 1; j <= y + 1; j++) {if (i <= 0 || i >= 11 || j <= 0 || j >= 11)  continue; //防止越界if (map[i][j] <= 8) {if (map[i][j] == 0) {loadingPlay(i, j);}else if (map[i][j] != -1) {map[i][j] += 10;win--;}}}}
}

困难模式

思路和普通模式一样,就不解析了,有注释。

{while (1){cleardevice();//清屏win = N * N - 35;//设置胜利条件为把除了雷的所有方块点完即可int type = 0;HWND hWnd = GetHWnd();//这个函数用于获取绘图窗口句柄int x, y, flag = 0;srand((unsigned)time(NULL));//播种随机数种子//通过双重for循环把所有数组归零for (int i = 0; i < N + 2; i++) {for (int j = 0; j < N + 2; j++) {map[i][j] = 0;//把该数组的位置归0}}//布雷循环while (flag < 35)//控制总雷数不超过35个{x = rand() % 10 + 1;//随机生成在第几行y = rand() % 10 + 1;//随机生成在第几列if (map[x][y] != -1) //为了判断所生成的雷有没有重复{map[x][y] = -1;//将此数组设为-1(-1表示雷)flag++;//总雷数增加1个}}//布数字for (int i = 1; i <= N; i++) //扫描数组10X10显示部分{for (int j = 1; j <= N; j++){if (map[i][j] != -1)//如果这个数组的位置不是-1(地雷){for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子{for (int n = j - 1; n <= j + 1; n++){if (map[m][n] == -1){map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)}}}}}}//判断输赢while (1){DrawGraph();type = MouseClick();if (type == -1) //判断用户是否点到雷了{DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败", MB_ICONINFORMATION|MB_OK) == IDOK) break;//输出提示框}if (win == 0)//判断用户是否赢了 {DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功", MB_ICONINFORMATION| MB_OK) == IDOK) break;//输出提示框}}}
}

生成日志 

1.获取时间

用到函数SYSTEMTIME,可以调取年月日时秒分。

2.输出内容

你想看到的后台调试信息都可以放进去。

for (int i = 0; i < count; i++) 
{cout << "log.txt  【Write】===>第" << i <<"行" << endl;myFile << "【" << sys.wYear <<"-"<< sys.wMonth << "-"<< sys.wDay << "    "<< sys.wHour << ":"<< sys.wMinute << ":"<< sys.wSecond << "."<< sys.wMilliseconds << "星期" << sys.wDayOfWeek << "】" << "\t" << squ[0] << "\t" << squ[1] << "\t" << squ[2] << "\t" << squ[3] << endl;
}

完整源代码 

#include<iostream>//C++头文件
#include<time.h>//时间函数
#include<stdio.h>//标准输出输入库
#include<graphics.h>			//图形库   EasyX 是EasyX Graphics Library 是针对 Visual C++ 的免费绘图库,支持 VC6.0 ~VC2022,简单易用,学习成本极低,应用领域广泛
#include<mmsystem.h>//windows SDK 播放函数#include <fstream>  //文件库
#include <windows.h>//windows api
#include<string>//#pragma comment(lib,"winmm.lib")	
#define N 10  //格子数
#define M 50  //一个格子的像素
using namespace std;//命名空间IMAGE image[13];		//存放图片数量为13张
int map[N + 2][N + 2];	//定义整形12行12列二维数组
int gamemodel;//定义游戏模式
int win = N * N - 15;
void StartWindow();		//开始界面
void normalModel();     //15雷模式
void BTmodel();			//35雷模式
void DrawGraph();		//画扫雷地图
int MouseClick();	    //鼠标点击事件
void loadingPlay(int x, int y);	//定义loadingPlay函数,为了运用递归实现点击一大片//void writeLog(int a [],int b);
void writeLogMsg(string msgStr);
int map_num;//map格子编号
int SquareInfo[4];//定义单个方格信息整型数组 用来存在单方格的基本信息。包括方格编号,方格数值,方格x,y坐标值。
int squ_num;//方格编号 从左往右,从第一行到最后一行计数编号。
int squ_value;//方格存放数值
int index_x;//定义格子坐标 X表示横,Y表示纵
int index_y;
int Square[100][4];//存放100个方格基本信息int  main() {writeLogMsg("===天天扫雷=开始执行===");writeLogMsg("===开始界面=开始执行===");//initgraph(N * M, N * M,EW_SHOWCONSOLE);//, EW_SHOWCONSOLE初始化绘图窗口宽500高500像素initgraph(N * M, N * M);//初始化绘图窗口宽500高500像素//测试日志//int log[4] = {0,1,2,3};//writeLog(log, 9);writeLogMsg("===开始界面=开始执行===");StartWindow();//调用开始界面函数。writeLogMsg("===开始界面=结束执行===");//1.设置开始页面:背景透明、字体、背景音乐、背景图片,2.加载待使用的图片列表。3.判定鼠标位置和点击事件确定,用户选择的是15雷模式还是35雷模式if (gamemodel == 0) {normalModel();//15雷模式}else {BTmodel();//35雷模式}closegraph();writeLogMsg("===天天扫雷=结束执行===");
}
void StartWindow() {writeLogMsg("===设置界面背景、字体=开始执行===");setbkmode(TRANSPARENT);		 //设置背景 透明风格settextstyle(40, 18, L"楷体");//设置开始界面字体大小 L设置字符集//初始化页面//播放音乐writeLogMsg("===设置界面背景、字体=结束执行===");writeLogMsg("===设置播放背景音乐=开始执行===");mciSendString(L"open ./天空之城.mp3 alias bgm", 0, 0, 0); //L为设置字符集,./表示当前文件夹mciSendString(L"play bgm repeat", 0, 0, 0);//重复播放bgmwriteLogMsg("===设置播放背景音乐=结束执行===");//加载图片/* 从图片文件获取图像(bmp / gif / jpg / png / tif / emf / wmf / ico)void loadimage(IMAGE * pDstImg,			// 保存图像的 IMAGE 对象指针LPCTSTR pImgFile,		// 图片文件名int nWidth = 0,			// 图片的拉伸宽度int nHeight = 0,		// 图片的拉伸高度bool bResize = false	// 是否调整 IMAGE 的大小以适应图片);*/writeLogMsg("===加载图片资源=开始执行===");loadimage(&image[0], L"./image/blank.jpg", M, M);loadimage(&image[1], L"./image/1.jpg", M, M);loadimage(&image[2], L"./image/2.jpg", M, M);loadimage(&image[3], L"./image/3.jpg", M, M);loadimage(&image[4], L"./image/4.jpg", M, M);loadimage(&image[5], L"./image/5.jpg", M, M);loadimage(&image[6], L"./image/6.jpg", M, M);loadimage(&image[7], L"./image/7.jpg", M, M);loadimage(&image[8], L"./image/8.jpg", M, M);loadimage(&image[9], L"./image/lei.jpg", M, M);loadimage(&image[10], L"./image/tag.jpg", M, M);loadimage(&image[11], L"./image/start.jpg", N * M, N * M);loadimage(&image[12], L"./image/0.jpg", M, M);writeLogMsg("===加载图片资源=开始执行===");/*// 绘制图像
void putimage(int dstX,				// 绘制位置的 x 坐标int dstY,				// 绘制位置的 y 坐标IMAGE *pSrcImg,			// 要绘制的 IMAGE 对象指针DWORD dwRop = SRCCOPY	// 三元光栅操作码
);*/writeLogMsg("===设置开始界面显示图片=开始执行===");putimage(0, 0, &image[11]);//将image[11]显示在屏幕上,坐标0,0writeLogMsg("===设置开始界面显示图片=结束执行===");writeLogMsg("===获取鼠标事件信息、设置15、35雷模式字体,坐标区域=开始执行===");MOUSEMSG msg = { 0 };//鼠标事件信息while (1) {msg = GetMouseMsg();//获取鼠标位置,存到msg里面//判断鼠标位置是否在15雷模式上,如果在设置颜色为红色if ((msg.x > 180 && msg.x < 320) &&( msg.y >250 && msg.y < 290)){settextcolor(RGB(255, 0, 0));		//设置字体颜色红色outtextxy(180, 250, L"15雷模式");//输出 15雷模式 在x180,y250//outtextxyS}//判断鼠标位置是否在35雷模式上,如果在设置颜色为红色else if ((msg.x > 180 && msg.x < 320) &&( msg.y >330 && msg.y < 370)){settextcolor(RGB(255, 0, 0));		//设置字体颜色红色outtextxy(180, 330, L"35雷模式");//输出 35雷模式 在x180,y330}//判断鼠标位置是否在退出上,如果在设置颜色为红色else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450){settextcolor(RGB(255, 0, 0));		//设置字体颜色红色outtextxy(180, 410, L"退出");//输出 退出 在x180,y410}//判断鼠标位置是否在背景部分,如果在设置颜色为黑色else{settextcolor(RGB(0, 0, 0));		//设置字体颜色为黑色outtextxy(180, 250, L"15雷模式");outtextxy(180, 330, L"35雷模式");outtextxy(180, 410, L"退出");}switch (msg.uMsg){case WM_LBUTTONDOWN://当点击鼠标左键后,判定鼠标坐标位置。//如果以下坐标区域则设置为正常模式0if (msg.x > 180 && msg.x < 320 && msg.y >250 && msg.y < 290) {gamemodel = 0;//将游戏模式设为0(15雷模式)return;}//如果以下坐标区域则设置为风控模式1else if (msg.x > 180 && msg.x < 320 && msg.y >330 && msg.y < 370) {gamemodel = 1;//将游戏模式设为1(35雷模式)return;}//如果以下坐标区域则退出游戏else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450) {exit(0);//退出游戏(终止)}}}writeLogMsg("===获取鼠标事件信息、设置15、35雷模式字体,坐标区域=结束执行===");
}
void normalModel()//15雷模式(普通模式)
{writeLogMsg("===15雷模式=开始执行===");while (1){cleardevice();//清屏win = N * N - 15;//设置胜利条件为把除了雷的所有方块点完即可int type = 0;HWND hWnd = GetHWnd();//GetHWnd()这个函数用于获取绘图窗口句柄int x, y, flag = 0;srand((unsigned)time(NULL));//播种随机数种子writeLogMsg("===初始化map[][]每个坐标的值开始执行===");//通过双重for循环把所有数组归零for (int i = 0; i < N + 2; i++) {for (int j = 0; j < N + 2; j++){map[i][j] = 0;//把该数组的位置归0}}writeLogMsg("===初始化map[][]每个坐标的值结束执行===");//随机布雷writeLogMsg("===随机布雷开始执行===");while (flag < 15)//控制总雷数不超过15个{x = rand() % 10 + 1;//随机生成在第几行(1-10行)y = rand() % 10 + 1;//随机生成在第几列(1-10列)//随机方格坐标string msgXY = "may[" + to_string(x) + "][" + to_string(y )+ "]"+"当前数值value is "+to_string(map[x][y]);if (map[x][y] != -1) //随机获取坐标为map[x][y]的值,判定是否不等于-1(-1表示雷),如果等于-1则表示该位置已经是雷。{map[x][y] = -1;//如果不等于-1,则赋值该位置为-1(布雷)flag++;//总雷数增加1个writeLogMsg("may[" + to_string(x) + "][" + to_string(y) + "]" + "当前数值value is " + to_string(map[x][y]) + "埋雷成功!!!累计埋雷个数===>" + to_string(flag));}}writeLogMsg("===随机布雷结束执行===");writeLogMsg("===计算每个方格周围雷数量开始执行===");//计算除了已布雷的格子之外,每个格子周围雷的数量并确定数字for (int i = 1; i <= N; i++) //扫描数组10X10显示部分{//writeLogMsg("===扫描第【"+to_string(i)+"】行开始执行===");for (int j = 1; j <= N; j++){squ_num++;if (map[i][j] != -1)//如果这个数组的位置不是-1即表示不是地雷{//writeLogMsg("==方格编号【"+ to_string(squ_num) +"】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷!!!开始扫描周围雷总数。当前map【】【】 valuse is==="+to_string(map[i][j]));for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子{for (int n = j - 1; n <= j + 1; n++){if (map[m][n] == -1){map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)}}}//writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷,扫描周围雷总数共计map[i][j]值为==>" + to_string(map[i][j]) + "个");}else{//writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】是雷,不扫锚周围!map[i][j]值为==>" + to_string(map[i][j]) );}}//writeLogMsg("===扫描第【" + to_string(i) + "】行结束执行===");}writeLogMsg("===计算每个方格周围雷数量结束执行===");//判断输赢int whileCount = 0;while (1){whileCount++;writeLogMsg("===根据map[i][j]存放数值设置对应图片开始执行===");DrawGraph();//更新雷的图片,数字对应的图片writeLogMsg("===根据map[i][j]存放数值设置对应图片结束执行===");type = MouseClick();if (type == -1) //判断用户是否点到雷了{DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败!", MB_ICONINFORMATION |MB_OK) == IDOK) break;//输出提示框}if (win == 0)//判断用户是否赢了,胜利条件为除了雷的所有方块点完即可{DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功!", MB_ICONINFORMATION |MB_OK) == IDOK) break;//输出提示框}writeLogMsg("===type = MouseClick()当前type值===》" + to_string(type));writeLogMsg("===win当前win值===》" + to_string(win));writeLogMsg("===设置图片循环次数===>" + to_string(whileCount));}}writeLogMsg("===15雷模式=结束执行===");
}
void BTmodel() //35雷模式(疯狂模式)
{while (1){cleardevice();//清屏win = N * N - 35;//设置胜利条件为把除了雷的所有方块点完即可int type = 0;HWND hWnd = GetHWnd();//这个函数用于获取绘图窗口句柄int x, y, flag = 0;srand((unsigned)time(NULL));//播种随机数种子//通过双重for循环把所有数组归零for (int i = 0; i < N + 2; i++) {for (int j = 0; j < N + 2; j++) {map[i][j] = 0;//把该数组的位置归0}}//布雷循环while (flag < 35)//控制总雷数不超过35个{x = rand() % 10 + 1;//随机生成在第几行y = rand() % 10 + 1;//随机生成在第几列if (map[x][y] != -1) //为了判断所生成的雷有没有重复{map[x][y] = -1;//将此数组设为-1(-1表示雷)flag++;//总雷数增加1个}}//布数字for (int i = 1; i <= N; i++) //扫描数组10X10显示部分{for (int j = 1; j <= N; j++){if (map[i][j] != -1)//如果这个数组的位置不是-1(地雷){for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子{for (int n = j - 1; n <= j + 1; n++){if (map[m][n] == -1){map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)}}}}}}//判断输赢while (1){DrawGraph();type = MouseClick();if (type == -1) //判断用户是否点到雷了{DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败", MB_ICONINFORMATION|MB_OK) == IDOK) break;//输出提示框}if (win == 0)//判断用户是否赢了 {DrawGraph();if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功", MB_ICONINFORMATION| MB_OK) == IDOK) break;//输出提示框}}}
}
void DrawGraph()
{int count_num=0;//计数器方格数for (int i = 1; i <= N; i++){for (int j = 1; j <= N; j++) {count_num++;//writeLogMsg("DrawGraph==>当前扫描第【"+ to_string(count_num) +"】map【" + to_string(i) + "】行第【" + to_string(j) + "】map[i][j]值为 ==> " + to_string(map[i][j]));switch (map[i][j]){case 9:putimage((i - 1) * M, (j - 1) * M, &image[9]); break;case 10:putimage((i - 1) * M, (j - 1) * M, &image[0]); break;case 11:putimage((i - 1) * M, (j - 1) * M, &image[1]); break;case 12:putimage((i - 1) * M, (j - 1) * M, &image[2]); break;case 13:putimage((i - 1) * M, (j - 1) * M, &image[3]); break;case 14:putimage((i - 1) * M, (j - 1) * M, &image[4]); break;case 15:putimage((i - 1) * M, (j - 1) * M, &image[5]); break;case 16:putimage((i - 1) * M, (j - 1) * M, &image[6]); break;case 17:putimage((i - 1) * M, (j - 1) * M, &image[7]); break;case 18:putimage((i - 1) * M, (j - 1) * M, &image[8]); break;case 29:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 30:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 31:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 32:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 33:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 34:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 35:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 36:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 37:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;case 38:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;default:putimage((i - 1) * M, (j - 1) * M, &image[12]); break;}}}
}
int MouseClick()
{MOUSEMSG msg = { 0 };int loadingPlayCout = 0;while (1) {msg = GetMouseMsg();writeLogMsg("===MouseClick循环while开始执行===msg.x==》【" +to_string( msg.x)+"】msg.y==》【"+ to_string(msg.y)+"】");switch (msg.uMsg){case WM_LBUTTONDOWN:writeLogMsg("===msg.uMsg.WM_LBUTTONDOWN===正在点击左键");if (map[msg.x / M + 1][msg.y / M + 1] == 0) {writeLogMsg("===map【"+to_string( msg.x / M) +"】【"+ to_string(msg.y / M) +"】的值是===>"+to_string(map[msg.x / M][msg.x / M]));writeLogMsg("当前方格周围没有雷!!!");writeLogMsg("开始加载loadingPlay  没有雷更换为无雷的图片递归循环判断!");loadingPlay(msg.x / M + 1, msg.y / M + 1);loadingPlayCout++;writeLogMsg("当前loadingPlayCout的值得是===>" + to_string(loadingPlayCout));writeLogMsg("结束加载loadingPlay");}else if (map[msg.x / M + 1][msg.y / M + 1] <= 8) {writeLogMsg("当前方格周围有雷!!!");writeLogMsg("map[][]<=8开始执行");writeLogMsg("===map【" + to_string(msg.x / M) + "】【" + to_string(msg.y / M) + "】的值是===>" + to_string(map[msg.x / M][msg.x / M]));map[msg.x / M + 1][msg.y / M + 1] += 10;win--;writeLogMsg("当前win的值是==>"+to_string(win));writeLogMsg("map[][]<=8结束执行");}if (map[msg.x / M + 1][msg.y / M + 1] == 9) {writeLogMsg("map[][]等于9");return -1;}break;case WM_RBUTTONDOWN:writeLogMsg("===msg.uMsg.WM_LBUTTONDOWN===正在点击右键");if (map[msg.x / M + 1][msg.y / M + 1] <= 8) {writeLogMsg("map[][]<=8开始执行");map[msg.x / M + 1][msg.y / M + 1] += 30;}else if (map[msg.x / M + 1][msg.y / M + 1] >= 29) {writeLogMsg("map[][]>=29开始执行");map[msg.x / M + 1][msg.y / M + 1] -= 30;}break;}writeLogMsg("===MouseClick循环while结束执行===" );return 0;}}
void loadingPlay(int x, int y) {map[x][y] += 10;win--;for (int i = x - 1; i <= x + 1; i++) {for (int j = y - 1; j <= y + 1; j++) {if (i <= 0 || i >= 11 || j <= 0 || j >= 11)  continue; //防止越界if (map[i][j] <= 8) {if (map[i][j] == 0) {loadingPlay(i, j);}else if (map[i][j] != -1) {map[i][j] += 10;win--;}}}}}void writeLog(int squ[], int n){//获取当前系统时间SYSTEMTIME sys;GetLocalTime(&sys);fstream myFile;myFile.open("log.txt", ios::out | ios::binary);if (!myFile) {cout << "log.txt can't open!" << endl;abort();}int count = n;myFile << "累计行数===>"<<count << endl;for (int i = 0; i < count; i++) {cout << "log.txt  【Write】===>第" << i <<"行" << endl;myFile << "【" << sys.wYear <<"-"<< sys.wMonth << "-"<< sys.wDay << "    "<< sys.wHour << ":"<< sys.wMinute << ":"<< sys.wSecond << "."<< sys.wMilliseconds << "星期" << sys.wDayOfWeek << "】" << "\t" << squ[0] << "\t" << squ[1] << "\t" << squ[2] << "\t" << squ[3] << endl;}myFile.close();
}void writeLogMsg(string msg) {//获取当前系统时间SYSTEMTIME sys;GetLocalTime(&sys);fstream myFile;myFile.open("log.txt", ios::out | ios::app);if (!myFile) {cout << "log.txt can't open!" << endl;abort();}myFile << "【" << sys.wYear << "-" << sys.wMonth << "-" << sys.wDay << "    " << sys.wHour << ":" << sys.wMinute << ":" << sys.wSecond << "." << sys.wMilliseconds << "   星期" << sys.wDayOfWeek << "】" << "\t" << msg << endl;myFile.close();/*#include<fstream>
#include<iostream>
using namespace std;
int main()
{fstream f;//追加写入,在原来基础上加了ios::app f.open("data.txt",ios::out|ios::app);//输入你想写入的内容 f<<"今天天气不错"<<endl;f.close();return 0;
}*/}

回顾编程过程

今天,我们一起探索了奇妙的扫雷游戏。运用到了很多知识点,有我们的老朋友递归,也有我们的新朋友日志。本篇文章制作不易,断更3周都在写这一篇。

本篇文章共20198字,真得不值得三连吗?

相关文章:

图形库实战丨C语言扫雷小游戏(超2w字,附图片素材)

目录 效果展示 游玩链接&#xff08;无需安装图形库及VS&#xff09; 开发环境及准备 1.VS2022版本 2.图形库 游戏初始化 1.头文件 2.创建窗口 3.主函数框架 开始界面函数 1.初始化 1-1.设置背景颜色及字体 1-2.处理背景音乐及图片素材 1-3.处理背景图位置 2.选…...

c++: string中 find, rfind, find_frist_of, find_laste_of 与 substr之间的操作

在 C 的 std::string 类中&#xff0c;有几个成员函数可以用于在字符串中执行搜索和子字符串提取操作。以下是这些函数的简要说明&#xff1a; find(): 查找子字符串的第一个出现位置。 size_t find(const string& str, size_t pos 0) const; size_t find(const char* s, …...

[python3] dataclass的对象排序

在使用 dataclass(orderTrue) 中&#xff0c;会比较数据类中定义的所有属性。具体来说&#xff0c;生成的比较运算符方法会按照数据类中定义属性的顺序逐个比较属性的取值。 下面是一个示例代码&#xff0c;演示了 orderTrue 比较数据类中所有属性的情况&#xff1a; from da…...

数据库基础——mysql知识体系(掌握mysql,看完这篇文章就够了)

1.关系型数据库 关系型数据库是一种基于关系模型的数据库系统&#xff0c;将数据组织成表格的形式&#xff0c;表格由行和列组成&#xff0c;每行代表一个记录&#xff0c;每列代表一个属性。它使用结构化查询语言SQL进行数据管理和操作。 特点&#xff1a;1.数据的组织&…...

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(二)

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;前导&#xff09; Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;一&#xff09; Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;三&#xff09; 五、实验目的 本次实验使用电脑上的…...

高级语言讲义2010计专(仅高级语言部分)

1.编写一程序&#xff0c;对输入的正整数&#xff0c;求他的约数和。 如&#xff1a;18的约数和为1236939 #include <stdio.h>int getsum(int n){int i,sum0;for(i1;i<n;i)if(n%i0)sumi;return sum; } int main(){int sum getsum(18);printf("%d",sum); …...

你喜欢那种舞者呢?

迷宫中的舞者&#xff1a;程序员职业赛道的探索与魅力 在数字世界的深处&#xff0c;程序员的职业赛道宛如一座神秘而迷人的迷宫。这个迷宫中&#xff0c;每个转角都隐藏着无限的可能&#xff0c;每个领域都散发着独特的魅力。前端开发者如同花园中的精灵&#xff0c;后端工程师…...

LeetCode每日一题之 快乐数

目录 题目介绍&#xff1a; 算法原理&#xff1a; 鸽巢原理&#xff1a; 如何找到环里元素&#xff1a; 代码实现&#xff1a; 题目介绍&#xff1a; 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 算法原理&#xff1a; 我先简单举两个例子&#xff…...

【机器学习】在Python中进行K-Means聚类和层次聚类

Python中聚类算法API的使用指南 聚类分析是数据分析中一种常见的无监督学习方法&#xff0c;通过将相似的对象分组在一起&#xff0c;我们能够识别出数据集中的自然分群。本文将介绍如何使用Python中的聚类算法接口&#xff0c;KMeans和层次聚类方法。 K-Means 聚类 K-Means…...

springboot254小区团购管理

小区团购管理设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装小区团购管理软件来发挥其高效地信…...

Word中的文档网格线与行距问题

在使用Word编辑文档时&#xff0c;经常会发生以下动图展示的这种情况&#xff1a; 上面的动图里&#xff0c;将文字大小放大到某个字号时&#xff0c;单倍行距的间距突然增加很多。造成这种情况的原因是文档中定义了网格线&#xff0c;并且设置了对齐到网格线。如果取消文档中…...

【简写Mybatis】03-Mapper xml的注册和使用

前言 在学习MyBatis源码文章中&#xff0c;斗胆想将其讲明白&#xff1b;故有此文章&#xff0c;如有问题&#xff0c;不吝指教&#xff01; 注意&#xff1a; 学习源码一定一定不要太关注代码的编写&#xff0c;而是注意代码实现思想&#xff1b; 通过设问方式来体现代码中的…...

Vue源码系列讲解——指令篇【一】(自定义指令)

目录 1. 前言 2. 何时生效 3. 指令钩子函数 4. 如何生效 5. 总结 1. 前言 在Vue中&#xff0c;除了Vue本身为我们提供的一些内置指令之外&#xff0c;Vue还支持用户自定义指令。并且用户有两种定义指令的方式&#xff1a;一种是使用全局API——Vue.directive来定义全局指令…...

STM32(14)USART

USART:一种片上外设&#xff0c;用来实现串口通信&#xff0c;就是stm32内部的串口 USART简介 串并转换电路 串行通信和并行通信 串行&#xff1a;一根数据线&#xff0c;逐个比特位发送 为什么要串并转换 移位寄存器 USART的基本模型 通过查询SR&#xff08;状态寄存器&…...

作业 字符数组-统计和加密

字串中数字个数 描述 输入一行字符&#xff0c;统计出其中数字字符的个数。 输入 一行字符串&#xff0c;总长度不超过255。 输出 输出为1行&#xff0c;输出字符串里面数字字符的个数。 样例 #include <iostream> #include<string.h> using namespace std; int m…...

Codeforces Round 719 (Div. 3)除F2题外补题报告

Codeforces Round 719 Div. 3 除F2题外补题报告 得分情况补题情况错题分析C题题目大意初次思路正解思路正解代码错误原因 D题题目大意初次思路正解思路正解代码错误原因 E题题目大意初次思路正解思路正解代码 F1题题目大意正解思路正解代码 G题题目大意正解思路正解代码 得分情…...

docker本地搭建spark yarn hive环境

docker本地搭建spark yarn hive环境 前言软件版本准备工作使用说明构建基础镜像spark on yarn模式构建on-yarn镜像启动on-yarn集群手动方式自动方式 spark on yarn with hive(derby server)模式构建on-yarn-hive镜像启动on-yarn-hive集群手动方式自动方式 常用示例spark执行sh脚…...

每日学习笔记:C++ 11的Tuple

#include <tuple> Tuple介绍(不定数的值组--可理解为pair的升级版) 定义 创建 取值 初始化 获取tuple元素个数、获取tuple某元素类型、将2个tuple类型串接为1个新tuple类型...

MongoDB聚合运算符;$dateToParts

$dateToParts聚合运算符将日期表达式拆分成多个字段放在一个文档返回&#xff0c;属性有year、month、day、hour、minute、second和millisecond。如果iso8601属性设置为true&#xff0c;返回的各部分用ISO周日期返回&#xff0c;属性分别是&#xff1a;isoWeekYear、isoWeek、i…...

Spring MVC RequestMappingHandlerAdapter原理解析

在Spring MVC框架中&#xff0c;RequestMappingHandlerAdapter是一个核心的组件&#xff0c;负责将请求映射到具体的处理器方法上&#xff0c;并调用这些方法来处理请求。其中&#xff0c;invokeHandlerMethod方法是这个适配器中的一个关键方法&#xff0c;它负责实际调用处理器…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...