C语言实战 - 扫雷(图形界面-鼠标操作)

news/2024/11/29 4:02:35/

目录

最终效果预览

预备内容

相关说明

相关教程

用到的知识

EasyX图形库的下载

思路

源代码

最后


 

最终效果预览

在学习如何编写扫雷程序之前,我们先来看一下最终写成代码的演示效果

扫雷视频素材

 

预备内容

相关说明

虽然这是C语言的实战项目,但由于easyx图形库需要在C++环境下才能运行,所以在写代码时创建的是.cpp文件而不是.c文件。而头文件依旧是.h文件

编写扫雷过程中用到的图片资源素材链接如下:

经典版本的扫雷图片素材-C文档类资源-CSDN下载经典版本的扫雷图片素材更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/m0_73759312/87398711

 

相关教程

预备内容中用到的一些知识在下方链接都能相应的找到

string.h中常用的函数(memset函数、sprintf函数都有)

有道云笔记_string.h中的相关函数https://note.youdao.com/s/JAuFJcnZ

 EasyX教程(官方文档)

EasyX 文档 - 基本说明https://docs.easyx.cn/zh-cn/intro

MessageBox函数

MessageBox函数http://t.csdn.cn/2HY12

C语言相关的知识

C语言_小白麋鹿的博客-CSDN博客https://blog.csdn.net/m0_73759312/category_12128703.html

用到的知识

  1. 函数、数组、结构体以及C语言的一些基础内容,其中指针没学也可以。懂得如何操作递归。
  2. memset函数,重置变量时会用到(头文件:srting.h)
  3. sprintf函数(头文件:srting.h)
  4. MessageBox函数(Windows封装的MFC内容)就是游戏退出或者游戏结束时弹出的消息框
  5. EasyX图形库的相关内容
  • 本次扫雷程序用到的EasyX的相关函数
  1. initgraph初始化图形
  2. 图片变量是IMAGE类型
  3. loadimage加载图片,如果程序报错可以尝试把地址用括号括起来,前面加上一个 _T
  4. putimage放置图片
  5. 使用图片之前需要预先加载图片
  6. ExMessage是信息类型
  7. peekmessage获取信息类型
  8. BeginBatchDraw()和EndBatchDraw()函数,以防止闪屏
  9. setlinestyle()、rectangle()等绘图函数(画图形、设置图形格式)
  10. setbkcolor、settextcolor、 settextstyle、drawtext等文字设置相关的函数

EasyX图形库的下载

EasyX图形库的安装过程很简单,和平时安装软件一样(后附链接)

89ae2b9a4f504011afb1db71f69a8a5f.png

EasyX图形库下载地址EasyX Graphics Library 是针对 Visual C++ 的绘图库,支持 VC6.0 ~ VC2019,简单易用,学习成本极低,应用领域广泛。目前已有许多大学将 EasyX 应用在教学当中。https://easyx.cn/

思路

我们先要考虑如何实现扫雷这样一个功能,然后再对其进行优化。

大体思路很简单,先创建一个SquareMessage结构体,这个结构体中放的是扫雷时我们所要点击的方块的信息,其中有放置图片的x轴坐标、y轴坐标,状态(是否为雷),标志(是否插旗或者被点开),代码实现如下:

创建方块的结构体类型

struct SquareMessage //方块信息
{int CoorX;	//X轴坐标int CoorY;	//Y轴坐标int	State;	//状况:0 - 非雷 / 1 - 是雷 int Mark;	//标志:0 - 正常 / 1 - 插旗 / 2 - 问号 / 3 - 被点开
}Square[15][15];					//方块信息以数组形式存储

然后再对这个数组进行初始化,代码实现如下:

初始化函数

