扫雷(基于 EasyX 图形库)

EasyX 基础第一部分

EasyX 基础第二部分

目录

一、游戏初始化(Game Initialization)

二、加密(Encrypt)

三、加载图片(Load Image)

四、 绘制界面(Draw the Interface)

五、鼠标控制(Mouse Control)

六、判断游戏是否结束(Is Game Over)

七、源代码


 


一、游戏初始化(Game Initialization)

#include <stdlib.h>
#include <time.h>  // 用时间戳作为随机数种子

#define ROW 10
#define COL 10
#define MINE_COUNT 10

int board[ROW][COL] = { 0 };  // 创建一个二维数组来存储棋盘上每个格子的信息

void GameInit()
{
	int i = 0;  // 控制行
	int j = 0;  // 控制列 
	// 1. 特别是当游戏重新开始时,需要将棋盘上的每个格子的信息重新初始化为 0
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = 0;
		}
	}
	// 2. 随机生成 MINE_COUNT 个雷
	for (i = 0; i < MINE_COUNT; i++)
	{
		while (1)
		{
			int row = rand() % ROW;  // row: 0 ~ ROW - 1
			int col = rand() % COL;  // col: 0 ~ COL - 1
			if (board[row][col] == 0)  // 避免在同一个格子上埋雷
			{
				board[row][col] = -1;  // -1 表示这个格子上埋有雷
				break;
			}
		}
	}
	// 3. 对以雷为中心九宫格进行 +1 操作,埋雷的格子除外
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			// 第一步:找到埋雷的格子
			if (board[i][j] == -1)
			{
				// 第二步:遍历以雷为中心的九宫格
				for (int row = i - 1; row <= i + 1; row++)
				{
					for (int col = j - 1; col <= j + 1; col++)
					{
						// 第三步:检查坐标是否合法,即是否越界;然后检查是否有雷
						if ((row >= 0 && row < ROW) && (col >= 0 && col < COL) && board[row][col] != -1)
						{
							board[row][col] += 1;
						}
					}
				}
			}
		}
	}
}
  1. 我们创建的二维数组是全局的,即便不初始化,也默认初始化为全 0。而在 GameInit 函数中,我们首先将棋盘上的每个格子的信息初始化为 0,其主要原因是当游戏重新开始时,需要重新埋雷,其他每个格子的信息也需要重新更新,所以这一步必不可少

  2. 埋雷时随机生成的坐标可能相同,所以为了真正地生成 MINE_COUNT 个雷,我们首先需要对格子的信息进行判断,即 board[row][col] 是否等于 0

  3. 对以雷为中心的九宫格进行 +1 操作,有雷的格子除外

1e2a74b3d7334545a58836595eb999e3.png

注意:在找到埋雷的格子后,首先要检查坐标是否合法,即是否越界,然后再检查是否有雷。&& 操作符控制求值顺序,可以避免越界访问的错误产生

 

 


二、加密(Encrypt)

void Encrypt()
{
	int i = 0;  // 控制行
	int j = 0;  // 控制列
	// 对每个格子进行加密操作,即每个格子的值 +20,当要打开格子时,再相应地 -20
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] += 20;  // board[i][j]:19 ~ 28(因为周围最多有 8 个雷)
		}
	}
}

扫雷初始界面如下所示

4ebdb6d5c260438d871af15d8d96dab5.png

我们不能直接将每个格子的信息绘制出来,所以在绘制游戏界面前,首先要先对每个格子进行加密操作

 

 


三、加载图片(Load Image)

#include <graphics.h>
#include <stdio.h>

#define BLOCK_SIZE 50  // 每个格子的宽度为 50 px

IMAGE imgs[12];  // 数组的元素类型为保存图片的 IMAGE 对象

void LoadImg()
{
	int i = 0;
	for (i = 0; i < 12; i++)
	{
		char imgPath[50];
		sprintf(imgPath, "./images/%d.jpg", i);
		loadimage(&imgs[i], imgPath, BLOCK_SIZE, BLOCK_SIZE);
	}
}

加载的 12 张图片

8a4ee197e8454b76a759fa0f55e18ddf.png

sprintf 函数

1.函数的原型:int sprintf(char* buffer, const char* format[, argument...]);
2.函数的作用:Write formatted data to a string.
3.需要引入的头文件:stdio.h

 

 


四、 绘制界面(Draw the Interface)

void DrawInterface()
{
	int i = 0;  // 控制行
	int j = 0;  // 控制列
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == -1)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[9]);  // 绘制雷
			}
			else if (board[i][j] >= 0 && board[i][j] <= 8)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[board[i][j]]);  // 绘制数字 0 ~ 8
			}
			else if (board[i][j] >= 19 && board[i][j] <= 28)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[10]);  // 绘制加密格子
			}
			else if (board[i][j] >= 39 && board[i][j] <= 48)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[11]);  // 绘制红旗
			}
		}
	}
}

