入门C语言第三话:数组之实战篇——扫雷(进阶版——图形化界面,递归展开,播放音乐与音效,标记取消雷,记录雷的个数,鼠标点击,文末附有完整代码)

前言

每日鸡汤

 不为模糊不清的未来过分担忧,只为清清楚楚的现在奋发图强。

基本思路

在这里插入图片描述

衔接基础班扫雷

 如果思路不是特别清晰的兄弟,建议多看一下基础版扫雷:数组之实战篇——带你从0到1实现扫雷(简易版)
 如果EasyX图形库不是特别明白:可以看一下这篇文章:入门EasyX图形库(带你进入不一样的程序窗口,超详解)
或者B站我自己找的教程:史上最详细的easyx图形库入门讲解
 这一篇与上一章的部分思路相同:放置雷与初始化雷盘,不过由于我们的游戏界面为图形化界面我们的雷盘只需设置一个存放信息(雷和雷周围的信息),而且放雷的时候可以直接将雷的信息放在雷的周围更加方便,由于存放信息的雷盘只有一个,因此我们设置整形的数组会更加方便一些!

准备阶段

所需图片:
在这里插入图片描述
图片资源: 百度云盘,点击即可进去
提取码: 6666
防止字符集出现问题:
在这里插入图片描述
为了更好的理解下文我们先把头文件先给出:

#define _CRT_SECURE_NO_WARNINGS 1
#define ROWS 11//行
#define COLS 11//列
#define COL (ROWS-2)//打印的列,括号是为了防止运算冲突4
#define ROW (COLS-2)//打印的行
#define MINE 10
#include <graphics.h>//要包含在SIZE的前面,防止与里面的SIZE冲突。EasyX要用
#define SIZE 50
#include <stdio.h>
#include <time.h>//time函数的头文件,c++只能用这个
#include <stdlib.h>//srand和rand函数的头文件,c++只能用这个
#include <mmsystem.h>//播放音乐所用的头文件,是系统自带的
#pragma comment(lib,"winmm.lib")//加载静态库
//初始化函数
void Initboard(int board[ROWS][COLS],int rows, int cols,int m);
//打印函数
void Printboard(int board[ROWS][COLS], int row, int col);
//设置雷的函数
void Setmine(int board[ROWS][COLS], int row, int col);
//加载图片的函数
void loadimages();
//放图片的函数
void putimages(int board[ROWS][COLS], int row, int col);
//加密的函数
void password(int board[ROWS][COLS], int rows, int cols);
//鼠标信息的函数
void mouseimages(int board[ROWS][COLS]);
//判断输赢的函数
void judge(int mine[ROWS][COLS]);

正文

说明:我们先在实现的逻辑的源文件实现函数的功能,,最后我们再把功能汇总一下,这样逻辑比较顺。

一.雷盘信息的存储

1.设置雷盘11*11与初始化

作用:防止在排查雷时,数组越界。
说明:数组已经初始化,下面只是初始化雷盘的函数。
代码:

//这里四个参数的功能与基础篇相同
void Initboard(int board[ROWS][COLS], int rows, int cols, int m)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = 0;
		}
	}
}

2.放置雷

注意:
1.在.cpp后缀的文件中引用time函数时只能用time.h
2.在.cpp后缀的文件中引用srand函数和rand函数时只能用stdlib.h
说明:下面的代码的随机数起点已经在项目开始时建立,下面是设置雷的函数
代码:

void Setmine(int board[ROWS][COLS], int row, int col)
{
	int count = 0;
	while (count<MINE)
	{
		//随机坐标
		//横坐标,行
		int x = rand() % 9 + 1;//1——9
		//纵坐标,列
		int y = rand() % 9 + 1;
		if (board[x][y] == 0)
		{
			board[x][y] = -1;
			//在雷的周围加1,下面的放置雷周围的信息会有说明
			mine_information(board,x,y);
			count++;
		}
	}
}

3.放置雷周围的信息

图解:
在这里插入图片描述
注意:是在每次放一个雷后在周围加一,到这就可理解上面的代码了。
代码:

//放置static的目的是将外部链接属性改为内部链接属性,使此函数只能在实现逻辑的
//源文件中进行使用,更为安全。
static void mine_information(int board[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{

			if (i != x || j != y)//i与j不能同时为x和y
			{
				board[i][j] += 1;
			}
		}
	}
}