void InitMessage()	//信息初始化
{//初始化方块for (int x = 15, i = 1; i <= 13; x += 40, i++){for (int y = 77, j = 1; j <= 13; y += 40, j++){Square[i][j].CoorX = x;Square[i][j].CoorY = y;Square[i][j].State = 0;Square[i][j].Mark = 0;			}}//布置雷int MineNum = MineNumber;int x, y;while (1){if (MineNum == 0)break;x = rand() % 13 + 1;y = rand() % 13 + 1;if (Square[x][y].State == 0){Square[x][y].State = 1;MineNum--;}}
}

然后批量加载我们需要用到的图片资源(因为如果每使用一次就加载一次可能会造成程序卡顿)

代码实现如下:

批量加载图片函数

void LoadPicture() //批量加载图片
{loadimage(&Background, _T("./PictureFiles/background.png"));loadimage(&Normal, _T("./PictureFiles/normal.png"));loadimage(&Safe0, _T("./PictureFiles/safe0.png"));loadimage(&Safe1, _T("./PictureFiles/safe1.png"));loadimage(&Safe2, _T("./PictureFiles/safe2.png"));loadimage(&Safe3, _T("./PictureFiles/safe3.png"));loadimage(&Safe4, _T("./PictureFiles/safe4.png"));loadimage(&Safe5, _T("./PictureFiles/safe5.png"));loadimage(&Safe6, _T("./PictureFiles/safe6.png"));loadimage(&Safe7, _T("./PictureFiles/safe7.png"));loadimage(&Safe8, _T("./PictureFiles/safe8.png"));loadimage(&Mine1, _T("./PictureFiles/mine1.png"));loadimage(&Mine2, _T("./PictureFiles/mine2.png"));loadimage(&Flage1, _T("./PictureFiles/flag1.png"));loadimage(&Flage2, _T("./PictureFiles/flag2.png"));loadimage(&Face1, _T("./PictureFiles/face1.png"));loadimage(&Face2, _T("./PictureFiles/face2.png"));loadimage(&Face3, _T("./PictureFiles/face3.png"));
}

以上的这些准备工作都是在正式游戏开始之前进行,接下来我们开始着手写游戏主体。

首先是绘制扫雷界面,这时我们先放置背景,然后再根据情况循环遍历方块(被点开的按情况处理,没被点开的是否插旗等等情况),这时我们先写一个不是雷的各种情况的函数,然后再写一个检测鼠标点击的函数,还需要一个函数用于检测被点击的方块周围雷的个数

获取周围雷数的函数