注意:i 控制行,j 控制列,而 putimage 函数的第一个参数是绘制位置的 x 坐标,第二个参数是绘制位置的 y 坐标

 

 


五、鼠标控制(Mouse Control)

int count = 0;  // 用来记录鼠标左键按下的次数,其中包括了空白的展开
int IsOver = 0;  // 用来判断游戏是否结束

void OpenBlank(int row, int col)
{
	// 打开以空格,即 board[row][col] == 0,为中心的九宫格,并进行递归
	for (int i = row - 1; i <= row + 1; i++)
	{
		for (int j = col - 1; j <= col + 1; j++)
		{
			// 首先检查坐标是否合法,然后打开不是雷的格子
			if ((i >= 0 && i < ROW) && (j >= 0 && j < COL) &&
				(board[i][j] > 19 && board[i][j] <= 28))
			{
				board[i][j] -= 20;
				count++;
				if (board[i][j] == 0)
				{
					OpenBlank(i, j);
				}
			}
		}
	}
}

void MouseControl()
{
	ExMessage msg;
	if (peekmessage(&msg))
	{
		int row = msg.y / BLOCK_SIZE;
		int col = msg.x / BLOCK_SIZE;
		switch (msg.message)
		{
		case WM_LBUTTONDOWN:  // 鼠标左键按下消息
			if (board[row][col] >= 19 && board[row][col] <= 28)
			{
				board[row][col] -= 20;  // 打开加密格子
				count++;
				if (board[row][col] == -1)
				{
					IsOver = 1;
					// 打开所有的雷
					for (int i = 0; i < ROW; i++)
					{
						for (int j = 0; j < COL; j++)
						{
							if (board[i][j] == 19)
							{
								board[i][j] -= 20;
							}
						}
					}
				}
				if (board[row][col] == 0)
				{
					OpenBlank(row, col);
				}
			}
			break;
		case WM_RBUTTONDOWN:  // 鼠标右键按下消息
			if (board[row][col] >= 19 && board[row][col] <= 28)
			{
				board[row][col] += 20;  // 再次加密
			}
			else if (board[row][col] >= 39 && board[row][col] <= 48)
			{
				board[row][col] -= 20;  // 解密
			}
			break;
		}
	}
}

 

 


六、判断游戏是否结束(Is Game Over)

void IsGameOver()
{
	// 1. 判断游戏是否结束
	if (IsOver)
	{
		HWND hWnd = GetHWnd();
		int ret = MessageBox(NULL, "Game Over! Again?", "提示", MB_OKCANCEL);
		if (ret == IDOK)
		{
			GameInit();
			Encrypt();
			IsOver = 0;
			count = 0;
		}
		else if (ret == IDCANCEL)
		{
			exit(0);
		}
	}
	// 2. 判断玩家是否获胜
	if (count == ROW * COL - MINE_COUNT)
	{
		HWND hWnd = GetHWnd();
		int ret = MessageBox(NULL, "You Are The Winner! Again?", "提示", MB_OKCANCEL);
		if (ret == IDOK)
		{
			GameInit();
			Encrypt();
			count = 0;
		}
		else if (ret == IDCANCEL)
		{
			exit(0);
		}
	}
}

 

 


七、源代码

#define _CRT_SECURE_NO_WARNINGS 1


// 一、游戏初始化
#include <stdlib.h>
#include <time.h>  // 用时间戳作为随机数种子
#include <graphics.h>
#include <stdio.h>

#define ROW 10
#define COL 10
#define MINE_COUNT 10
#define BLOCK_SIZE 50  // 每个格子的宽度为 50 px

int board[ROW][COL] = { 0 };  // 创建一个二维数组来存储棋盘上每个格子的信息
IMAGE imgs[12];  // 数组的元素类型为保存图片的 IMAGE 对象

int count = 0;  // 用来记录鼠标左键按下的次数,其中包括了空白的展开
int IsOver = 0;  // 用来判断游戏是否结束

// 一、游戏初始化
void GameInit()
{
	int i = 0;  // 控制行
	int j = 0;  // 控制列 
	// 1. 特别是当游戏重新开始时,需要将棋盘上的每个格子的信息重新初始化为 0
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = 0;
		}
	}
	// 2. 随机生成 MINE_COUNT 个雷
	for (i = 0; i < MINE_COUNT; i++)
	{
		while (1)
		{
			int row = rand() % ROW;  // row: 0 ~ ROW - 1
			int col = rand() % COL;  // col: 0 ~ COL - 1
			if (board[row][col] == 0)  // 避免在同一个格子上埋雷
			{
				board[row][col] = -1;  // -1 表示这个格子上埋有雷
				break;
			}
		}
	}
	// 3. 对以雷为中心九宫格进行 +1 操作,有雷的格子除外
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			// 第一步:找到埋雷的格子
			if (board[i][j] == -1)
			{
				// 第二步:遍历以雷为中心的九宫格
				for (int row = i - 1; row <= i + 1; row++)
				{
					for (int col = j - 1; col <= j + 1; col++)
					{
						// 第三步:检查坐标是否合法,即是否越界;然后检查是否有雷
						if ((row >= 0 && row < ROW) && (col >= 0 && col < COL) && board[row][col] != -1)
						{
							board[row][col] += 1;
						}
					}
				}
			}
		}
	}
}


