0
点赞
收藏
分享

微信扫一扫

C++实现扫雷

木匠0819 2022-03-12 阅读 51
c++

前言

要实现扫雷,我们可以将其分为这样几个不同的部分来一一实现:

  1. 引入游戏所需文件
  2. 游戏界面的初始化
  3. 打印游戏界面
  4. 游戏机制的实现
  5. 玩家与游戏之间的互动
  6. 游戏运行以及胜负判定

一、引入游戏所需文件

在扫雷中我们需要用到EasyX绘图库中包含的部分功能

附上官网链接:https://easyx.cn/

下面列出需要包含的头文件:

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<graphics.h>
#include<windows.h>
#include<Mmsystem.h>
#pragma comment(lib,"winmm.lib")

初始化需要用到的图片以及声音资源(我的资源是从别的博主那里下载来的,就是一二三四五和红旗的等,故不附上):

int o = 0;
int init[row + 2][col + 2];
IMAGE img[11];
void load()
{
	loadimage(&img[0], L"0.jpg", size, size);
	loadimage(&img[1], L"1.jpg", size, size);
	loadimage(&img[2], L"2.jpg", size, size);
	loadimage(&img[3], L"3.jpg", size, size);
	loadimage(&img[4], L"4.jpg", size, size);
	loadimage(&img[5], L"5.jpg", size, size);
	loadimage(&img[6], L"6.jpg", size, size);
	loadimage(&img[7], L"7.jpg", size, size);
	loadimage(&img[8], L"8.jpg", size, size);
	loadimage(&img[9], L"9.jpg", size * row, size * col);
	loadimage(&img[10], L"10.jpg", size * row, size * col);
}

二、游戏界面初始化

1.宏定义游戏行列数,图片的尺寸以及雷的数量

#define row  10										//定义行数
#define col  10										//定义列数
#define boom 10										//定义炸弹数
#define size 50										//定义图片长宽

2.InitGame函数,实现游戏初始化功能

int init[row + 2][col + 2];
void InitGame()//初始化游戏、
{
	for (int i = 0; i < row + 2; i++) //设置缓冲区,并使缓冲区内全为0,缓冲区是指在游戏界面外一圈区域,不设置缓冲区的话会有一些麻烦
	{
		for (int j = 0; j < col + 2; j++)
		{
			init[i][j] = 0;
		}
	}

	srand((int)time(0));

	int a = boom;//初始化雷的数量
	while (a > 0)//设置循环,初始化雷的位置,雷的位置是随机出现的
	{
		int row1 = rand() % row + 1;  //缓冲区不可出现雷,rand%10是指[0,10),加一使得雷在界面内出现
		int col1 = rand() % col + 1;

		if (init[row1][col1] == 0)
		{
			init[row1][col1] = 6;//将雷所在数组元素设为6
			a--;
		}
	}

	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (init[i][j] == 0)
			{
				for (int m = i - 1; m <= i + 1; m++)//设置两个循环,获取某一位置周围雷的数量
				{
					for (int n = j - 1; n <= j + 1; n++)
					{
						if (init[m][n] == 6)
						{
							init[i][j]++;
						}

					}
				}
			}
		}
	}

	for (int i = 1; i <= row ; i++)//把数组的每个元素都+100,通过加载图片成为初始状态
	{
		for (int j = 1; j <= col ; j++)
		{
			init[i][j] = init[i][j] + 100;
		}
	}
}

三、PaintGame函数打印游戏界面

当二维数组元素值处于不同区间时,我们打印不同的图片,就可以实现每个格子不同的状态,注意一定要控制好值和范围的关系,否则可能会出现插了棋子却又可以左键点击出炸弹等尴尬的Bug,我所定义的范围肯定没有问题,可以放心用

void PaintGame()
{
	for (int i = 1; i <= row ; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (init[i][j] >= 0 && init[i][j] <= 6)
			{
				putimage((i - 1) * size, (j - 1) * size, &img[init[i][j]]);
			}
			if (init[i][j] >= 100 && init[i][j] <= 106 || init[i][j] >= 300 && init[i][j] % 200 > 10)//初始时为100到106,大于300以后有插旗与取消状态,除以200取余大于10,则为插旗后取消旗子
			{
				putimage((i - 1) * size, (j - 1) * size, &img[8]);
			}
			if (init[i][j] >= 200 && init[i][j] <= 206 || init[i][j] >= 300 && init[i][j] % 200 < 10)//同上,小于10则为插旗
			{
				putimage((i - 1) * size, (j - 1) * size, &img[7]);
			}
			if (init[i][j] == -101)
			{
				putimage((i - 1) * size, (j - 1) * size, &img[0]);
			}
		}
	}
}