int MineAroundNumber(int X, int Y)//周围的雷数
{int mine_sum = 0;for (int i = -1; i < 2; i++){for (int j = -1; j < 2; j++){//跳过自身if (i == 0 && j == 0)continue;mine_sum += Square[X + i][Y + j].State; }}return mine_sum;//非雷为0,雷为1,所以sum的值就是周围雷的数量
}

不是雷的函数

void BaseShow(int X, int Y) //不是雷的各种情况
{if (X > 0 && Y > 0 && X < 14 && Y < 14)	//限制范围switch (MineAroundNumber(X, Y)){case 0: putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe0);Square[X][Y].Mark = 3;break;case 1:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe1);Square[X][Y].Mark = 3;break;case 2:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe2);Square[X][Y].Mark = 3;break;case 3:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe3);Square[X][Y].Mark = 3;break;case 4:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe4);Square[X][Y].Mark = 3;break;case 5:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe5);Square[X][Y].Mark = 3;break;case 6:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe6);Square[X][Y].Mark = 3;break;case 7:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe7);Square[X][Y].Mark = 3;break;case 8:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe8);Square[X][Y].Mark = 3;break;}
}

处理鼠标信息的函数

void ReplyMouse() //处理鼠标信息
{ExMessage msg = { 0 };peekmessage(&msg, EX_MOUSE);//捕获鼠标信息int x = (msg.x - 15) / 40 + 1; //放置方块图片的x轴坐标int y = (msg.y - 77) / 40 + 1; //放置方块图片的y轴坐标if (Square[x][y].Mark != 3 && x > 0 && y > 0 && x < 14 && y < 14) //被点击之后就不能再被点击,限制点击范围{switch (msg.message)//筛选鼠标信息{case WM_LBUTTONDOWN: //左键按下if (Square[x][y].Mark == 0){//防止第一次踩雷,如果不想要这个功能,直接把下面这条if语句注释掉即可if (FirstHit == 0){switch (Square[x][y].State){case 0://不是雷,直接跳出break;case 1://是雷,重置雷的坐标Square[x][y].State = 0;int remine_x, remine_y;while (1){remine_x = rand() % 13 + 1;remine_y = rand() % 13 + 1;if (Square[x][y].State == 0 && remine_x != x && remine_y != y){Square[remine_x][remine_y].State = 1;break;//这个break跳出的是while循环}}break;//这个break跳出的是switch}FirstHit = 1;//取消第一次点击的状态}Square[x][y].Mark = 3;//设置踩雷坐标,踩雷坐标爆红雷if (Square[x][y].State == 1){MineX = x;MineY = y;					}//周围0个雷的情况if (MineAroundNumber(x, y) == 0){MouseLeftHit(x, y);}}break;case WM_RBUTTONDOWN: //右键按下switch (Square[x][y].Mark){case 0://正常 -> 旗子	if (FlagNumber < MineNumber) //保证旗的数量小于雷的数量{Square[x][y].Mark = 1;FlagNumber++;}break;case 1://旗子 -> 问号Square[x][y].Mark = 2;FlagNumber--;break;case 2://问号 -> 正常Square[x][y].Mark = 0;break;}break;default:break;}}
}

这时简单的点击方块功能就实现了,接下来就是实现当周围雷数为0时的展开功能和踩雷的情况。

对此,展开的功能用递归来实现,就是当周围雷数为0时,将被点击的方块点开,然后对周围的8个方块进行递归,当周围雷数不为0时结束,跳出递归。

踩雷的情况就是先遍历将所有雷打印出来,将踩雷的坐标再打印红色的雷(特殊标注踩雷的位置)

具体代码实现如下:

鼠标左击的函数(处理展开情况)

void MouseLeftHit(int X, int Y) //鼠标左击的情况
{if (Square[X][Y].State == 1) //是雷,直接跳出return;if (X > 0 && Y > 0 && X < 14 && Y < 14)	//限制范围switch (MineAroundNumber(X, Y)){case 0: //周围无雷,需要展开putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe0);if (Square[X][Y].Mark == 0)Square[X][Y].Mark = 3;//递归展开for (int i = X - 1; i < X + 2; i++){for (int j = Y - 1; j < Y + 2; j++){if (Square[i][j].Mark == 0)MouseLeftHit(i, j);}}break;default:BaseShow(X, Y);break;}
}

展示所有雷的函数

 (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){if (Square[i][j].State == 1) {putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Mine1);Square[i][j].Mark = 3; }}}
}

然后汇总出一个图片输出函数:

图片输出函数

int GraphyShow() //返回值:1-输/2-没输
{int tmp_return = 0;//绘制背景putimage(0, 0, &Background);//绘制游戏界面for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){		switch(Square[i][j].Mark) {case 0: //正常putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Normal); break;case 1: //插旗putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Flage1); break;case 2: //问号putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Flage2);break;case 3: //鼠标左击if (Square[i][j].State == 0) //没踩到雷BaseShow(i, j);if (Square[i][j].State == 1) //踩到雷了{ShowMine(i, j); putimage(Square[MineX][MineY].CoorX, Square[MineX][MineY].CoorY, &Mine2);tmp_return = 1; //返回值改变FaceKind = 2;	//脸表情改变}break;}}}//绘制提示区域ShowTips();//函数返回 | 返回值:1-输/2-没输return tmp_return;
}

最后再添加一个判断是否赢了的函数

判断赢了的函数

