• 图形库实战丨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.头文件

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

    1. #include //C++头文件
    2. #include //时间函数
    3. #include //标准输出输入库
    4. #include //图形库
    5. #include //windows SDK 播放函数
    6. #include //文件库
    7. #include //windows api
    8. #include

    2.创建窗口

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

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

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

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

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

    3.主函数框架

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

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

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

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

    开始界面函数

    1.初始化

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

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

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

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

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

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

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

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

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

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

     加载:

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

    1-3.处理背景图位置

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

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

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

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

    2.选择模式 

    2-1.获取鼠标信息

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

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

    2-2.处理颜色变化

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

    1. //判断鼠标位置是否在15雷模式上,如果在设置颜色为红色
    2. if ((msg.x > 180 && msg.x < 320) &&( msg.y >250 && msg.y < 290))
    3. {
    4. settextcolor(RGB(255, 0, 0)); //设置字体颜色红色
    5. outtextxy(180, 250, L"15雷模式");//输出 15雷模式 在x180,y250//outtextxyS
    6. }
    7. //判断鼠标位置是否在35雷模式上,如果在设置颜色为红色
    8. else if ((msg.x > 180 && msg.x < 320) &&( msg.y >330 && msg.y < 370))
    9. {
    10. settextcolor(RGB(255, 0, 0)); //设置字体颜色红色
    11. outtextxy(180, 330, L"35雷模式");//输出 35雷模式 在x180,y330
    12. }
    13. //判断鼠标位置是否在退出上,如果在设置颜色为红色
    14. else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450)
    15. {
    16. settextcolor(RGB(255, 0, 0)); //设置字体颜色红色
    17. outtextxy(180, 410, L"退出");//输出 退出 在x180,y410
    18. }
    19. //判断鼠标位置是否在背景部分,如果在设置颜色为黑色
    20. else
    21. {
    22. settextcolor(RGB(0, 0, 0)); //设置字体颜色为黑色
    23. outtextxy(180, 250, L"15雷模式");
    24. outtextxy(180, 330, L"35雷模式");
    25. outtextxy(180, 410, L"退出");
    26. }

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

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

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

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

    普通模式

    1.随机生成地雷

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

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

    1-2.随机布雷

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

    2.确定数字

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

    1. for (int i = 1; i <= N; i++) //扫描数组10X10显示部分
    2. {
    3. //writeLogMsg("===扫描第【"+to_string(i)+"】行开始执行===");
    4. for (int j = 1; j <= N; j++)
    5. {
    6. squ_num++;
    7. if (map[i][j] != -1)//如果这个数组的位置不是-1即表示不是地雷
    8. {
    9. //writeLogMsg("==方格编号【"+ to_string(squ_num) +"】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷!!!开始扫描周围雷总数。当前map【】【】 valuse is==="+to_string(map[i][j]));
    10. for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子
    11. {
    12. for (int n = j - 1; n <= j + 1; n++)
    13. {
    14. if (map[m][n] == -1)
    15. {
    16. map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)
    17. }
    18. }
    19. }
    20. //writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷,扫描周围雷总数共计map[i][j]值为==>" + to_string(map[i][j]) + "个");
    21. }
    22. else
    23. {
    24. //writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】是雷,不扫锚周围!map[i][j]值为==>" + to_string(map[i][j]) );
    25. }
    26. }
    27. //writeLogMsg("===扫描第【" + to_string(i) + "】行结束执行===");
    28. }

    3.判断输赢

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

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

    画图 

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

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

    递归函数 

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

    1. void loadingPlay(int x, int y)
    2. {
    3. map[x][y] += 10;
    4. win--;
    5. for (int i = x - 1; i <= x + 1; i++) {
    6. for (int j = y - 1; j <= y + 1; j++) {
    7. if (i <= 0 || i >= 11 || j <= 0 || j >= 11) continue; //防止越界
    8. if (map[i][j] <= 8) {
    9. if (map[i][j] == 0) {
    10. loadingPlay(i, j);
    11. }
    12. else if (map[i][j] != -1) {
    13. map[i][j] += 10;
    14. win--;
    15. }
    16. }
    17. }
    18. }
    19. }

    困难模式

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

    1. {
    2. while (1)
    3. {
    4. cleardevice();//清屏
    5. win = N * N - 35;//设置胜利条件为把除了雷的所有方块点完即可
    6. int type = 0;
    7. HWND hWnd = GetHWnd();//这个函数用于获取绘图窗口句柄
    8. int x, y, flag = 0;
    9. srand((unsigned)time(NULL));//播种随机数种子
    10. //通过双重for循环把所有数组归零
    11. for (int i = 0; i < N + 2; i++)
    12. {
    13. for (int j = 0; j < N + 2; j++)
    14. {
    15. map[i][j] = 0;//把该数组的位置归0
    16. }
    17. }
    18. //布雷循环
    19. while (flag < 35)//控制总雷数不超过35个
    20. {
    21. x = rand() % 10 + 1;//随机生成在第几行
    22. y = rand() % 10 + 1;//随机生成在第几列
    23. if (map[x][y] != -1) //为了判断所生成的雷有没有重复
    24. {
    25. map[x][y] = -1;//将此数组设为-1(-1表示雷)
    26. flag++;//总雷数增加1个
    27. }
    28. }
    29. //布数字
    30. for (int i = 1; i <= N; i++) //扫描数组10X10显示部分
    31. {
    32. for (int j = 1; j <= N; j++)
    33. {
    34. if (map[i][j] != -1)//如果这个数组的位置不是-1(地雷)
    35. {
    36. for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子
    37. {
    38. for (int n = j - 1; n <= j + 1; n++)
    39. {
    40. if (map[m][n] == -1)
    41. {
    42. map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)
    43. }
    44. }
    45. }
    46. }
    47. }
    48. }
    49. //判断输赢
    50. while (1)
    51. {
    52. DrawGraph();
    53. type = MouseClick();
    54. if (type == -1) //判断用户是否点到雷了
    55. {
    56. DrawGraph();
    57. if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败", MB_ICONINFORMATION|MB_OK) == IDOK) break;//输出提示框
    58. }
    59. if (win == 0)//判断用户是否赢了
    60. {
    61. DrawGraph();
    62. if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功", MB_ICONINFORMATION| MB_OK) == IDOK) break;//输出提示框
    63. }
    64. }
    65. }
    66. }

    生成日志 

    1.获取时间

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

    2.输出内容

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

    1. for (int i = 0; i < count; i++)
    2. {
    3. cout << "log.txt 【Write】===>第" << i <<"行" << endl;
    4. 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;
    5. }

    完整源代码 

    1. #include//C++头文件
    2. #include//时间函数
    3. #include//标准输出输入库
    4. #include //图形库 EasyX 是EasyX Graphics Library 是针对 Visual C++ 的免费绘图库,支持 VC6.0 ~VC2022,简单易用,学习成本极低,应用领域广泛
    5. #include//windows SDK 播放函数
    6. #include //文件库
    7. #include //windows api
    8. #include//
    9. #pragma comment(lib,"winmm.lib")
    10. #define N 10 //格子数
    11. #define M 50 //一个格子的像素
    12. using namespace std;//命名空间
    13. IMAGE image[13]; //存放图片数量为13张
    14. int map[N + 2][N + 2]; //定义整形12行12列二维数组
    15. int gamemodel;//定义游戏模式
    16. int win = N * N - 15;
    17. void StartWindow(); //开始界面
    18. void normalModel(); //15雷模式
    19. void BTmodel(); //35雷模式
    20. void DrawGraph(); //画扫雷地图
    21. int MouseClick(); //鼠标点击事件
    22. void loadingPlay(int x, int y); //定义loadingPlay函数,为了运用递归实现点击一大片
    23. //void writeLog(int a [],int b);
    24. void writeLogMsg(string msgStr);
    25. int map_num;//map格子编号
    26. int SquareInfo[4];//定义单个方格信息整型数组 用来存在单方格的基本信息。包括方格编号,方格数值,方格x,y坐标值。
    27. int squ_num;//方格编号 从左往右,从第一行到最后一行计数编号。
    28. int squ_value;//方格存放数值
    29. int index_x;//定义格子坐标 X表示横,Y表示纵
    30. int index_y;
    31. int Square[100][4];//存放100个方格基本信息
    32. int main() {
    33. writeLogMsg("===天天扫雷=开始执行===");
    34. writeLogMsg("===开始界面=开始执行===");
    35. //initgraph(N * M, N * M,EW_SHOWCONSOLE);//, EW_SHOWCONSOLE初始化绘图窗口宽500高500像素
    36. initgraph(N * M, N * M);//初始化绘图窗口宽500高500像素
    37. //测试日志
    38. //int log[4] = {0,1,2,3};
    39. //writeLog(log, 9);
    40. writeLogMsg("===开始界面=开始执行===");
    41. StartWindow();//调用开始界面函数。
    42. writeLogMsg("===开始界面=结束执行===");
    43. //1.设置开始页面:背景透明、字体、背景音乐、背景图片,2.加载待使用的图片列表。3.判定鼠标位置和点击事件确定,用户选择的是15雷模式还是35雷模式
    44. if (gamemodel == 0) {
    45. normalModel();//15雷模式
    46. }
    47. else {
    48. BTmodel();//35雷模式
    49. }
    50. closegraph();
    51. writeLogMsg("===天天扫雷=结束执行===");
    52. }
    53. void StartWindow() {
    54. writeLogMsg("===设置界面背景、字体=开始执行===");
    55. setbkmode(TRANSPARENT); //设置背景 透明风格
    56. settextstyle(40, 18, L"楷体");//设置开始界面字体大小 L设置字符集
    57. //初始化页面
    58. //播放音乐
    59. writeLogMsg("===设置界面背景、字体=结束执行===");
    60. writeLogMsg("===设置播放背景音乐=开始执行===");
    61. mciSendString(L"open ./天空之城.mp3 alias bgm", 0, 0, 0); //L为设置字符集,./表示当前文件夹
    62. mciSendString(L"play bgm repeat", 0, 0, 0);//重复播放bgm
    63. writeLogMsg("===设置播放背景音乐=结束执行===");
    64. //加载图片
    65. /*
    66. 从图片文件获取图像(bmp / gif / jpg / png / tif / emf / wmf / ico)
    67. void loadimage(
    68. IMAGE * pDstImg, // 保存图像的 IMAGE 对象指针
    69. LPCTSTR pImgFile, // 图片文件名
    70. int nWidth = 0, // 图片的拉伸宽度
    71. int nHeight = 0, // 图片的拉伸高度
    72. bool bResize = false // 是否调整 IMAGE 的大小以适应图片
    73. );
    74. */
    75. writeLogMsg("===加载图片资源=开始执行===");
    76. loadimage(&image[0], L"./image/blank.jpg", M, M);
    77. loadimage(&image[1], L"./image/1.jpg", M, M);
    78. loadimage(&image[2], L"./image/2.jpg", M, M);
    79. loadimage(&image[3], L"./image/3.jpg", M, M);
    80. loadimage(&image[4], L"./image/4.jpg", M, M);
    81. loadimage(&image[5], L"./image/5.jpg", M, M);
    82. loadimage(&image[6], L"./image/6.jpg", M, M);
    83. loadimage(&image[7], L"./image/7.jpg", M, M);
    84. loadimage(&image[8], L"./image/8.jpg", M, M);
    85. loadimage(&image[9], L"./image/lei.jpg", M, M);
    86. loadimage(&image[10], L"./image/tag.jpg", M, M);
    87. loadimage(&image[11], L"./image/start.jpg", N * M, N * M);
    88. loadimage(&image[12], L"./image/0.jpg", M, M);
    89. writeLogMsg("===加载图片资源=开始执行===");
    90. /*
    91. // 绘制图像
    92. void putimage(
    93. int dstX, // 绘制位置的 x 坐标
    94. int dstY, // 绘制位置的 y 坐标
    95. IMAGE *pSrcImg, // 要绘制的 IMAGE 对象指针
    96. DWORD dwRop = SRCCOPY // 三元光栅操作码
    97. );
    98. */
    99. writeLogMsg("===设置开始界面显示图片=开始执行===");
    100. putimage(0, 0, &image[11]);//将image[11]显示在屏幕上,坐标0,0
    101. writeLogMsg("===设置开始界面显示图片=结束执行===");
    102. writeLogMsg("===获取鼠标事件信息、设置15、35雷模式字体,坐标区域=开始执行===");
    103. MOUSEMSG msg = { 0 };//鼠标事件信息
    104. while (1)
    105. {
    106. msg = GetMouseMsg();//获取鼠标位置,存到msg里面
    107. //判断鼠标位置是否在15雷模式上,如果在设置颜色为红色
    108. if ((msg.x > 180 && msg.x < 320) &&( msg.y >250 && msg.y < 290))
    109. {
    110. settextcolor(RGB(255, 0, 0)); //设置字体颜色红色
    111. outtextxy(180, 250, L"15雷模式");//输出 15雷模式 在x180,y250//outtextxyS
    112. }
    113. //判断鼠标位置是否在35雷模式上,如果在设置颜色为红色
    114. else if ((msg.x > 180 && msg.x < 320) &&( msg.y >330 && msg.y < 370))
    115. {
    116. settextcolor(RGB(255, 0, 0)); //设置字体颜色红色
    117. outtextxy(180, 330, L"35雷模式");//输出 35雷模式 在x180,y330
    118. }
    119. //判断鼠标位置是否在退出上,如果在设置颜色为红色
    120. else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450)
    121. {
    122. settextcolor(RGB(255, 0, 0)); //设置字体颜色红色
    123. outtextxy(180, 410, L"退出");//输出 退出 在x180,y410
    124. }
    125. //判断鼠标位置是否在背景部分,如果在设置颜色为黑色
    126. else
    127. {
    128. settextcolor(RGB(0, 0, 0)); //设置字体颜色为黑色
    129. outtextxy(180, 250, L"15雷模式");
    130. outtextxy(180, 330, L"35雷模式");
    131. outtextxy(180, 410, L"退出");
    132. }
    133. switch (msg.uMsg)
    134. {
    135. case WM_LBUTTONDOWN://当点击鼠标左键后,判定鼠标坐标位置。
    136. //如果以下坐标区域则设置为正常模式0
    137. if (msg.x > 180 && msg.x < 320 && msg.y >250 && msg.y < 290) {
    138. gamemodel = 0;//将游戏模式设为0(15雷模式)
    139. return;
    140. }//如果以下坐标区域则设置为风控模式1
    141. else if (msg.x > 180 && msg.x < 320 && msg.y >330 && msg.y < 370) {
    142. gamemodel = 1;//将游戏模式设为1(35雷模式)
    143. return;
    144. }//如果以下坐标区域则退出游戏
    145. else if (msg.x > 180 && msg.x < 320 && msg.y >410 && msg.y < 450) {
    146. exit(0);//退出游戏(终止)
    147. }
    148. }
    149. }
    150. writeLogMsg("===获取鼠标事件信息、设置15、35雷模式字体,坐标区域=结束执行===");
    151. }
    152. void normalModel()//15雷模式(普通模式)
    153. {
    154. writeLogMsg("===15雷模式=开始执行===");
    155. while (1)
    156. {
    157. cleardevice();//清屏
    158. win = N * N - 15;//设置胜利条件为把除了雷的所有方块点完即可
    159. int type = 0;
    160. HWND hWnd = GetHWnd();//GetHWnd()这个函数用于获取绘图窗口句柄
    161. int x, y, flag = 0;
    162. srand((unsigned)time(NULL));//播种随机数种子
    163. writeLogMsg("===初始化map[][]每个坐标的值开始执行===");
    164. //通过双重for循环把所有数组归零
    165. for (int i = 0; i < N + 2; i++)
    166. {
    167. for (int j = 0; j < N + 2; j++)
    168. {
    169. map[i][j] = 0;//把该数组的位置归0
    170. }
    171. }
    172. writeLogMsg("===初始化map[][]每个坐标的值结束执行===");
    173. //随机布雷
    174. writeLogMsg("===随机布雷开始执行===");
    175. while (flag < 15)//控制总雷数不超过15个
    176. {
    177. x = rand() % 10 + 1;//随机生成在第几行(1-10行)
    178. y = rand() % 10 + 1;//随机生成在第几列(1-10列)
    179. //随机方格坐标
    180. string msgXY = "may[" + to_string(x) + "][" + to_string(y )+ "]"+"当前数值value is "+to_string(map[x][y]);
    181. if (map[x][y] != -1) //随机获取坐标为map[x][y]的值,判定是否不等于-1(-1表示雷),如果等于-1则表示该位置已经是雷。
    182. {
    183. map[x][y] = -1;//如果不等于-1,则赋值该位置为-1(布雷)
    184. flag++;//总雷数增加1个
    185. writeLogMsg("may[" + to_string(x) + "][" + to_string(y) + "]" + "当前数值value is " + to_string(map[x][y]) + "埋雷成功!!!累计埋雷个数===>" + to_string(flag));
    186. }
    187. }
    188. writeLogMsg("===随机布雷结束执行===");
    189. writeLogMsg("===计算每个方格周围雷数量开始执行===");
    190. //计算除了已布雷的格子之外,每个格子周围雷的数量并确定数字
    191. for (int i = 1; i <= N; i++) //扫描数组10X10显示部分
    192. {
    193. //writeLogMsg("===扫描第【"+to_string(i)+"】行开始执行===");
    194. for (int j = 1; j <= N; j++)
    195. {
    196. squ_num++;
    197. if (map[i][j] != -1)//如果这个数组的位置不是-1即表示不是地雷
    198. {
    199. //writeLogMsg("==方格编号【"+ to_string(squ_num) +"】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷!!!开始扫描周围雷总数。当前map【】【】 valuse is==="+to_string(map[i][j]));
    200. for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子
    201. {
    202. for (int n = j - 1; n <= j + 1; n++)
    203. {
    204. if (map[m][n] == -1)
    205. {
    206. map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)
    207. }
    208. }
    209. }
    210. //writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】不是雷,扫描周围雷总数共计map[i][j]值为==>" + to_string(map[i][j]) + "个");
    211. }else{
    212. //writeLogMsg("==方格编号【" + to_string(squ_num) + "】=扫描第map【" + to_string(i) + "】行第【" + to_string(j) + "】是雷,不扫锚周围!map[i][j]值为==>" + to_string(map[i][j]) );
    213. }
    214. }
    215. //writeLogMsg("===扫描第【" + to_string(i) + "】行结束执行===");
    216. }
    217. writeLogMsg("===计算每个方格周围雷数量结束执行===");
    218. //判断输赢
    219. int whileCount = 0;
    220. while (1)
    221. {
    222. whileCount++;
    223. writeLogMsg("===根据map[i][j]存放数值设置对应图片开始执行===");
    224. DrawGraph();//更新雷的图片,数字对应的图片
    225. writeLogMsg("===根据map[i][j]存放数值设置对应图片结束执行===");
    226. type = MouseClick();
    227. if (type == -1) //判断用户是否点到雷了
    228. {
    229. DrawGraph();
    230. if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败!", MB_ICONINFORMATION |MB_OK) == IDOK) break;//输出提示框
    231. }
    232. if (win == 0)//判断用户是否赢了,胜利条件为除了雷的所有方块点完即可
    233. {
    234. DrawGraph();
    235. if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功!", MB_ICONINFORMATION |MB_OK) == IDOK) break;//输出提示框
    236. }
    237. writeLogMsg("===type = MouseClick()当前type值===》" + to_string(type));
    238. writeLogMsg("===win当前win值===》" + to_string(win));
    239. writeLogMsg("===设置图片循环次数===>" + to_string(whileCount));
    240. }
    241. }
    242. writeLogMsg("===15雷模式=结束执行===");
    243. }
    244. void BTmodel() //35雷模式(疯狂模式)
    245. {
    246. while (1)
    247. {
    248. cleardevice();//清屏
    249. win = N * N - 35;//设置胜利条件为把除了雷的所有方块点完即可
    250. int type = 0;
    251. HWND hWnd = GetHWnd();//这个函数用于获取绘图窗口句柄
    252. int x, y, flag = 0;
    253. srand((unsigned)time(NULL));//播种随机数种子
    254. //通过双重for循环把所有数组归零
    255. for (int i = 0; i < N + 2; i++)
    256. {
    257. for (int j = 0; j < N + 2; j++)
    258. {
    259. map[i][j] = 0;//把该数组的位置归0
    260. }
    261. }
    262. //布雷循环
    263. while (flag < 35)//控制总雷数不超过35个
    264. {
    265. x = rand() % 10 + 1;//随机生成在第几行
    266. y = rand() % 10 + 1;//随机生成在第几列
    267. if (map[x][y] != -1) //为了判断所生成的雷有没有重复
    268. {
    269. map[x][y] = -1;//将此数组设为-1(-1表示雷)
    270. flag++;//总雷数增加1个
    271. }
    272. }
    273. //布数字
    274. for (int i = 1; i <= N; i++) //扫描数组10X10显示部分
    275. {
    276. for (int j = 1; j <= N; j++)
    277. {
    278. if (map[i][j] != -1)//如果这个数组的位置不是-1(地雷)
    279. {
    280. for (int m = i - 1; m <= i + 1; m++) //扫描包含该数字周围的九个格子
    281. {
    282. for (int n = j - 1; n <= j + 1; n++)
    283. {
    284. if (map[m][n] == -1)
    285. {
    286. map[i][j]++;//确定除了雷每个格子中间数字,(也就是周围八个格子的总雷数)
    287. }
    288. }
    289. }
    290. }
    291. }
    292. }
    293. //判断输赢
    294. while (1)
    295. {
    296. DrawGraph();
    297. type = MouseClick();
    298. if (type == -1) //判断用户是否点到雷了
    299. {
    300. DrawGraph();
    301. if (MessageBox(hWnd, L"按下确定重玩", L"很遗憾失败", MB_ICONINFORMATION|MB_OK) == IDOK) break;//输出提示框
    302. }
    303. if (win == 0)//判断用户是否赢了
    304. {
    305. DrawGraph();
    306. if (MessageBox(hWnd, L"按下确定重玩", L"恭喜成功", MB_ICONINFORMATION| MB_OK) == IDOK) break;//输出提示框
    307. }
    308. }
    309. }
    310. }
    311. void DrawGraph()
    312. {
    313. int count_num=0;//计数器方格数
    314. for (int i = 1; i <= N; i++)
    315. {
    316. for (int j = 1; j <= N; j++) {
    317. count_num++;
    318. //writeLogMsg("DrawGraph==>当前扫描第【"+ to_string(count_num) +"】map【" + to_string(i) + "】行第【" + to_string(j) + "】map[i][j]值为 ==> " + to_string(map[i][j]));
    319. switch (map[i][j])
    320. {
    321. case 9:putimage((i - 1) * M, (j - 1) * M, &image[9]); break;
    322. case 10:putimage((i - 1) * M, (j - 1) * M, &image[0]); break;
    323. case 11:putimage((i - 1) * M, (j - 1) * M, &image[1]); break;
    324. case 12:putimage((i - 1) * M, (j - 1) * M, &image[2]); break;
    325. case 13:putimage((i - 1) * M, (j - 1) * M, &image[3]); break;
    326. case 14:putimage((i - 1) * M, (j - 1) * M, &image[4]); break;
    327. case 15:putimage((i - 1) * M, (j - 1) * M, &image[5]); break;
    328. case 16:putimage((i - 1) * M, (j - 1) * M, &image[6]); break;
    329. case 17:putimage((i - 1) * M, (j - 1) * M, &image[7]); break;
    330. case 18:putimage((i - 1) * M, (j - 1) * M, &image[8]); break;
    331. case 29:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    332. case 30:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    333. case 31:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    334. case 32:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    335. case 33:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    336. case 34:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    337. case 35:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    338. case 36:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    339. case 37:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    340. case 38:putimage((i - 1) * M, (j - 1) * M, &image[10]); break;
    341. default:putimage((i - 1) * M, (j - 1) * M, &image[12]); break;
    342. }
    343. }
    344. }
    345. }
    346. int MouseClick()
    347. {
    348. MOUSEMSG msg = { 0 };
    349. int loadingPlayCout = 0;
    350. while (1) {
    351. msg = GetMouseMsg();
    352. writeLogMsg("===MouseClick循环while开始执行===msg.x==》【" +to_string( msg.x)+"】msg.y==》【"+ to_string(msg.y)+"】");
    353. switch (msg.uMsg)
    354. {
    355. case WM_LBUTTONDOWN:
    356. writeLogMsg("===msg.uMsg.WM_LBUTTONDOWN===正在点击左键");
    357. if (map[msg.x / M + 1][msg.y / M + 1] == 0) {
    358. writeLogMsg("===map【"+to_string( msg.x / M) +"】【"+ to_string(msg.y / M) +"】的值是===>"+to_string(map[msg.x / M][msg.x / M]));
    359. writeLogMsg("当前方格周围没有雷!!!");
    360. writeLogMsg("开始加载loadingPlay 没有雷更换为无雷的图片递归循环判断!");
    361. loadingPlay(msg.x / M + 1, msg.y / M + 1);
    362. loadingPlayCout++;
    363. writeLogMsg("当前loadingPlayCout的值得是===>" + to_string(loadingPlayCout));
    364. writeLogMsg("结束加载loadingPlay");
    365. }
    366. else if (map[msg.x / M + 1][msg.y / M + 1] <= 8) {
    367. writeLogMsg("当前方格周围有雷!!!");
    368. writeLogMsg("map[][]<=8开始执行");
    369. writeLogMsg("===map【" + to_string(msg.x / M) + "】【" + to_string(msg.y / M) + "】的值是===>" + to_string(map[msg.x / M][msg.x / M]));
    370. map[msg.x / M + 1][msg.y / M + 1] += 10;
    371. win--;
    372. writeLogMsg("当前win的值是==>"+to_string(win));
    373. writeLogMsg("map[][]<=8结束执行");
    374. }
    375. if (map[msg.x / M + 1][msg.y / M + 1] == 9) {
    376. writeLogMsg("map[][]等于9");
    377. return -1;
    378. }
    379. break;
    380. case WM_RBUTTONDOWN:
    381. writeLogMsg("===msg.uMsg.WM_LBUTTONDOWN===正在点击右键");
    382. if (map[msg.x / M + 1][msg.y / M + 1] <= 8) {
    383. writeLogMsg("map[][]<=8开始执行");
    384. map[msg.x / M + 1][msg.y / M + 1] += 30;
    385. }
    386. else if (map[msg.x / M + 1][msg.y / M + 1] >= 29) {
    387. writeLogMsg("map[][]>=29开始执行");
    388. map[msg.x / M + 1][msg.y / M + 1] -= 30;
    389. }
    390. break;
    391. }
    392. writeLogMsg("===MouseClick循环while结束执行===" );
    393. return 0;
    394. }
    395. }
    396. void loadingPlay(int x, int y) {
    397. map[x][y] += 10;
    398. win--;
    399. for (int i = x - 1; i <= x + 1; i++) {
    400. for (int j = y - 1; j <= y + 1; j++) {
    401. if (i <= 0 || i >= 11 || j <= 0 || j >= 11) continue; //防止越界
    402. if (map[i][j] <= 8) {
    403. if (map[i][j] == 0) {
    404. loadingPlay(i, j);
    405. }
    406. else if (map[i][j] != -1) {
    407. map[i][j] += 10;
    408. win--;
    409. }
    410. }
    411. }
    412. }
    413. }
    414. void writeLog(int squ[], int n){
    415. //获取当前系统时间
    416. SYSTEMTIME sys;
    417. GetLocalTime(&sys);
    418. fstream myFile;
    419. myFile.open("log.txt", ios::out | ios::binary);
    420. if (!myFile) {
    421. cout << "log.txt can't open!" << endl;
    422. abort();
    423. }
    424. int count = n;
    425. myFile << "累计行数===>"<
    426. for (int i = 0; i < count; i++) {
    427. cout << "log.txt 【Write】===>第" << i <<"行" << endl;
    428. 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;
    429. }
    430. myFile.close();
    431. }
    432. void writeLogMsg(string msg) {
    433. //获取当前系统时间
    434. SYSTEMTIME sys;
    435. GetLocalTime(&sys);
    436. fstream myFile;
    437. myFile.open("log.txt", ios::out | ios::app);
    438. if (!myFile) {
    439. cout << "log.txt can't open!" << endl;
    440. abort();
    441. }
    442. myFile << "【" << sys.wYear << "-" << sys.wMonth << "-" << sys.wDay << " " << sys.wHour << ":" << sys.wMinute << ":" << sys.wSecond << "." << sys.wMilliseconds << " 星期" << sys.wDayOfWeek << "】" << "\t" << msg << endl;
    443. myFile.close();
    444. /*
    445. #include
    446. #include
    447. using namespace std;
    448. int main()
    449. {
    450. fstream f;
    451. //追加写入,在原来基础上加了ios::app
    452. f.open("data.txt",ios::out|ios::app);
    453. //输入你想写入的内容
    454. f<<"今天天气不错"<
    455. f.close();
    456. return 0;
    457. }
    458. */
    459. }

    回顾编程过程

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

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

  • 相关阅读:
    Scala入门到精通(尚硅谷学习笔记)章节十——模式匹配
    c++ 模板详解
    Android ContentProvider
    抓包工具 Charles 使用手册
    软件测试|Python自动化测试实现的思路
    接口偶尔超时,竟又是JVM停顿的锅!
    node.js-模块化
    【面试必刷101】链表
    你我他是谁
    【AutoSAR CAN】01 - CAN模块的功能及提供的API
  • 原文地址:https://blog.csdn.net/m0_73787047/article/details/136287145