二.图形化界面

1.创建与初始化窗口

在main函数里面创建

int main()
{
	//创建一个9*50(宽),10*50(高),并且展示控制台窗口。
    initgraph((ROW) * SIZE, (COL+1) * SIZE, SHOWCONSOLE);
     //游戏的逻辑省去了,最后接上去
    getchar();
	closegraph();
    retuen 0;
}

2.加载图片

我们先把所需的图片加载之后才能进行使用,否则使用不了。
代码:

void loadimages()
{
	char fill[50] = "";
	int i = 0;
	for (i = 0; i < 12; i++)
	{
		sprintf(fill, "./扫雷/%d.jpg", i);
		loadimage(&image[i], fill,SIZE,SIZE);//设置50*50的大小
	}
	loadimage(&image[12], "./扫雷/12.jpg",SIZE*ROW,SIZE);//设置450*50的大小
}

3.放入图片

如何放呢?
前九行放雷信息的图片,最后一行放雷的个数的图片。
尤其注意:行和列与横坐标和纵坐标的关系,将行和列转化成界面的纵坐标和横坐标,再放进去。
在这里插入图片描述
代码:

void putimages(int board[ROWS][COLS],int row, int col)
{
	putimage(0, ROW * SIZE, &image[12]);
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
		//行为纵坐标
		for (j = 1; j <= col; j++)
		{
			//列为横坐标
			int n = board[i][j];//方便书写
			if (n == -1)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[9]);
			}
			else if (n >= 0 && n <= 8)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[n]);
			}
			//加密之后
			else if (n >= 19 && n <= 28)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[10]);//加密格子
			}
			else if (n >= 39 && n <= 48)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[11]);//标记
			}
		}
	}
}

4.加密格子

 将格子加上20之后就将格子加密了,当你点击时,这个格子所对应的数组的元素减去二十,再把原来的图片(这个位置存储的信息)放上去即可。
代码:

void password(int board[ROWS][COLS], int rows, int cols)
{
	//将所有格子都加上20
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] +=20;
		}
	}
}

三.鼠标点击

1.具体步骤

1.获取鼠标信息
2.将鼠标信息转化为数组下标
3.将鼠标信息进行分发
4.将情况分类进行解密与加密

2.代码实现

void mouseimages(int board[ROWS][COLS])
{
	if (MouseHit())
	{
		MOUSEMSG msg = GetMouseMsg();
		//列对应横坐标
		int col = msg.x / SIZE + 1;
		//行对应纵坐标
		int row = msg.y / SIZE + 1;
		int m = board[row][col];
		switch (msg.uMsg)//分发鼠标信息
		{
		case WM_LBUTTONDOWN:
			if (m >= 19 && m <= 48)//只有在这个范围才执行操作,否则会将这个格子弄没
			{
				board[row][col] -= 20;
			}
			//递归展开省略了
			
			break;
		case WM_RBUTTONDOWN:
			if (m >= 19 && m <= 28)
			{
				board[row][col] += 20;
			}
			break;
		}
	}
}

四.递归展开

1.具体步骤

1.在为空格时,将周围的格子打开,且不为自身
2.打开的格子是加密的
3.加密的格子不是雷
4.如果为空继续调用

2.代码实现

static void  Nullarround(int board[ROWS][COLS], int x, int y)
{
	if (board[x][y] == 0)//判断是否为空白
	{

		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{

				if (i != x || j != y)
				{
					if (board[i][j] != 19 && board[i][j] >= 9)
					//比没加密大就是加密的格子,且加密的格子不为雷
					{

						board[i][j] -= 20;
						Nullarround(board, i, j);//继续调用,为空格继续展开。
					}
				}
			}
		}
	}
}

五.判断输赢

1.具体步骤

 每次打开后都要判断一下,递归展开中也是。说明,这里如果输了或赢了,我们设置一个标志为flag为0,如果每次点击不为雷,那就加一,输了就设置为-1。
在赢或输之后我们就提示一下,是否要继续再开一把。

2.代码实现

