注:除计时器和剩余雷数显示外,其他功能完美还原。

目录

一、程序演示

二、程序信息

1.基础信息

2.前言

3.所需文件

三、代码解析

1.头文件

2.变量声明

3.随机生成雷

4.生成雷位置矩阵

5.生成雷数矩阵

6.绘制界面

7.输赢检测

8.鼠标交互逻辑

9.检测相邻空元素

10.拓展空元素区域

11.按照状态贴图

12.游戏成功

13.游戏失败

14.展示地雷


一、程序演示

二、程序信息

1.基础信息

程序名:扫雷

开发语言:C语言

程序作者:YYYwaiwai

开发工具:VS2019

需求环境:Easy-X Graphics.h 图形库

源码下载:扫雷源码及图片素材

2.前言

本人小白一枚,目前大一

该程序只是自己写着玩的一个小游戏,因此并没有完全写完

计时器功能和剩余雷显示功能懒得做了,所以只是半成品

但是其他功能几乎完美复刻经典版扫雷游戏

本程序为本人原创,没有参考任何其他资料或博客

图片素材也为本人绘制,图片素材包含在源码压缩包中

如有问题,请大佬们指正!

3.所需文件

如上图,pic文件夹内包含了该程序所需的图片素材

请将问文件夹置于程序根目录下

三、代码解析

1.头文件

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <graphics.h>
#include <time.h>
#pragma comment(lib,"winmm.lib")
#pragma warning(disable:4996)

2.变量声明

int matrix[16][16] = { 0 };
int number[16][16] = { 0 };
int status[16][16] = { 0 };int num = 0;
int history[40] = { 0 };
int i = 0;
int j = 0;
int k = 0;
int m = 0;
int n = 0;
int counter = 0;
int boom = 0;
int his = 0;
int select;
int wincount = 0;int break_flag = 0;
int fail_flag = 0;

3.随机生成雷

此处的想法是,建立一个16*16的矩阵,从左到右,从上到下由1~256编号

通过生成40个范围处于【1,256】的随机数,通过对应矩阵的位置,将对应的编号设为雷

为防止生成的随机数有重复的,每生成一个随机数,就将这个数存入his[40]这个数组中

之后生成的随机数需与数组中的元素进行比较,若重复则不会保存在数组中

    srand((unsigned int)time(0));  //通过用时间播种生成随机数for (boom = 0; boom < 40; boom++){select = rand() % 256 + 1;  //生成1~256范围内随机数if (select < 0 || select > 256)boom--;else{for (his = 0; his < 40; his++){if (history[his] == select)  //与his数组内元素比较{boom--;break;}if (history[his] == 0)  //若没有出现过,则存入his数组{history[his] = select;break;}}}}//雷位置调试/*for (i = 0; i < 40; i++)printf("%d\n", history[i]);*/

雷位置调试打印:

4.生成雷位置矩阵

通过his数组中的随机数,通过对应关系写入雷矩阵matrix[16][16]中

雷的位置表示为1,非雷的位置表示为0

for(his = 0; his < 40; his++ ){for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){counter++;if (history[his] == counter){matrix[i][j] = 1;break_flag = 1;break;}}if (break_flag == 1)break;}break_flag = 0;counter = 0;}//雷矩阵调试/*//雷矩阵调试for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (matrix[i][j] == 1)colour(12);elsecolour(15);printf("%d ", matrix[i][j]);}colour(15);printf("\n");}*/

雷矩阵调试打印:

5.生成雷数矩阵

通过对matrix矩阵每个元素的周围八个元素计算雷数,并将雷数存入number[16][16]矩阵中

number矩阵中1~8表示雷数,9表示该元素为雷

此处的检测方法并不好,但本人懒得改了。更优的检测方法请参考9.检测相邻空元素