int IsWin()//判断输赢:1 - 赢 / 2 - 没赢
{int RestSquare = 0;for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){if (Square[i][j].Mark != 3)RestSquare++;}}//理论上讲,当所剩的方块数等于雷数时,剩下的就都是雷if (RestSquare == MineNumber)//赢了{//把所有雷插棋子 - 胜利标志for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){if (Square[i][j].State == 1)Square[i][j].Mark = 1;}}FlagNumber = MineNumber;//旗的数量等于雷的数量FaceKind = 3; //脸表情改变return 1;}return 0;
}

至此,扫雷的游戏功能就是实现了,接下来就是对其进行优化了。相关优化的函数内容如下:

绘制表情(赢了戴墨镜,输了大哭脸) 添加到图片输出(GraphyShow)函数中的后面部分

	//绘制脸的表情switch(FaceKind){case 1:putimage(252, 16, &Face1);break;case 2:putimage(252, 16, &Face2);break;case 3:putimage(252, 16, &Face3);break;}	

游戏提示区域(游戏雷数,时间提示) 添加到图片输出(GraphyShow)函数中的后面部分

void ShowTips() //绘制提示区域
{//设置字体规格	setbkcolor(BLACK);						//黑色背景settextcolor(RGB(255, 35, 35));			//红色字体settextstyle(40, 20, "DigifaceWide");	//高40宽20,"液晶数字"字体//绘制雷数提示区域 - 5个"身位"RECT words_area = { 20, 17, 135, 57 };sprintf(MineNumberTip, "%03d", MineNumber - FlagNumber);drawtext(MineNumberTip, &words_area, DT_LEFT | DT_SINGLELINE);//绘制时间提示区域 - 5个"身位"RECT time_area = { 410, 17, 525, 57 };sprintf(GameTimeTip, "%03d", GameTime);drawtext(GameTimeTip, &time_area, DT_RIGHT | DT_SINGLELINE);}

如果想要更规范一些,可以添加一个菜单函数

菜单函数

//菜单页面
int Menu()
{int ButtonStateMark = 0;// 0-没有按钮被按下 | 1-开始按钮被按下 | 2-结束按钮被按下while(1){//处理鼠标信息ExMessage msg = { 0 };				//创建信息变量peekmessage(&msg, EX_MOUSE);		//捕获鼠标信息		if (msg.message == WM_LBUTTONDOWN)	//左键被按下{//开始游戏if (msg.x >= 100 && msg.y >= 140 && msg.x <= 445 && msg.y <= 240)			ButtonStateMark = 1;			//退出游戏	if (msg.x >= 100 && msg.y >= 300 && msg.x <= 445 && msg.y <= 400)ButtonStateMark = 2;}BeginBatchDraw();//菜单背景IMAGE menu;loadimage(&menu, _T("./PictureFiles/menu.png"));putimage(0, 0, &menu);//开始按钮IMAGE StartButton;loadimage(&StartButton, _T("./PictureFiles/StartGameButton.png"));putimage(100, 140, &StartButton);//退出按钮IMAGE ExitButton;loadimage(&ExitButton, _T("./PictureFiles/ExitGameButton.png"));putimage(100, 300, &ExitButton);//点击周围出现线条setlinecolor(BLACK);setlinestyle(PS_DASHDOT);switch (ButtonStateMark){case 1:rectangle(100, 140, 445, 240);break;case 2:rectangle(100, 300, 445, 400);break;}EndBatchDraw();//左击按钮,返回退出if (ButtonStateMark != 0){Sleep(200);return ButtonStateMark;}}
}

 

至此,游戏流程就完成了,完整的程序代码在下面。

源代码

共分为三个文件:game.cpp文件、main.cpp文件、game.h文件

game.cpp文件

#include"game.h"//创建信息变量
#define MineNumber 29			//雷的数量
struct SquareMessage
{ //方块信息int CoorX;	//X轴坐标int CoorY;	//Y轴坐标int	State;	//状况:0 - 非雷 / 1 - 是雷 int Mark;	//标志:0 - 正常 / 1 - 插旗 / 2 - 问号 / 3 - 被点开
}
Square[15][15];					//方块信息以数组形式存储
int FaceKind = 1;				//脸的表情:1-微笑/2-哭脸/3-潇洒
int FlagNumber = 0;				//旗子的数量
int MineX = 0, MineY = 0;		//踩雷的坐标
char MineNumberTip[20] = { 0 };	//雷数的数量提示
int GameTime = 0;				//游戏时间
char GameTimeTip[20] = { 0 };	//游戏时间提示
int FirstHit = 0;				//鼠标是否第一次点击(防止第一次踩雷)0-没点击/1-点击一次//创建图片变量
IMAGE Background; //背景
IMAGE Normal;	  //未被点击
IMAGE Safe0;	  //周围0个雷
IMAGE Safe1;	  //周围1个雷
IMAGE Safe2;	  //周围2个雷
IMAGE Safe3;	  //周围3个雷
IMAGE Safe4;	  //周围4个雷
IMAGE Safe5;	  //周围5个雷
IMAGE Safe6;	  //周围6个雷
IMAGE Safe7;	  //周围7个雷
IMAGE Safe8;	  //周围8个雷
IMAGE Mine1;	  //黑雷
IMAGE Mine2;	  //红雷
IMAGE Flage1;	  //棋子
IMAGE Flage2;	  //问号
IMAGE Face1;	  //微笑
IMAGE Face2;	  //哭脸
IMAGE Face3;	  //潇洒//菜单页面
int Menu()
{int ButtonStateMark = 0;// 0-没有按钮被按下 | 1-开始按钮被按下 | 2-结束按钮被按下while(1){//处理鼠标信息ExMessage msg = { 0 };				//创建信息变量peekmessage(&msg, EX_MOUSE);		//捕获鼠标信息		if (msg.message == WM_LBUTTONDOWN)	//左键被按下{//开始游戏if (msg.x >= 100 && msg.y >= 140 && msg.x <= 445 && msg.y <= 240)			ButtonStateMark = 1;			//退出游戏	if (msg.x >= 100 && msg.y >= 300 && msg.x <= 445 && msg.y <= 400)ButtonStateMark = 2;}BeginBatchDraw();//菜单背景IMAGE menu;loadimage(&menu, _T("./PictureFiles/menu.png"));putimage(0, 0, &menu);//开始按钮IMAGE StartButton;loadimage(&StartButton, _T("./PictureFiles/StartGameButton.png"));putimage(100, 140, &StartButton);//退出按钮IMAGE ExitButton;loadimage(&ExitButton, _T("./PictureFiles/ExitGameButton.png"));putimage(100, 300, &ExitButton);//点击周围出现线条setlinecolor(BLACK);setlinestyle(PS_DASHDOT);switch (ButtonStateMark){case 1:rectangle(100, 140, 445, 240);break;case 2:rectangle(100, 300, 445, 400);break;}EndBatchDraw();//左击按钮,返回退出if (ButtonStateMark != 0){Sleep(200);return ButtonStateMark;}}
}
//游戏内容
void InitMessage()	//信息初始化
{//全局变量重置memset(Square, 0, sizeof(Square));FaceKind = 1;				FlagNumber = 0;				MineX = 0;MineY = 0;memset(MineNumberTip, ' ', 20);GameTime = 0;memset(GameTimeTip, ' ', 20);	FirstHit = 0;//初始化方块for (int x = 15, i = 1; i <= 13; x += 40, i++){for (int y = 77, j = 1; j <= 13; y += 40, j++){Square[i][j].CoorX = x;Square[i][j].CoorY = y;Square[i][j].State = 0;Square[i][j].Mark = 0;			}}//布置雷int MineNum = MineNumber;int x, y;while (1){if (MineNum == 0)break;x = rand() % 13 + 1;y = rand() % 13 + 1;if (Square[x][y].State == 0){Square[x][y].State = 1;MineNum--;}}
}
void LoadPicture() //批量加载图片
{loadimage(&Background, _T("./PictureFiles/background.png"));loadimage(&Normal, _T("./PictureFiles/normal.png"));loadimage(&Safe0, _T("./PictureFiles/safe0.png"));loadimage(&Safe1, _T("./PictureFiles/safe1.png"));loadimage(&Safe2, _T("./PictureFiles/safe2.png"));loadimage(&Safe3, _T("./PictureFiles/safe3.png"));loadimage(&Safe4, _T("./PictureFiles/safe4.png"));loadimage(&Safe5, _T("./PictureFiles/safe5.png"));loadimage(&Safe6, _T("./PictureFiles/safe6.png"));loadimage(&Safe7, _T("./PictureFiles/safe7.png"));loadimage(&Safe8, _T("./PictureFiles/safe8.png"));loadimage(&Mine1, _T("./PictureFiles/mine1.png"));loadimage(&Mine2, _T("./PictureFiles/mine2.png"));loadimage(&Flage1, _T("./PictureFiles/flag1.png"));loadimage(&Flage2, _T("./PictureFiles/flag2.png"));loadimage(&Face1, _T("./PictureFiles/face1.png"));loadimage(&Face2, _T("./PictureFiles/face2.png"));loadimage(&Face3, _T("./PictureFiles/face3.png"));
}
int MineAroundNumber(int X, int Y)//周围的雷数
{int mine_sum = 0;for (int i = -1; i < 2; i++){for (int j = -1; j < 2; j++){//跳过自身if (i == 0 && j == 0)continue;mine_sum += Square[X + i][Y + j].State; }}return mine_sum;//非雷为0,雷为1,所以sum的值就是周围雷的数量
}
void BaseShow(int X, int Y) //不是雷的各种情况
{if (X > 0 && Y > 0 && X < 14 && Y < 14)	//限制范围switch (MineAroundNumber(X, Y)){case 0: putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe0);Square[X][Y].Mark = 3;break;case 1:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe1);Square[X][Y].Mark = 3;break;case 2:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe2);Square[X][Y].Mark = 3;break;case 3:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe3);Square[X][Y].Mark = 3;break;case 4:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe4);Square[X][Y].Mark = 3;break;case 5:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe5);Square[X][Y].Mark = 3;break;case 6:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe6);Square[X][Y].Mark = 3;break;case 7:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe7);Square[X][Y].Mark = 3;break;case 8:putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe8);Square[X][Y].Mark = 3;break;}
}
void ShowMine(int X, int Y) //是雷的情况
{//展示所有雷for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){if (Square[i][j].State == 1) {putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Mine1);Square[i][j].Mark = 3; }}}
}
void ShowTips() //绘制提示区域
{//设置字体规格	setbkcolor(BLACK);						//黑色背景settextcolor(RGB(255, 35, 35));			//红色字体settextstyle(40, 20, "DigifaceWide");	//高40宽20,"液晶数字"字体//绘制雷数提示区域 - 5个"身位"RECT words_area = { 20, 17, 135, 57 };sprintf(MineNumberTip, "%03d", MineNumber - FlagNumber);drawtext(MineNumberTip, &words_area, DT_LEFT | DT_SINGLELINE);//绘制时间提示区域 - 5个"身位"RECT time_area = { 410, 17, 525, 57 };sprintf(GameTimeTip, "%03d", GameTime);drawtext(GameTimeTip, &time_area, DT_RIGHT | DT_SINGLELINE);}
int GraphyShow() //返回值:1-输/2-没输
{int tmp_return = 0;//绘制背景putimage(0, 0, &Background);//绘制游戏界面for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){		switch(Square[i][j].Mark) {case 0: //正常putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Normal); break;case 1: //插旗putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Flage1); break;case 2: //问号putimage(Square[i][j].CoorX, Square[i][j].CoorY, &Flage2);break;case 3: //鼠标左击if (Square[i][j].State == 0) //没踩到雷BaseShow(i, j);if (Square[i][j].State == 1) //踩到雷了{ShowMine(i, j); putimage(Square[MineX][MineY].CoorX, Square[MineX][MineY].CoorY, &Mine2);tmp_return = 1; //返回值改变FaceKind = 2;	//脸表情改变}break;}}}//绘制脸的表情switch(FaceKind){case 1:putimage(252, 16, &Face1);break;case 2:putimage(252, 16, &Face2);break;case 3:putimage(252, 16, &Face3);break;}	//绘制提示区域ShowTips();//函数返回 | 返回值:1-输/2-没输return tmp_return;
}
void MouseLeftHit(int X, int Y) //鼠标左击的情况
{if (Square[X][Y].State == 1) //是雷,直接跳出return;if (X > 0 && Y > 0 && X < 14 && Y < 14)	//限制范围switch (MineAroundNumber(X, Y)){case 0: //周围无雷,需要展开putimage(Square[X][Y].CoorX, Square[X][Y].CoorY, &Safe0);if (Square[X][Y].Mark == 0)Square[X][Y].Mark = 3;//递归展开for (int i = X - 1; i < X + 2; i++){for (int j = Y - 1; j < Y + 2; j++){if (Square[i][j].Mark == 0)MouseLeftHit(i, j);}}break;default:BaseShow(X, Y);break;}
}
void ReplyMouse() //处理鼠标信息
{ExMessage msg = { 0 };peekmessage(&msg, EX_MOUSE);//捕获鼠标信息int x = (msg.x - 15) / 40 + 1; //放置方块图片的x轴坐标int y = (msg.y - 77) / 40 + 1; //放置方块图片的y轴坐标if (Square[x][y].Mark != 3 && x > 0 && y > 0 && x < 14 && y < 14) //被点击之后就不能再被点击,限制点击范围{switch (msg.message)//筛选鼠标信息{case WM_LBUTTONDOWN: //左键按下if (Square[x][y].Mark == 0){//防止第一次踩雷,如果不想要这个功能,直接把下面这条if语句注释掉即可if (FirstHit == 0){switch (Square[x][y].State){case 0://不是雷,直接跳出break;case 1://是雷,重置雷的坐标Square[x][y].State = 0;int remine_x, remine_y;while (1){remine_x = rand() % 13 + 1;remine_y = rand() % 13 + 1;if (Square[x][y].State == 0 && remine_x != x && remine_y != y){Square[remine_x][remine_y].State = 1;break;//这个break跳出的是while循环}}break;//这个break跳出的是switch}FirstHit = 1;//取消第一次点击的状态}Square[x][y].Mark = 3;//设置踩雷坐标,踩雷坐标爆红雷if (Square[x][y].State == 1){MineX = x;MineY = y;					}//周围0个雷的情况if (MineAroundNumber(x, y) == 0){MouseLeftHit(x, y);}}break;case WM_RBUTTONDOWN: //右键按下switch (Square[x][y].Mark){case 0://正常 -> 旗子	if (FlagNumber < MineNumber) //保证旗的数量小于雷的数量{Square[x][y].Mark = 1;FlagNumber++;}break;case 1://旗子 -> 问号Square[x][y].Mark = 2;FlagNumber--;break;case 2://问号 -> 正常Square[x][y].Mark = 0;break;}break;default:break;}}
}
int IsWin()//判断输赢:1 - 赢 / 2 - 没赢
{int RestSquare = 0;for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){if (Square[i][j].Mark != 3)RestSquare++;}}//理论上讲,当所剩的方块数等于雷数时,剩下的就都是雷if (RestSquare == MineNumber)//赢了{//把所有雷插棋子 - 胜利标志for (int i = 1; i < 14; i++){for (int j = 1; j < 14; j++){if (Square[i][j].State == 1)Square[i][j].Mark = 1;}}FlagNumber = MineNumber;//旗的数量等于雷的数量FaceKind = 3; //脸表情改变return 1;}return 0;
}
void Game()
{int begin = clock();	//程序开始时间int jud_lose = 0;		//判断输变量int	jud_win = 0;		//判断赢变量LoadPicture();			//批量加载图片InitMessage();			//初始化信息//进入扫雷环节while(1)				{jud_win = IsWin();				//判断是否赢了BeginBatchDraw();				//开始绘制图形jud_lose = GraphyShow();		//展示画面并判断是否输了EndBatchDraw();					//结束图形绘制ReplyMouse();					//处理鼠标消息int end = clock();				//当前时间时间GameTime = (end - begin) / 1000;//计数器时间if (jud_lose == 1)	break;		//踩雷跳出 - 输了			if (jud_win == 1)	break;		//排雷成功 - 赢了			}
}

main.cpp文件

#include"game.h"int main()
{srand(time(NULL));//预设种子 - 布雷用initgraph(545, 610);int exit_reconfirm = 0;while(1){switch (Menu())//加载菜单{case 1://左击开始键Game();MessageBox(NULL, "游戏结束", " ", MB_OK);break;case 2://左击退出键exit_reconfirm = MessageBox(NULL, "是否确定退出扫雷程序?", " ", MB_YESNO | MB_ICONQUESTION);switch (exit_reconfirm){case IDYES:exit(0);break;}break;}}return 0;
}

game.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
#include<easyx.h>
#include<graphics.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>int Menu();	//声明menu函数
void Game();//声明Game函数

最后

本文如果有哪里有缺陷或者待优化的地方,欢迎大家在评论区指出来。让我们一起共同学习,共同进步。

 

 


http://www.ppmy.cn/news/366394.html

相关文章

关于宠物的HTML网页设计-----梅花鹿(dreamweaver网页设计)

&#x1f329;️ 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f482; 作者主页: 【进入主页—&#x1f680;获取更多源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;HTML5网页期末作业 (1000套…

C#获取bing每日一图的图片及图片故事(IDE为VS Code .net core)

BingImage 获取必应每日一图及故事 获取图片api:http://cn.bing.com/HPImageArchive.aspx?formatjs&idx0&n1 idx参数&#xff1a;指获取图片的时间&#xff0c;0&#xff08;指获取当天图片&#xff09;&#xff0c;1&#xff08;获取昨天照片&#xff09;&#xff0…

python爬取豆瓣图片到本地,并用java上传至服务

换行缩进不要用tab键&#xff01;&#xff01;&#xff01;&#xff01; import requests import urllib import json import os,sys from lxml import etreedef main():f open(test1.txt, w)path os.getcwd()/image;print(path)if not os.path.isdir(path):os.makedirs(pat…

PS教程如何用Photoshop 创造奇幻的燃烧着的鹿

在本教程中我会展示如何使用来创建有火焰效果的奇幻的鹿。 你的工作涉及混合和调整图层&#xff0c;将不同的素材图片合并到一个场景中。 你还将学习如何用火工作&#xff0c;创建一个照明效果并提高景深。 安装Photoshop 2022 1.创建背景 第 1 步 在 Photoshop 中创建一个新…

html5实现点击弹出图片

前台代码&#xff1a; <a href"javascript:;" onclick"repeat()"><div id"modal_volume" style"position: fixed; text-align: center; width: 100%; height: 100%; top: 0; z-index: 9999; display: none;"><table s…

Unity3d使用socket传输图片

C# socket通信只能传输的字节流&#xff0c;所以 我们若是想利用socket传输任何东西&#xff0c;都必须将之以字节的形式进行传输 So 本文就抛砖引玉一下&#xff0c;以传输图片的形式来示范一下&#xff0c;socket传输除文本数据以外的数据 嘻嘻~~~ 既然是网络通信&#xf…

html上传图片后,在页面显示上传的图片

第一种方法&#xff1a; 1.前端代码 <form class"container" enctype"multipart/form-data" method"post" idformBox name"form"> <input type"file" id"chooseImage" name"file"> <…

.NET Base64解码图片及上传

/// <summary>/// 图片上传 Base64解码/// </summary>/// <param name"dataURL">Base64数据</param>/// <param name"path">保存路径</param>/// <param name"imgName">图片名字</param>/// &l…