欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言实现扫雷小游戏

程序员文章站 2024-03-18 11:04:52
...

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]++;
						}
					}
				}
			}
		}
	}

那么对于一些特殊的位置该怎么处理呢?比如我们在玩扫雷的时候,对于某一个没有被翻起来的位置,我们鼠标左键单击之后,可能是空白,也可能是数字,那么这个数字是怎么得到的呢?我们讲一下关于扫雷的游戏规则:
如图:
C语言实现扫雷小游戏
当我们点击某一个没有被点击过的位置的时候,对于该位置的数字的求法,是以该位置为中心,周围形成的一个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;
}

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

相关标签: C