for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){num = 0;if (matrix[i][j] == 1){number[i][j] = 9;continue;}else{if (i == 0 && j == 0){if (matrix[0][1] == 1)num++;if (matrix[1][0] == 1)num++;if (matrix[1][1] == 1)num++;}else if (i == 0 && j == 15){if (matrix[0][14] == 1)num++;if (matrix[1][15] == 1)num++;if (matrix[1][14] == 1)num++;}else if (i == 15 && j == 0){if (matrix[15][1] == 1)num++;if (matrix[14][0] == 1)num++;if (matrix[14][1] == 1)num++;}else if (i == 15 && j == 15){if (matrix[15][14] == 1)num++;if (matrix[14][15] == 1)num++;if (matrix[14][14] == 1)num++;}else if (i == 0){if (matrix[i][j-1] == 1)num++;if (matrix[i][j + 1] == 1)num++;if (matrix[i + 1][j - 1] == 1)num++;if (matrix[i + 1][j] == 1)num++;if (matrix[i + 1][j + 1] == 1)num++;}else if (i == 15){if (matrix[i][j - 1] == 1)num++;if (matrix[i][j + 1] == 1)num++;if (matrix[i - 1][j - 1] == 1)num++;if (matrix[i - 1][j] == 1)num++;if (matrix[i - 1][j + 1] == 1)num++;}else if (j == 0){if (matrix[i - 1][j] == 1)num++;if (matrix[i + 1][j] == 1)num++;if (matrix[i - 1][j + 1] == 1)num++;if (matrix[i][j + 1] == 1)num++;if (matrix[i + 1][j + 1] == 1)num++;}else if (j == 15){if (matrix[i - 1][j] == 1)num++;if (matrix[i + 1][j] == 1)num++;if (matrix[i - 1][j - 1] == 1)num++;if (matrix[i][j - 1] == 1)num++;if (matrix[i + 1][j - 1] == 1)num++;}else{if (matrix[i - 1][j - 1] == 1)num++;if (matrix[i - 1][j] == 1)num++;if (matrix[i - 1][j + 1] == 1)num++;if (matrix[i][j - 1] == 1)num++;if (matrix[i][j + 1] == 1)num++;if (matrix[i + 1][j - 1] == 1)num++;if (matrix[i + 1][j] == 1)num++;if (matrix[i + 1][j + 1] == 1)num++;}}number[i][j] = num;}}//数字阵调试for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (number[i][j] == 9)colour(12);elsecolour(15);printf("%d ", number[i][j]);}colour(15);printf("\n");}

数字阵调试打印:

6.绘制界面

此处通过两个循环嵌套来绘制扫雷的矩阵,循环中的i和j也同时对应matrix,  numberstatus矩阵中元素坐标。这样做的好处是方便后面的鼠标机交互坐标检测。

    initgraph(745, 850);//载入素材IMAGE cube;IMAGE cube_trigger;IMAGE background;IMAGE mine_eliminate;IMAGE mine_trigger;IMAGE mine_flag;IMAGE question;IMAGE question_trigger;IMAGE smile;IMAGE smile_trigger;IMAGE caution;IMAGE dead;IMAGE dead_trigger;IMAGE mine_0;IMAGE mine_1;IMAGE mine_2;IMAGE mine_3;IMAGE mine_4;IMAGE mine_5;IMAGE mine_6;IMAGE mine_7;IMAGE mine_8;IMAGE mine_9;loadimage(&cube, "./pic/cube.jpg");loadimage(&cube_trigger, "./pic/cube_trigger.jpg");loadimage(&background, "./pic/background.jpg");loadimage(&mine_eliminate, "./pic/mine_eliminate.jpg");loadimage(&mine_trigger, "./pic/mine_trigger.jpg");loadimage(&mine_flag, "./pic/mine_flag.jpg");loadimage(&question, "./pic/question.jpg");loadimage(&question_trigger, "./pic/question_trigger.jpg");loadimage(&smile, "./pic/smile.jpg");loadimage(&smile_trigger, "./pic/smile_trigger.jpg");loadimage(&caution, "./pic/caution.jpg");loadimage(&dead, "./pic/dead.jpg");loadimage(&dead_trigger, "./pic/dead_trigger.jpg");loadimage(&mine_0, "./pic/mine_0.jpg");loadimage(&mine_1, "./pic/mine_1.jpg");loadimage(&mine_2, "./pic/mine_2.jpg");loadimage(&mine_3, "./pic/mine_3.jpg");loadimage(&mine_4, "./pic/mine_4.jpg");loadimage(&mine_5, "./pic/mine_5.jpg");loadimage(&mine_6, "./pic/mine_6.jpg");loadimage(&mine_7, "./pic/mine_7.jpg");loadimage(&mine_8, "./pic/mine_8.jpg");//贴图putimage(0, 0, &background);putimage(333, 17, &smile);for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){putimage(15 + 45 * j, 118 + 45 * i, &cube);}}