void judge(int mine[ROWS][COLS])
{
	if (flag == -1)
	{
		//失败
		int isok = MessageBox(GetHWnd(), "很遗憾!你输了!是否再来一把?", "提示", MB_OKCANCEL);
		if (isok == IDOK)
		{
			flag = 0;
			//将游戏初始化,意思就是再开一把
			Initboard(mine, ROWS, ROWS, 0);
			Setmine(mine, ROW, COL);
			password(mine, ROWS, ROWS);
		}
		else
		{
			exit(5201314);//参数为随机数,退出程序。
		}
	}
	if (flag == ROW * COL - MINE)
	{
		//胜利

		int isok = MessageBox(GetHWnd(), "很高兴!你赢了!是否再来一把?", "提示", MB_OKCANCEL);
		if (isok == IDOK)
		{
			flag = 0;
			//将游戏初始化
			Initboard(mine, ROWS, ROWS, 0);
			Setmine(mine, ROW, COL);
			password(mine, ROWS, ROWS);
		}
		else
		{
			exit(5201314);//随机数
		}
	}
}

六.设置音乐与点击音效

入门EasyX的教程里面都有细说,如果不明白可以看一下教程,就在文章开头,点击即可进入。

1.播放音乐

代码:

    //初始化界面时要打开音乐
	mciSendString("open ./扫雷/卡农.mp3", 0, 0, 0);
	//打开音乐后要播放音乐
	mciSendString("play ./扫雷/卡农.mp3 repeat", 0, 0, 0);
	//游戏结束后要关闭音乐
	mciSendString("close ./扫雷/卡农.mp3 repeat", 0, 0, 0);

2.播放音效

说明:每次点击要播放,播放完或者下一次播放时关闭,要不然只会响一次。

		mciSendString("close ./扫雷/点击声音.wav", 0, 0, 0);
		mciSendString("open ./扫雷/点击声音.wav", 0, 0, 0);
		mciSendString("play ./扫雷/点击声音.wav", 0, 0, 0);

七.总结代码

1.运行逻辑的代码:mine.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "mine.h"
int flag = 0;
int main()
{
	initgraph((ROW) * SIZE, (COL+1) * SIZE, SHOWCONSOLE);

	setbkcolor(WHITE);
	cleardevice();
	srand((unsigned int)time(NULL));
	int mine[ROWS][COLS] = { 0 };
	Initboard(mine, ROWS, ROWS, 0);
	Setmine(mine,ROW,COL);
	password(mine, ROWS, ROWS);
	Printboard(mine, ROW, COL);
	mciSendString("open ./扫雷/卡农.mp3", 0, 0, 0);
	mciSendString("play ./扫雷/卡农.mp3 repeat", 0, 0, 0);
	loadimages();
	//每次放图片,鼠标点击,与判断是否结束是循环。
	while (1)
	{
		BeginBatchDraw();//批量开始绘图使图形更加的稳定
		putimages(mine, ROW, COL);
		FlushBatchDraw();//批量结束绘图使图形更加的稳定
		mouseimages(mine);
		judge(mine);
		//Printboard(mine, ROW, COL);
	}
	int ret = getchar();
	closegraph();
}

2.头文件代码:mine.h

#define _CRT_SECURE_NO_WARNINGS 1
#define ROWS 11
#define COLS 11
#define COL (ROWS-2)
#define ROW (COLS-2)
#define MINE 10
#include <graphics.h>//要包含在SIZE的前面,防止与里面的SIZE冲突。
#define SIZE 50
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
void Initboard(int board[ROWS][COLS],int rows, int cols,int m);
void Printboard(int board[ROWS][COLS], int row, int col);
void Setmine(int board[ROWS][COLS], int row, int col);
void loadimages();
void putimages(int board[ROWS][COLS], int row, int col);
void password(int board[ROWS][COLS], int rows, int cols);
void mouseimages(int board[ROWS][COLS]);
void judge(int mine[ROWS][COLS]);