// 二、加密
void Encrypt()
{
	int i = 0;  // 控制行
	int j = 0;  // 控制列
	// 对每个格子进行加密操作,即每个格子的值 +20,当要打开格子时,再相应地 -20
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] += 20;  // board[i][j]:19 ~ 28(周围最多有 8 个雷)
		}
	}
}


// 三、加载图片
void LoadImg()
{
	int i = 0;
	for (i = 0; i < 12; i++)
	{
		char imgPath[50];
		sprintf(imgPath, "./images/%d.jpg", i);
		loadimage(&imgs[i], imgPath, BLOCK_SIZE, BLOCK_SIZE);
	}
}


// 四、绘制游戏界面
void DrawInterface()
{
	int i = 0;  // 控制行
	int j = 0;  // 控制列
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == -1)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[9]);  // 绘制雷
			}
			else if (board[i][j] >= 0 && board[i][j] <= 8)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[board[i][j]]);  // 绘制数字 0 ~ 8
			}
			else if (board[i][j] >= 19 && board[i][j] <= 28)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[10]);  // 绘制加密格子
			}
			else if (board[i][j] >= 39 && board[i][j] <= 48)
			{
				putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[11]);  // 绘制红旗
			}
		}
	}
}


// 五、鼠标控制
void OpenBlank(int row, int col)
{
	// 打开以空格,即 board[row][col] == 0,为中心的九宫格,并进行递归
	for (int i = row - 1; i <= row + 1; i++)
	{
		for (int j = col - 1; j <= col + 1; j++)
		{
			// 首先检查坐标是否合法,然后打开不是雷的格子
			if ((i >= 0 && i < ROW) && (j >= 0 && j < COL) &&
				(board[i][j] > 19 && board[i][j] <= 28))
			{
				board[i][j] -= 20;
				count++;
				if (board[i][j] == 0)
				{
					OpenBlank(i, j);
				}
			}
		}
	}
}

void MouseControl()
{
	ExMessage msg;
	if (peekmessage(&msg))
	{
		int row = msg.y / BLOCK_SIZE;
		int col = msg.x / BLOCK_SIZE;
		switch (msg.message)
		{
		case WM_LBUTTONDOWN:  // 鼠标左键按下消息
			if (board[row][col] >= 19 && board[row][col] <= 28)
			{
				board[row][col] -= 20;  // 打开加密格子
				count++;
				if (board[row][col] == -1)
				{
					IsOver = 1;
					// 打开所有的雷
					for (int i = 0; i < ROW; i++)
					{
						for (int j = 0; j < COL; j++)
						{
							if (board[i][j] == 19)
							{
								board[i][j] -= 20;
							}
						}
					}
				}
				if (board[row][col] == 0)
				{
					OpenBlank(row, col);
				}
			}
			break;
		case WM_RBUTTONDOWN:  // 鼠标右键按下消息
			if (board[row][col] >= 19 && board[row][col] <= 28)
			{
				board[row][col] += 20;  // 再次加密
			}
			else if (board[row][col] >= 39 && board[row][col] <= 48)
			{
				board[row][col] -= 20;  // 解密
			}
			break;
		}
	}
}


// 六、判断游戏是否结束
void IsGameOver()
{
	// 1. 判断游戏是否结束
	if (IsOver)
	{
		HWND hWnd = GetHWnd();
		int ret = MessageBox(NULL, "Game Over! Again?", "提示", MB_OKCANCEL);
		if (ret == IDOK)
		{
			GameInit();
			Encrypt();
			IsOver = 0;
			count = 0;
		}
		else if (ret == IDCANCEL)
		{
			exit(0);
		}
	}
	// 2. 判断玩家是否获胜
	if (count == ROW * COL - MINE_COUNT)
	{
		HWND hWnd = GetHWnd();
		int ret = MessageBox(NULL, "You Are The Winner! Again?", "提示", MB_OKCANCEL);
		if (ret == IDOK)
		{
			GameInit();
			Encrypt();
			count = 0;
		}
		else if (ret == IDCANCEL)
		{
			exit(0);
		}
	}
}


// 主函数
int main()
{
	srand((unsigned int)time(NULL));  // 设置随机数种子
	
	initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE, EW_SHOWCONSOLE);
	GameInit();
	Encrypt();
	LoadImg();
	
    /*
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			printf("%2d ", board[i][j]);
		}
		printf("\n");
	}
	*/

	BeginBatchDraw();
	while (1)
	{
		MouseControl();
		DrawInterface();
		FlushBatchDraw();
		IsGameOver();
	}
	EndBatchDraw();
	
	closegraph();
	return 0;
}

链接:百度网盘 请输入提取码

提取码:uwd1

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值