7.输赢检测

在游戏逻辑大循环前进行输赢检测

通过遍历status矩阵中的值进行计数

若值4的数量到达216即判定为胜利

status矩阵内元素值的含义:

0 = 未触发的元素

1 = 鼠标左键点击某元素后与其相邻的number矩阵中值为0的,但还未检测周边元素的元素

2 = 鼠标左键点击某元素后触发拓展后与number矩阵中值为0的元素相邻的number矩阵中值为1~8的元素

3 = 鼠标左键点击某元素后与其相邻的number矩阵中值为0的,且已检测周边元素的元素

4 = 界面上已被贴图位置(空贴图,及1~8数字贴图)所对应的元素

5 = 鼠标右键点击某元素后,该元素位置被贴为红旗的元素

6 = 鼠标右键点击某元素后,该元素位置被贴为问号的元素

while (1){for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 5)if (matrix[i][j] == 1)wincount++;}}if (wincount == 40){fail_flag = 2;  //游戏成功旗帜break;}wincount = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 4)wincount++;}}if (wincount == 216){fail_flag = 2;  //游戏成功旗帜break;}wincount = 0;

8.鼠标交互逻辑

        MOUSEMSG mouse = GetMouseMsg();for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (15 + 45 * j <= mouse.x && mouse.x <= 15 + 45 * j + 42 && 118 + 45 * i <= mouse.y && mouse.y <= 118 + 45 * i + 42 && (status[i][j] == 0 || status[i][j] == 5 || status[i][j] == 6))  //检测鼠标处于哪个元素范围内,已被贴图的元素不再参与交互监测{if (mouse.uMsg == WM_LBUTTONDOWN && status[i][j] != 5)  //如果左键点击{putimage(333, 17, &caution);  //界面上方笑脸样式改变if (number[i][j] == 9)  //如果点击的元素为雷{putimage(15 + 45 * j, 118 + 45 * i, &mine_trigger);fail_flag = 1;  //触发游戏失败的旗break_flag = 1;break;}else if (number[i][j] != 0)  //如果点击的元素不为雷且不为空{switch (number[i][j])  //按照相邻元素中雷的数量进行贴图{case 1: putimage(15 + 45 * j, 118 + 45 * i, &mine_1); break;case 2: putimage(15 + 45 * j, 118 + 45 * i, &mine_2); break;case 3: putimage(15 + 45 * j, 118 + 45 * i, &mine_3); break;case 4: putimage(15 + 45 * j, 118 + 45 * i, &mine_4); break;case 5: putimage(15 + 45 * j, 118 + 45 * i, &mine_5); break;case 6: putimage(15 + 45 * j, 118 + 45 * i, &mine_6); break;case 7: putimage(15 + 45 * j, 118 + 45 * i, &mine_7); break;case 8: putimage(15 + 45 * j, 118 + 45 * i, &mine_8); break;}status[i][j] = 4;  //转换状态为已贴图}else  //该元素不为雷,且相邻元素也没有雷{status[i][j] = 1;detect_null(i, j, number, status);  //扫描周围所有的空元素expand_null(status);  //对空元素周围的数字元素进行拓展view(number, status, mine_0, mine_1, mine_2, mine_3, mine_4, mine_5, mine_6, mine_7, mine_8);  //对空元素及拓展出的元素进行贴图}Sleep(250);putimage(333, 17, &smile);  //界面上方笑脸}else if (mouse.uMsg == WM_RBUTTONDOWN)  //如果右键点击{if (status[i][j] == 0){status[i][j] = 5;putimage(15 + 45 * j, 118 + 45 * i, &mine_flag);}else if (status[i][j] == 5)status[i][j] = 6;else if (status[i][j] == 6)status[i][j] = 0;}                              //将元素状态在未触发,插旗与问号之间切换else{if(status[i][j] == 0)putimage(15 + 45 * j, 118 + 45 * i, &cube_trigger);else if(status[i][j] == 6)putimage(15 + 45 * j, 118 + 45 * i, &question_trigger);}}else if (status[i][j] == 0 || status[i][j] == 6)  //鼠标交互反应贴图{if (status[i][j] == 0)putimage(15 + 45 * j, 118 + 45 * i, &cube);elseputimage(15 + 45 * j, 118 + 45 * i, &question);}}if (break_flag == 1)break;}if (break_flag == 1){break_flag = 0;break;}if (333 <= mouse.x && mouse.x <= 413 && 15 <= mouse.y && mouse.y <= 95){if (mouse.uMsg == WM_LBUTTONDOWN)  //如果点击笑脸,重新开始游戏{putimage(333, 17, &smile_trigger);Sleep(200);goto start;  //比较懒,所以才用了goto,请别喷我 :)}}}

9.检测相邻空元素

此处运用递归方法,实现了对触发空元素的所有相邻空元素的检测

检测完成的空元素,status矩阵中的状态值会被调整为3;而被检测出但还未检测其本身的元素状态值将会被设为1

status矩阵中不存在值为1的元素时,即表示所有相邻空元素已检测完成,递归停止

void detect_null(int i, int j, int number[16][16], int status[16][16])
{if( i != 0 )if (number[i - 1][j] == 0 && status[i - 1][j] != 3 && status[i - 1][j] != 5 && status[i - 1][j] != 6)status[i - 1][j] = 1;if( i != 15 )if (number[i + 1][j] == 0 && status[i + 1][j] != 3 && status[i + 1][j] != 5 && status[i + 1][j] != 6)status[i + 1][j] = 1;if( j != 0 )if (number[i][j - 1] == 0 && status[i][j - 1] != 3 && status[i][j - 1] != 5 && status[i][j - 1] != 6)status[i][j - 1] = 1;if( j != 15 )if (number[i][j + 1] == 0 && status[i][j + 1] != 3 && status[i][j + 1] != 5 && status[i][j + 1] != 6)status[i][j + 1] = 1;status[i][j] = 3;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 1)detect_null(i, j, number, status);}}
}

