1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > C语言初学者复刻经典扫雷小游戏(图形界面 非黑白窗口)(含源码)

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

时间:2021-05-13 14:33:51

相关推荐

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

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

目录

一、程序演示

二、程序信息

1.基础信息

2.前言

3.所需文件

三、代码解析

1.头文件

2.变量声明

3.随机生成雷

4.生成雷位置矩阵

5.生成雷数矩阵

6.绘制界面

7.输赢检测

8.鼠标交互逻辑

9.检测相邻空元素

10.拓展空元素区域

11.按照状态贴图

12.游戏成功

13.游戏失败

14.展示地雷

一、程序演示

二、程序信息

1.基础信息

程序名:扫雷

开发语言:C语言

程序作者:YYYwaiwai

开发工具:VS

需求环境: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);}}}}

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。