四、Expand函数实现游戏机制

玩扫雷时我们发现,当点击目标周围没有炸弹时,我们点击一下会点开一片,可以称之为游戏独特的机制,用Expand函数来实现

void Expand(int m, int n)
{
	if (m >= 1 && n >= 1 && m <= row && n <= col)//设置好,小心超出范围
	{
		for (int i = m - 1; i <= m + 1; i++)
		{
			for (int j = n - 1; j <= n + 1; j++)
			{
				if (init[i][j] % 100 == 0)
				{
					init[i][j] = -101;		//换一个新的独特的值,避免套娃造成bug
					Expand(i, j);			//逢0递归
				}
				if (init[i][j] % 100 >= 1 && init[i][j] % 100 <= 5)
				{
					init[i][j] = init[i][j] % 100;
				}
				else
				{
					init[i][j] = init[i][j];
				}
			}
		}
	}
	else
	{
		init[m][n] = init[m][n];
	}
}

五、PlayGame函数实现玩家与游戏的互动

总体来看很好理解,通过鼠标的点击,来改变点击位置对应数组元素的值,点击方式的不同,即左键点击和右键点击,值的改变也不同,一定要设置好改变的值,否则会出现插了棋子却又可以左键点击等Bug

void PlayGame()
{
	MOUSEMSG msg = { 0 };
	msg = GetMouseMsg();
	int m, n;
	m = msg.x / size + 1;	//加一使该函数的操作作用于init[1][1]及之后的元素,因为打印时外圈的全为0的行列不打印
	n = msg.y / size + 1;
	if (msg.uMsg == WM_LBUTTONDOWN)
	{
		PlaySound(TEXT("b.wav"), NULL, SND_FILENAME | SND_ASYNC);
		if ( init[m][n] >= 100 && init[m][n] <= 106 || init[m][n] >= 300 && init[m][n] % 200 > 10)//当前格子赋值为100到106(初始),或者300以上并且除以200余数小于10时,因为旗子状态不能变为数
		{
			init[m][n] = init[m][n] % 100;//取除以10的余数,即可得原值

			if (init[m][n] == 0)//当选中位置元素为0时,扩散
			{
				Expand(m, n);
			}
		}
		else
		{
			init[m][n] = init[m][n];
		}
	}

	if (msg.uMsg == WM_RBUTTONDOWN)
	{
		PlaySound(TEXT("b.wav"), NULL, SND_FILENAME | SND_ASYNC);
		if (init[m][n] > 6)//因为所有值都大于0,所以小于等于6时是12345和炸
		{
			init[m][n] = init[m][n] + 100;
		}
		else
		{
			init[m][n] = init[m][n];
		}
	}
}

六、JudgeMent函数实现游戏运行以及胜负的判定

void JudgeMent()
{
	while (1)
	{
		PaintGame();
		PlayGame();

		int boomnum = 0;//初始化踩中炸弹数量
		int boomleft = boom;//初始化剩余炸弹数量

		for (int i = 1; i <= row; i++)
		{
			for (int j = 1; j <= col; j++)
			{
				if (init[i][j] == 6)
				{
					boomnum++;//踩中炸弹
				}
				if (init[i][j] == 206 || init[i][j] >= 300 && init[i][j] % 200 == 6)
				{
					boomleft--;
				}
			}
		}
		if (boomnum == 1)//踩中炸弹即为失败
		{
			PlaySound(TEXT("a.wav"), NULL, SND_FILENAME | SND_ASYNC);
			putimage(0, 0, &img[9]);
			break;
		}
		if (boomleft == 0)
		{
			PlaySound(TEXT("c.wav"), NULL, SND_FILENAME | SND_ASYNC);
			putimage(0, 0, &img[10]);
			break;
		}
	}
}

七、主函数

int main()
{
	load();
	initgraph(row * size, col * size);
	InitGame();
	JudgeMent();
	getchar();
	return 0;
}

总结

以上即为扫雷小游戏的全部实现过程,难点在于选择的值范围对格子状态的影响以及递归函数的构造,总体难度不大。

举报

相关推荐

0 条评论