10.拓展空元素区域

经上一步检测出的空元素后,要将其相邻的数值为1~8的元素也贴上图

因此将status矩阵中值为3的元素的周围的值不为3的元素的状态值变为2,即表示即将要被贴图的不为空的元素

void expand_null(int status[16][16])
{int i = 0;int j = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 3){if (i != 0)if(status[i - 1][j] != 3 && status[i - 1][j] != 5 && status[i - 1][j] != 6)status[i - 1][j] = 2;if (i != 15)if (status[i + 1][j] != 3 && status[i + 1][j] != 5 && status[i + 1][j] != 6)status[i + 1][j] = 2;if (j != 0)if (status[i][j - 1] != 3 && status[i][j - 1] != 5 && status[i][j - 1] != 6)status[i][j - 1] = 2;if (j != 15)if (status[i][j + 1] != 3 && status[i][j + 1] != 5 && status[i][j + 1] != 6)status[i][j + 1] = 2;if(i != 0 && j != 0)if (status[i - 1][j - 1] != 3 && status[i - 1][j - 1] != 5 && status[i - 1][j - 1] != 6)status[i - 1][j - 1] = 2;if (i != 0 && j != 15)if (status[i - 1][j + 1] != 3 && status[i - 1][j + 1] != 5 && status[i - 1][j + 1] != 6)status[i - 1][j + 1] = 2;if (i != 15 && j != 0)if (status[i + 1][j - 1] != 3 && status[i + 1][j - 1] != 5 && status[i + 1][j - 1] != 6)status[i + 1][j - 1] = 2;if (i != 15 && j != 15)if (status[i + 1][j + 1] != 3 && status[i + 1][j + 1] != 5 && status[i + 1][j + 1] != 6)status[i + 1][j + 1] = 2;}}}
}

