C语言实现扫雷小游戏

C语言实现扫雷

之前闲来无趣,就自己模仿网上视频写了一个扫雷玩玩,这里记录分享一下,主要是用C语言进行,然后用了一点MFC的图形化界面窗口。
先分析一下,对于扫雷,我们应该要实现的是哪些东西?
首先,对于游戏地图的处理上,使用的是二维数组,然后对整个二维数组进行赋初值为0;
然后对于雷,我们用-1来进行表示,想要随机生成雷,那就直接用随机生成数的函数进行实现,用便利的方式把雷放到里面去就行。(先分小块进行讲,最后贴全部代码)

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<graphics.h>
using namespace std;

#define ROW 33 //列
#define COL 16 //行
#define NUM 99 //雷的个数
#define SIZE 30//图片的大小;
int map[ROW + 2][COL + 2];//地图

void Initmap()
{
	int n = 0;
	srand((unsigned int)time(NULL));//随机数种子;
	for (int i = 0; i < ROW + 2; i++)
	{
		for (int j = 0; j < COL + 2; j++)
		{
			map[i][j] = 0;
		}
	}
	while (n < NUM)//放置炸弹
	{
		int r = rand() % ROW + 1;//用生成随机数函数实现生成随机数
		int c = rand() % COL + 1;
		if (map[r][c] == 0)//放置雷;
		{
			map[r][c] = -1;
			n++;
		}
	}
	for (int i = 1; i <=ROW; i++)//遍历某个位置的九宫格,将该位置的数变成九宫格中雷的总数;
	{
		for (int j = 1; j <=COL; j++)
		{
			if (map[i][j] != -1)
			{
				for (int m = i - 1; m <= i + 1; m++)
				{
					for (int n = j - 1; n <= j + 1; n++)
					{
						if (map[m][n] == -1)
						{
							map[i][j]++;
						}
					}
				}
			}
		}
	}

那么对于一些特殊的位置该怎么处理呢?比如我们在玩扫雷的时候,对于某一个没有被翻起来的位置,我们鼠标左键单击之后,可能是空白,也可能是数字,那么这个数字是怎么得到的呢?我们讲一下关于扫雷的游戏规则:
如图:
在这里插入图片描述
当我们点击某一个没有被点击过的位置的时候,对于该位置的数字的求法,是以该位置为中心,周围形成的一个3x3的矩阵,然后便利除了他自己本身以外的格子,每遇到一个类,该位置的数字就加1,所以对应的数字就是这么得到的。但是在进行程序处理的时候,我们会发现一个问题,那如果我在边缘进行处理呢?也就是整个地图的最右上角或者左上角进行处理呢?不是越界了嘛?所以,我们在二维数组初始化的时候,将整个地图应该设置为(n+1)(m+1)的格式,后面进行地图更新和打印的时候,在nm内进行操作就行了;

for (int i = 1; i <=ROW; i++)//遍历某个位置的九宫格,将该位置的数变成九宫格中雷的总数;
	{
		for (int j = 1; j <=COL; j++)
		{
			if (map[i][j] != -1)
			{
				for (int m = i - 1; m <= i + 1; m++)
				{
					for (int n = j - 1; n <= j + 1; n++)
					{
						if (map[m][n] == -1)
						{
							map[i][j]++;
						}
					}
				}
			}
		}

然后我们再考虑一点,按照目前我们所对二位数组初始化的数据中,只有-1和0这两个数据,但是我们想要在鼠标点击之后显示1-8和小红旗(鼠标右击之后插红旗的动作)还有炸弹该如何处理呢?对于数字其实还好,我们可以直接进行判断,但是又有一个问题,在我鼠标没有任何动作的时候,地图所展现的每一个小方格都是没有被翻起的,就是空白的,那空白图片该怎么显示呢?还有小红旗该如何显示?那么我们可以在数据上进行一些操作,我这里是把所有的都加上20,使其范围变到19-28,然后凡是在这个范围内的都输出空白图片,也就是没有被翻起来,只要某一处被鼠标进行动作,比如被鼠标进行左击,假设是在进行翻起动作,就把当前该位置的值-20,得到的数字进行判断,是-1就显示雷(结束游戏),是数字就显示数字。再来说一下红旗的解决,同样的,当数据进行操作之后,我仅仅是对鼠标的左击进行了监控,而右击还没有,所以当鼠标在某个位置进行右击的时候,我再把这个位置的书加上30,让其变得更大,然后如果像取消小红旗,则就判断一下,如果该位置的值>50,那么就把这个位置的值-50即可。

//-------------------------------------------------------------------------------------------
for (int i = 1; i <= ROW; i++)//对每个数据进行加20,对数据进行优化,方便后面对空白,红旗的处理;
	{
		for (int j = 1; j <= COL; j++)
		{
			map[i][j] += 20;
		}
	}
	//-----------------------------------------------------------------------------------------
	int PlayGame()//对鼠标动作进行监控
{
	int r ; int c ;
	MOUSEMSG msg = { 0 };//定义一个鼠标消息;
	while(1)
	{ 
	msg = GetMouseMsg();
	switch (msg.uMsg)
	{
	case WM_LBUTTONDOWN://翻开空白图片;
		r = msg.x / SIZE + 1;
		c = msg.y / SIZE + 1;
		if (map[r][c] >= 19 && map[r][c] <= 28)
		{
			if (map[r][c] == 20)
			{
				OpenZ(r, c);
			}
			else
			{
				map[r][c] -= 20;
				counts++;
			}
		}
		return map[r][c];
		break;
	case WM_RBUTTONDOWN://标志小红旗
		r = msg.x / SIZE + 1;
		c = msg.y / SIZE + 1;
		if (map[r][c] >= 19 && map[r][c] <= 28)//放置小红旗
		{
			map[r][c] += 50;
		}
		else if(map[r][c] > 30)//取消放置的小红旗
		{
			map[r][c] -= 50;
		}
		return map[r][c];
		break;
		}
	}
}

最后还有一个小问题,就是在游玩扫雷的时候,你会发现有时候你点击了某一处,可能显示的不仅仅是你那个3x3的小方格的所有内容,可能还显示了其他的一些,这个就涉及到如果你点击的位置是空白,也就是周围一个雷都没有。那么也就是说,如果你点击到了一个周围一个类都没有的矩阵中心,然后就去遍历这个矩阵中其他小方格,看是否有也为0的,然后再以其为中心遍历其自身的矩阵。很明显,递归。

void OpenZ(int r,int c)//递归实现输出对应能输出的空位
{
	map[r][c] -= 20;
	counts++;
	for (int m = r - 1; m <= r + 1; m++)
	{
		for (int n = c - 1; n <= c + 1; n++)
		{
			if (m >= 1 && m <= ROW && n >= 1 && n <= COL)//保证在游戏区
			{
				if (map[m][n] >=19 && map[m][n] <=28)//保证遍历的是空白的,也就是没有被翻起的
				{
					if (map[m][n] == 20)//如果遍历到的位置是空白的话,进行递归
					{
						OpenZ(m, n);
					}
					else//如果不是,那么就把他翻起来,然后把监控翻起来数值的变量加1,这里不用考虑雷的问题,因为本身遍历的这个矩阵中心是0,所以这个矩阵中没有雷
					{
						map[m][n] -= 20;
						counts++;
					}
				}
			}
		}
	}
}

最后我把整体代码贴一下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<graphics.h>
using namespace std;

#define ROW 33 //列
#define COL 16 //行
#define NUM 99 //雷的个数
#define SIZE 30//图片的大小;
int map[ROW + 2][COL + 2];//地图
int counts = 0;//判断游戏胜利的条件;

IMAGE img[12];//存放对应数字的图片和对应雷和红旗的图片;

void OpenZ(int r,int c)//递归实现输出对应能输出的空位
{
	map[r][c] -= 20;
	counts++;
	for (int m = r - 1; m <= r + 1; m++)
	{
		for (int n = c - 1; n <= c + 1; n++)
		{
			if (m >= 1 && m <= ROW && n >= 1 && n <= COL)//保证在游戏区
			{
				if (map[m][n] >=19 && map[m][n] <=28)//保证遍历的是空白的,也就是没有被翻起的
				{
					if (map[m][n] == 20)//如果遍历到的位置是空白的话,进行递归
					{
						OpenZ(m, n);
					}
					else//如果不是,那么就把他翻起来,然后把监控翻起来数值的变量加1,这里不用考虑雷的问题,因为本身遍历的这个矩阵中心是0,所以这个矩阵中没有雷
					{
						map[m][n] -= 20;
						counts++;
					}
				}
			}
		}
	}
}
void Initmap()
{
	int n = 0;
	srand((unsigned int)time(NULL));//随机数种子;
	for (int i = 0; i < ROW + 2; i++)
	{
		for (int j = 0; j < COL + 2; j++)
		{
			map[i][j] = 0;
		}
	}
	while (n < NUM)//放置炸弹
	{
		int r = rand() % ROW + 1;
		int c = rand() % COL + 1;
		if (map[r][c] == 0)//放置雷;
		{
			map[r][c] = -1;
			n++;
		}
	}
	
	for (int i = 1; i <=ROW; i++)//遍历某个位置的九宫格,将该位置的数变成九宫格中雷的总数;
	{
		for (int j = 1; j <=COL; j++)
		{
			if (map[i][j] != -1)
			{
				for (int m = i - 1; m <= i + 1; m++)
				{
					for (int n = j - 1; n <= j + 1; n++)
					{
						if (map[m][n] == -1)
						{
							map[i][j]++;
						}
					}
				}
			}
		}
	}

	for (int i = 1; i <= ROW; i++)//对每个数据进行加20,对数据进行优化,方便后面对雷,空白,红旗的处理;
	{
		for (int j = 1; j <= COL; j++)
		{
			map[i][j] += 20;
		}
	}

}
int PlayGame()//对鼠标动作进行监控
{
	int r ; int c ;
	MOUSEMSG msg = { 0 };//定义一个鼠标消息;
	while(1)
	{ 
	msg = GetMouseMsg();
	switch (msg.uMsg)
	{
	case WM_LBUTTONDOWN://翻开空白图片;
		r = msg.x / SIZE + 1;
		c = msg.y / SIZE + 1;
		if (map[r][c] >= 19 && map[r][c] <= 28)
		{
			if (map[r][c] == 20)
			{
				OpenZ(r, c);
			}
			else
			{
				map[r][c] -= 20;
				counts++;
			}
		}
		return map[r][c];
		break;
	case WM_RBUTTONDOWN://标志小红旗
		r = msg.x / SIZE + 1;
		c = msg.y / SIZE + 1;
		if (map[r][c] >= 19 && map[r][c] <= 28)//放置小红旗
		{
			map[r][c] += 50;
		}
		else if(map[r][c] > 30)//取消放置的小红旗
		{
			map[r][c] -= 50;
		}
		return map[r][c];
		break;
		}
	}
}
void Showmap()
{
	for (int i = 1; i <= ROW; i++)
	{
		for (int j = 1; j <= COL; j++)
		{
			printf("%2d ", map[i][j]);
			if (map[i][j] == -1)
			{
				putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[9]);//打印雷的图片
			}
			else if (map[i][j] >= 0 && map[i][j] <= 8)
			{
				putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[map[i][j]]);//打印数字图片
			}
			else if (map[i][j] >= 19 && map[i][j] <= 28)
			{
				putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[10]);//打印空白图片
			}
			else if (map[i][j] > 30)
			{
				putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[11]);//打印小红旗;
			}
		}
		cout << endl;
	}
}
int main()
{
	HWND hwnd = initgraph(ROW*SIZE, COL*SIZE);
	loadimage(&img[0], "0.jpg", SIZE, SIZE);
	loadimage(&img[1], "1.jpg", SIZE, SIZE);
	loadimage(&img[2], "2.jpg", SIZE, SIZE);
	loadimage(&img[3], "3.jpg", SIZE, SIZE);
	loadimage(&img[4], "4.jpg", SIZE, SIZE);
	loadimage(&img[5], "5.jpg", SIZE, SIZE);
	loadimage(&img[6], "6.jpg", SIZE, SIZE);
	loadimage(&img[7], "7.jpg", SIZE, SIZE);
	loadimage(&img[8], "8.jpg", SIZE, SIZE);
	loadimage(&img[9], "9.jpg", SIZE, SIZE);
	loadimage(&img[10], "10.jpg", SIZE, SIZE);
	loadimage(&img[11], "11.jpg", SIZE, SIZE);
STAR:	Initmap();
	while (1)
	{
		Showmap();
		if (PlayGame() == -1)
		{
			Showmap();
			int result=MessageBox(hwnd, "踩到雷啦!游戏失败!是否需要重新开始呢?", "", MB_YESNO);
			if (result == IDYES)
			{
				MessageBox(hwnd, "您选择了重新开始!", "", MB_OK);
				counts = 0;
				goto STAR;
				break;
			}
			else
			{
				MessageBox(hwnd, "那么感谢您的游玩!", "", MB_OK);
			}
			break;
		}
		if (ROW*COL - NUM == counts)
		{
			int result=MessageBox(hwnd, "很棒哦!恭喜你找出了所有雷!是否需要再来一局呢?", "", MB_YESNO);
			if (result == IDYES)
			{
				MessageBox(hwnd, "那么在此预祝你本次游玩愉快!", "", MB_OK);
				counts = 0;
				goto STAR;
				break;
			}
			else
			{
				MessageBox(hwnd, "那么感谢您的游玩!", "", MB_OK);
			}
			break;
		}
	}
	closegraph();
	system("pause");
	return 0;
}

实现的样子:
在这里插入图片描述
注:关于图片,图片好似只能用.jpg格式,然后要存放到项目目录下(就是放.cpp的里面),不然显示不出来。还有游戏胜利的判断是用一个变量记录所有翻起来的格子数,当变量的值=总的数-雷的数,那就是胜利了。

  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值