C语言初学者复刻经典扫雷小游戏(图形界面,非黑白窗口)(含源码)
注:除计时器和剩余雷数显示外,其他功能完美还原。
目录
一、程序演示
二、程序信息
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, number 和 status矩阵中元素坐标。这样做的好处是方便后面的鼠标机交互坐标检测。
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语言初学者复刻经典扫雷小游戏(图形界面,非黑白窗口)(含源码)相关推荐
- C语言实现扫雷小游戏 纯小白 非黑窗口
C语言实现一个普通的扫雷小游戏 纯小白所编(含代码非黑窗口!) 扫雷 主要功能 1.创建一个图形界面 2.了解扫雷游戏的原理 3.随机生成雷的位置 4.为整个数组加密,并在雷周围的位置加一 5.导入图 ...
- 学编程很枯燥?用Python制作3个小游戏,边玩边学(含源码)
经常听到有朋友说,学习编程是一件非常枯燥无味的事情.其实,大家有没有认真想过,可能是我们的学习方法不对? 比方说,你有没有想过,可以通过打游戏来学编程?今天我想跟大家分享几个Python小游戏,教你如 ...
- C语言简单的键盘玩扫雷小游戏(完结)
1:这次我们会将前面的代码进行整合,和整理,最终使我们的程序可以有效的运行起来. [1]初始化函数. void GameInit() { //随机数种子 srand((unsigned ...
- C语言系列(1) --- C语言实现经典扫雷小游戏
(一)游戏简介 游戏初始界面有两个选择,选项"1"为开始游戏,选项"0"为退出游戏:选择开始游戏之后将会打印出9*9的棋盘,此时系统已经为游戏设置了10个雷,输 ...
- 用C语言实现一个简单的扫雷小游戏(附全代码及教程)
本文实例为大家分享了C语言实现扫雷游戏的具体代码,供大家参考,具体内容如下: 首先,创建一个text.c文件: 编写主函数: int main() {test();return 0; } 定义test ...
- 【C语言实现】全面的扫雷小游戏(包括空白展开、标记等)具体步骤加代码分析
文章目录 前言 一.问题描述 二.基本框架构思 三.具体实现 1.扫雷接口实现 2.地图初始化 3.设置雷 4.显示界面 5.开始扫雷 6.计算周围雷的数量 7.排查雷 8.空白展开 9.标记雷 10 ...
- C语言项目:接球小游戏(自制)!详细思路+源码分享
每天一个C语言小项目,提升你的编程能力! 用VS写了一个小小的游戏,在界面右侧有运行时间,接到的小球个数等信息,有 10 个小球下落,玩家可以控制一个盒子左右移动(方向键),来接小球,按 Esc 键退 ...
- Java经典小游戏——贪吃蛇简单实现(附源码)
文章目录 一.使用知识 二.使用工具 三.开发过程 3.1素材准备 3.2 开发过程 3.2.1 创建项目 3.2.2 页面设计 3.23 画蛇 3.24创建蛇的食物 3.2.5增加蛇的存活状态 3. ...
- 大家都在发圣诞树,我偏偏要发一个圣诞小游戏给大家玩【内附源码】
大家好,我是辣条. 前言 圣诞节快来了,热榜都被一堆圣诞树攻占了,这样的流量密码我怎么会错过,大家都发圣诞树,我就不发啦,直接分享一个圣诞小游戏给大家玩,代码太长一定要先赞和收藏. 领取福利 300 ...
最新文章
- linux shell 创建序列数组(list,array)方法
- 内核并发控制---读写自旋锁 (来自网易)
- 2019.7.23整理记录以及四道题
- 流行前沿的暗黑APP主题UI设计素材模板
- php文字红色代码,IOS_IOS中一段文字设置多种字体颜色代码,给定range和需要设置的颜色, - phpStudy...
- 计算机网络物理层之数据通信的基础知识
- ubuntu下锐捷客户端连接校园网
- winrar 4.20注册码
- 未来大数据的主要应用领域包括哪些
- PHP实现图片加文字/图案水印
- Win11输入法的选字框不见了怎么办?
- matplotlib learning-----案例:对比电影的票房收入(3)
- Anaconda配置强化学习环境
- 用python模拟登录12306
- MySQL week()函数及参数mode详解
- 全面解读VTL(虚拟磁带库)
- 解放拖动屏幕的双手——用xrandr配置多屏显示
- Direct3D 9.0 SDK 文档(中文版)
- 内推名企实习,就来CSDN超级实习生计划,2022年名企实习内推开始发车
- 飞船打外星人(python)