11.按照状态贴图

经过上两步的检测,所有要被贴图的元素状态已被设为2和3

因此仅需遍历status矩阵,找到状态为2和3的元素,在对照number矩阵中的值贴上空以及1~8的图片素材

void view(int number[16][16], int status[16][16], IMAGE mine_0, IMAGE mine_1, IMAGE mine_2, IMAGE mine_3, IMAGE mine_4, IMAGE mine_5, IMAGE mine_6, IMAGE mine_7, IMAGE mine_8)
{int i = 0;int j = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 2 || status[i][j] == 3 ){switch (number[i][j]){case 0: putimage(15 + 45 * j, 118 + 45 * i, &mine_0); break;case 1: putimage(15 + 45 * j, 118 + 45 * i, &mine_1); break;case 2: putimage(15 + 45 * j, 118 + 45 * i, &mine_2); break;case 3: putimage(15 + 45 * j, 118 + 45 * i, &mine_3); break;case 4: putimage(15 + 45 * j, 118 + 45 * i, &mine_4); break;case 5: putimage(15 + 45 * j, 118 + 45 * i, &mine_5); break;case 6: putimage(15 + 45 * j, 118 + 45 * i, &mine_6); break;case 7: putimage(15 + 45 * j, 118 + 45 * i, &mine_7); break;case 8: putimage(15 + 45 * j, 118 + 45 * i, &mine_8); break;}status[i][j] == 4;}}}
}

12.游戏成功

if (fail_flag == 2){settextcolor(RED);setbkmode(TRANSPARENT);settextstyle(80, 0, "黑体");outtextxy(110, 10, "Congragulate!");putimage(333, 17, &smile);Sleep(10);view_mine(matrix, mine_eliminate);system("pause");return 0;}

13.游戏失败

if (fail_flag == 1){settextcolor(RED);setbkmode(TRANSPARENT);settextstyle(100, 0, "黑体");outtextxy(122, 5, "GAME  OVER");putimage(333, 17, &dead);Sleep(10);view_mine(matrix, mine_eliminate);putimage(15 + 45 * j, 118 + 45 * i, &mine_trigger);while (1){MOUSEMSG mouse = GetMouseMsg();if (333 <= mouse.x && mouse.x <= 413 && 15 <= mouse.y && mouse.y <= 95)  //点击哭脸可重新开始游戏{if (mouse.uMsg == WM_LBUTTONDOWN){putimage(333, 17, &dead_trigger);Sleep(200);goto start;  //本人偷懒,所以用了goto,不喜勿喷}}}}

14.展示地雷

游戏失败后,需向玩家展示所有地雷的位置

根据雷位置所在元素进行贴图即可

void view_mine(int matrix[16][16], IMAGE mine_eliminate)
{int i = 0;int j = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (matrix[i][j] == 1){putimage(15 + 45 * j, 118 + 45 * i, &mine_eliminate);}}}
}

详尽代码请下载源码查看!