3.实现逻辑的代码:源.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "mine.h"
IMAGE image[13];
extern int flag;
static void  Nullarround(int board[ROWS][COLS], int x, int y)
{
	if (board[x][y] == 0)//判断是否为空白
	{

		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{

				if (i != x || j != y)
				{
					if (board[i][j] != 19 && board[i][j] >= 9)//比没加密大就是加密的格子
					{

						board[i][j] -= 20;
						flag++;
						Nullarround(board, i, j);//继续调用,为空格继续展开。
					}
				}
			}
		}
	}
}
static void mine_information(int board[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{

  			if ((i != x || j !=y)&&board[i][j]!=-1)
			{
				board[i][j] += 1;
			}
		}
	}
}
void Initboard(int board[ROWS][COLS], int rows, int cols, int m)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = 0;
		}
	}
}
void Printboard(int board[ROWS][COLS], int row, int col)
{
	system("cls");
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			printf("%2d ",board[i][j]);
		}
		printf("\n");
	}
}
void Setmine(int board[ROWS][COLS], int row, int col)
{
	int count = MINE;
	while (count)
	{
		//随机坐标
		//横坐标,行
		int x = rand() % 9 + 1;//1——9
		//纵坐标,列
		int y = rand() % 9 + 1;
		if (board[x][y] == 0)
		{
			board[x][y] = -1;
			//在雷的周围加1
 			mine_information(board,x,y);
			count--;
		} 
	}
}
void loadimages()
{
	char fill[50] = "";
	int i = 0;
	for (i = 0; i < 12; i++)
	{
		sprintf(fill, "./扫雷/%d.jpg", i);
		loadimage(&image[i], fill,SIZE,SIZE);
	}
	loadimage(&image[12], "./扫雷/12.png",SIZE*ROW,SIZE);
}
void putimages(int board[ROWS][COLS],int row, int col)
{
	putimage(0, ROW * SIZE, &image[12]);
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
		//行为纵坐标
		for (j = 1; j <= col; j++)
		{
			//列为横坐标
			int n = board[i][j];
			if (n == -1)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[9]);
			}
			else if (n >= 0 && n <= 8)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[n]);
			}
			//加密之后
			else if (n >= 19 && n <= 28)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[10]);//加密格子
			}
			else if (n >= 39 && n <= 48)
			{
				putimage((j - 1) * SIZE, (i - 1) * SIZE, &image[11]);//标记
			}
		}
	}
}
//加密
void password(int board[ROWS][COLS], int rows, int cols)
{
	//将所有格子都加上20
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] +=20;
		}
	}
}
void mouseimages(int board[ROWS][COLS])
{
	if (MouseHit())
	{
		MOUSEMSG msg = GetMouseMsg();
		//列对应横坐标
		int col = msg.x / SIZE + 1;
		//行对应纵坐标
		int row = msg.y / SIZE + 1;
		int m = board[row][col];
		switch (msg.uMsg)//分发鼠标信息
		{
		case WM_LBUTTONDOWN:
			if (m >= 19 && m <= 48)
			{
				mciSendString("close ./扫雷/点击声音.wav", 0, 0, 0);
				mciSendString("open ./扫雷/点击声音.wav", 0, 0, 0);
				mciSendString("play ./扫雷/点击声音.wav", 0, 0, 0);
				board[row][col] -= 20;
				if (board[row][col] != -1)
				{
					flag++;
				}
				else
				{
					flag = -1;
					break;
				}
				
			}
			//递归展开
			Nullarround(board, row, col);
			
			break;
		case WM_RBUTTONDOWN:
			if (m >= 19 && m <= 28)
			{
				board[row][col] += 20;
			}
			break;
		}
	}
}
void judge(int mine[ROWS][COLS])
{
	if (flag == -1)
	{
		//失败
		int isok = MessageBox(GetHWnd(), "很遗憾!你输了!是否再来一把?", "提示", MB_OKCANCEL);
		if (isok == IDOK)
		{
			flag = 0;
			//将游戏初始化
			Initboard(mine, ROWS, ROWS, 0);
			Setmine(mine, ROW, COL);
			password(mine, ROWS, ROWS);
		}
		else
		{
			mciSendString("close ./扫雷/卡农.mp3", 0, 0, 0);

			exit(5201314);//随机数
		}

	}
	if (flag == ROW * COL - MINE)
	{
		//胜利

		int isok = MessageBox(GetHWnd(), "很高兴!你赢了!是否再来一把?", "提示", MB_OKCANCEL);
		if (isok == IDOK)
		{
			flag = 0;
			//将游戏初始化
			Initboard(mine, ROWS, ROWS, 0);
			Setmine(mine, ROW, COL);
			password(mine, ROWS, ROWS);
		}
		else
		{
			mciSendString("close ./扫雷/卡农.mp3", 0, 0, 0);
			exit(5201314);//随机数
		}

	}
}

效果:
在这里插入图片描述

总结

如果能认真看到这里,我坚信你能收获很多很多!也希望这篇文章能帮助到你,如果觉得不错,请点击一下不要钱的赞,如果有误请温柔的指出,在这里感谢大家了!

  • 38
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值