C语言初学者复刻经典扫雷小游戏(图形界面,非黑白窗口)(含源码)相关推荐

  1. C语言实现扫雷小游戏 纯小白 非黑窗口

    C语言实现一个普通的扫雷小游戏 纯小白所编(含代码非黑窗口!) 扫雷 主要功能 1.创建一个图形界面 2.了解扫雷游戏的原理 3.随机生成雷的位置 4.为整个数组加密,并在雷周围的位置加一 5.导入图 ...

  2. 学编程很枯燥?用Python制作3个小游戏,边玩边学(含源码)

    经常听到有朋友说,学习编程是一件非常枯燥无味的事情.其实,大家有没有认真想过,可能是我们的学习方法不对? 比方说,你有没有想过,可以通过打游戏来学编程?今天我想跟大家分享几个Python小游戏,教你如 ...

  3. C语言简单的键盘玩扫雷小游戏(完结)

    1:这次我们会将前面的代码进行整合,和整理,最终使我们的程序可以有效的运行起来. [1]初始化函数. void GameInit() {     //随机数种子     srand((unsigned ...

  4. C语言系列(1) --- C语言实现经典扫雷小游戏

    (一)游戏简介 游戏初始界面有两个选择,选项"1"为开始游戏,选项"0"为退出游戏:选择开始游戏之后将会打印出9*9的棋盘,此时系统已经为游戏设置了10个雷,输 ...

  5. 用C语言实现一个简单的扫雷小游戏(附全代码及教程)

    本文实例为大家分享了C语言实现扫雷游戏的具体代码,供大家参考,具体内容如下: 首先,创建一个text.c文件: 编写主函数: int main() {test();return 0; } 定义test ...

  6. 【C语言实现】全面的扫雷小游戏(包括空白展开、标记等)具体步骤加代码分析

    文章目录 前言 一.问题描述 二.基本框架构思 三.具体实现 1.扫雷接口实现 2.地图初始化 3.设置雷 4.显示界面 5.开始扫雷 6.计算周围雷的数量 7.排查雷 8.空白展开 9.标记雷 10 ...

  7. C语言项目:接球小游戏(自制)!详细思路+源码分享

    每天一个C语言小项目,提升你的编程能力! 用VS写了一个小小的游戏,在界面右侧有运行时间,接到的小球个数等信息,有 10 个小球下落,玩家可以控制一个盒子左右移动(方向键),来接小球,按 Esc 键退 ...

  8. Java经典小游戏——贪吃蛇简单实现(附源码)

    文章目录 一.使用知识 二.使用工具 三.开发过程 3.1素材准备 3.2 开发过程 3.2.1 创建项目 3.2.2 页面设计 3.23 画蛇 3.24创建蛇的食物 3.2.5增加蛇的存活状态 3. ...

  9. 大家都在发圣诞树,我偏偏要发一个圣诞小游戏给大家玩【内附源码】

    ​大家好,我是辣条. 前言 圣诞节快来了,热榜都被一堆圣诞树攻占了,这样的流量密码我怎么会错过,大家都发圣诞树,我就不发啦,直接分享一个圣诞小游戏给大家玩,代码太长一定要先赞和收藏. 领取福利 300 ...

最新文章

  1. linux shell 创建序列数组(list,array)方法
  2. 内核并发控制---读写自旋锁 (来自网易)
  3. 2019.7.23整理记录以及四道题
  4. 流行前沿的暗黑APP主题UI设计素材模板
  5. php文字红色代码,IOS_IOS中一段文字设置多种字体颜色代码,给定range和需要设置的颜色, - phpStudy...
  6. 计算机网络物理层之数据通信的基础知识
  7. ubuntu下锐捷客户端连接校园网
  8. winrar 4.20注册码
  9. 未来大数据的主要应用领域包括哪些
  10. PHP实现图片加文字/图案水印
  11. Win11输入法的选字框不见了怎么办?
  12. matplotlib learning-----案例:对比电影的票房收入(3)
  13. Anaconda配置强化学习环境
  14. 用python模拟登录12306
  15. MySQL week()函数及参数mode详解
  16. 全面解读VTL(虚拟磁带库)
  17. 解放拖动屏幕的双手——用xrandr配置多屏显示
  18. Direct3D 9.0 SDK 文档(中文版)
  19. 内推名企实习,就来CSDN超级实习生计划,2022年名企实习内推开始发车
  20. 飞船打外星人(python)

热门文章

  1. 【推荐收藏】倾心整理的Python量化资源大合集
  2. sendmessage WM_PAINT 无效(6月19日)
  3. 失控 - 人造与天生
  4. Hefei-NAMD报错与解决
  5. 微信小程序界面设计小程序中的WXSS(css)选择器课程-伪类-:nth-last-child伪类
  6. 11届蓝桥杯大赛青少年C++高级组模拟题
  7. window清理dns缓存
  8. 计算机科学与技术985211学校排名,八所实力最强的211高校名单
  9. 吉天wms智能仓储物流管理系统
  10. 13张图解分布式系统服务注册与发现机制,给你整明白