laitimes

【Project Practice】C language + easyX takes you to realize: Minesweeper Game (Hexagon Upgrade Version)!

author:C language programming

One small programming project every day to improve your programming skills!

【Project Practice】C language + easyX takes you to realize: Minesweeper Game (Hexagon Upgrade Version)!

Introduction to the program

Minesweeper Game Upgrade! Hexagonal minesweeper (treasure hunting mode) a little introduction ha ~

He also wants to point out all safe places.

He does not have a zeroing algorithm for minesweeping modes. Each safe point needs to be dug up separately, showing one grid at a time.

Added the concept of health, which means that there is a certain tolerance for fault.

The numbers displayed differ from minesweeping modes. Click on the treasure points, the number of surrounding treasure points will be displayed, green; Click on the mine, and the number of surrounding mines will be displayed, black. Note that this number does not include itself, and the range displayed is naturally 0~6. Tapping on a mine will reduce health, and when health reaches zero, it ends.

So both thunder and treasure are valuable and can give accurate information.

I can give a reference difficulty: 40% of the total number of tiles, 50% of the total number of mines health.

[Note: Need compiler + graphics library plug-in can be obtained at the end of the article]

Program operation demonstration

【Project Practice】C language + easyX takes you to realize: Minesweeper Game (Hexagon Upgrade Version)!

After a brief understanding of the game, let's try it! (Directly on the source code, you can see the comments)

////////////////////////////////////////
// 程序:六边形扫雷:寻宝模式

# include <math.h>
# include <graphics.h>
# include <string>
# include <time.h>

static double pi = acos (-1.0);			// 圆周率 π
static HWND hOut;						// 画布

// 定义一个结构体,按钮
struct Node1
{
	int posx1, posy1, posx2, posy2;		// 坐标
	LPTSTR text;						// 文字
	int mod;							// 状态
};

// 定义一个结构体,六边形格子
struct Node2
{
	int i, j, k;						// 特征值
	int mod_life;						// 翻开
	int mod_mine;						// 雷
	int mod_flag;						// 标记
	int posx, posy;						// 坐标
	int num_mine;						// 周围雷数
	int num_peace;						// 周围空地块
};

// 定义一个类
class Gary
{
public:
	void carry ();						// 主进程
	void initialization ();				// 初始化
	void draw_scene ();					// 绘制界面函数
	void draw_box (int num_box);		// 绘制格子
	void draw_flag (int num_box);		// 绘制标记
	void draw_num (int num_box, int num);	// 绘制数字
	void move ();						// 窗口主视角
	void create ();						// 地雷生成
	void check_over ();					// 结束判定

	int num_button;						// 按钮数量参数
	int exit_carry;						// 主循函数控制参数
	int exit_move;						// 开始界面控制参数
	int exit_game;						// 游戏进行控制参数
	int num_life;						// 生命值
	int num_size;						// 边长
	int num_mine;						// 总雷数
	int num_box;						// 总地块数
	int num_flag;						// 标记数
	COLORREF color_text[2];				// 按钮绘制填充
	Node1 boxm[30];						// 按钮,预制 30 个
	Node2 box[1000];					// 地块
};

// 标记绘制函数
void Gary::draw_flag (int num_box)
{
	setlinestyle (PS_SOLID, 1);
	setlinecolor (BLACK);
	line (box[num_box].posx + 2, box[num_box].posy + 7, box[num_box].posx + 2, box[num_box].posy - 7);
	setfillcolor (LIGHTRED);
	setlinecolor (LIGHTRED);
	fillrectangle (box[num_box].posx - 7 + 2, box[num_box].posy - 7, box[num_box].posx + 2, box[num_box].posy - 1);
}

// 数字绘制函数
void Gary::draw_num (int num_box, int num)
{
	int i;
	// 画六边形,格子处于点击后状态
	setfillcolor (RGB (170, 170, 170));
	setlinecolor (RGB (85, 85, 85));
	POINT pts[6];
	setlinestyle (PS_SOLID, 1);
	for (i = 0; i < 6; i++)
	{
		pts[i].x = long(box[num_box].posx + 14.0 * cos (60.0 * double (i) * pi / 180.0));
		pts[i].y = long(box[num_box].posy + 14.0 * sin (60.0 * double (i) * pi / 180.0));
	}
	fillpolygon (pts, 6);

	// 数字绘制
	TCHAR s[15];
	settextstyle (20, 0, _T ("Consolas"));
	_stprintf_s (s, _T ("%0.1d"), num);
	outtextxy (box[num_box].posx - 5, box[num_box].posy - 10, s);
}

// 场景绘制函数
void Gary::draw_scene ()
{
	TCHAR s[15];
	int i, j;
	setlinecolor (BLACK);
	setfillcolor (WHITE);
	setlinestyle (PS_SOLID, 1);
	// 主界面
	fillrectangle (401, 0, 650, 400);
	// 根据按钮数量绘制	
	settextcolor (BLACK);
	for (i = 0; i < num_button; i++)
	{
		setfillcolor (color_text[boxm[i].mod]);
		setbkcolor (color_text[boxm[i].mod]);
		// 边框
		fillrectangle (boxm[i].posx1, boxm[i].posy1, boxm[i].posx2, boxm[i].posy2);
		// 文字
		outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + 4, boxm[i].text);
	}

	// 设置参数
	setbkcolor (WHITE);
	settextcolor (BLACK);
	setlinecolor (BLACK);

	// 变量绘制
	j = 25;
	// 生命值
	i = 1;
	setbkcolor (color_text[boxm[i].mod]);
	_stprintf_s (s, _T ("%0.1d"), num_life);
	outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
	// 边长
	i = 2;
	setbkcolor (color_text[boxm[i].mod]);
	_stprintf_s (s, _T ("%0.1d"), num_size);
	outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
	// 总地雷数
	i = 3;
	setbkcolor (color_text[boxm[i].mod]);
	_stprintf_s (s, _T ("%0.1d"), num_mine);
	outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
	// 格子
	i = 4;
	setbkcolor (color_text[boxm[i].mod]);
	_stprintf_s (s, _T ("%0.1d"), num_box);
	outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
	// 标记数
	i = 5;
	setbkcolor (color_text[boxm[i].mod]);
	_stprintf_s (s, _T ("%0.1d"), num_flag);
	outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);

	FlushBatchDraw ();
}

// 地雷生成函数
void Gary::create ()
{
	int i, j;
	// 设置雷
	for (i = 0; i < num_mine; i++)
	{
		// 随机
		j = rand () % 1000;
		while (box[j].mod_mine == 1 || box[j].mod_life == 1)
		{
			// 随机
			j = rand () % 1000;
		}
		// 是雷
		box[j].mod_mine = 1;
	}
	// 周边雷数统计
	// 遍历
	for (i = 0; i <= 888; i++)
	{
		if (box[i].mod_life == 0)
		{
			// 遍历
			for (j = 0; j <= 999; j++)
			{
				// 排除自己
				if (j != i && box[j].mod_life == 0)
				{
					// 周围六个
					if ((box[j].posx - box[i].posx) * (box[j].posx - box[i].posx) + (box[j].posy - box[i].posy) * (box[j].posy - box[i].posy) <= 900)
					{
						// 是雷
						if (box[j].mod_mine == 1)
						{
							// 周边雷数参数加一
							box[i].num_mine++;
						}
						// 不是雷
						else if (box[j].mod_mine == 0)
						{
							// 周边安全数参数加一
							box[i].num_peace++;
						}
					}
				}
			}
		}
	}
}

// 结束判断函数
void Gary::check_over ()
{
	int i, k;
	k = 0;
	for (i = 0; i <= 888; i++)
	{
		// 每有一个翻开且不是雷的点,则加一
		if (box[i].mod_mine == 0 && box[i].mod_life == 1)
		{
			k++;
		}
	}
	// 全翻开则结束
	if (k == num_box - num_mine)
	{
		// 将所有未翻开雷做上标记
		for (i = 0; i <= 888; i++)
		{
			if (box[i].mod_mine == 1 && box[i].mod_life == 0)
			{
				draw_flag (i);
			}
		}
		// 胜利标志:笑脸
		setfillcolor (WHITE);
		setlinecolor (WHITE);
		fillrectangle (50, 20, 75, 45);
		settextstyle (30, 0, _T ("Wingdings"));
		setbkmode (TRANSPARENT);
		settextcolor (BLACK);
		outtextxy (50, 20, 0x4A);
		setbkmode (OPAQUE);
		settextstyle (20, 0, _T ("Consolas"));
		// 结束变化
		exit_game = 1;
		boxm[1].mod = 0;
		boxm[2].mod = 0;
		boxm[3].mod = 0;
		boxm[6].mod = 0;
		boxm[7].mod = 1;
		num_flag = 0;
		// 绘制
		draw_scene ();
	}
}

// 格子绘制函数
void Gary::draw_box (int num_box)
{
	int i;
	int posx, posy;

	// 六边形绘制
	posx = box[num_box].posx;
	posy = box[num_box].posy;

	POINT pts[6];
	setlinestyle (PS_SOLID, 2);
	// 背景色
	setfillcolor (RGB (255, 255, 255));
	for (i = 0; i < 6; i++)
	{
		pts[i].x = long(posx + 14.0 * cos (60.0 * double (i) * pi / 180.0));
		pts[i].y = long(posy + 14.0 * sin (60.0 * double (i) * pi / 180.0));
	}
	solidpolygon (pts, 6);
	// 灰边
	setlinecolor (RGB (85, 85, 85));
	line (pts[0].x, pts[0].y, pts[1].x, pts[1].y);
	line (pts[5].x, pts[5].y, pts[0].x, pts[0].y);
	line (pts[1].x, pts[1].y, pts[2].x, pts[2].y);
	// 前景色
	setfillcolor (RGB (170, 170, 170));
	for (i = 0; i < 6; i++)
	{
		pts[i].x = long(posx + 12.0 * cos (60.0 * double (i) * pi / 180.0));
		pts[i].y = long(posy + 12.0 * sin (60.0 * double (i) * pi / 180.0));
	}
	solidpolygon (pts, 6);
	FlushBatchDraw ();
}

// 初始化函数
void Gary::initialization ()
{
	int i, j, k, t;
	// 随机初始化
	srand ((unsigned)time (NULL));
	// 颜色初始化
	color_text[0] = WHITE;
	color_text[1] = RGB (170, 170, 170);

	// 按钮的初始化
	num_button = 10;

	// 坐标
	for (i = 0; i < 10; i++)
	{
		boxm[i].posx1 = 410 + 120 * (i % 2);
		boxm[i].posy1 = 25 + 75 * (i / 2);
		boxm[i].posx2 = 520 + 120 * (i % 2);
		boxm[i].posy2 = 75 + 75 * (i / 2);
	}

	// 内容
	boxm[0].text = _T ("寻宝模式");	boxm[1].text = _T ("生命值");
	boxm[2].text = _T ("地图边长");	boxm[3].text = _T ("总地雷数");
	boxm[4].text = _T ("总地块数");	boxm[5].text = _T ("已标记数");
	boxm[6].text = _T ("开始");		boxm[7].text = _T ("重置");
	boxm[8].text = _T ("截图");		boxm[9].text = _T ("退出");

	// 状态
	boxm[0].mod = 1;
	boxm[1].mod = 1;
	boxm[2].mod = 1;
	boxm[3].mod = 1;
	boxm[4].mod = 1;
	boxm[5].mod = 1;
	boxm[6].mod = 1;
	boxm[7].mod = 0;
	boxm[8].mod = 0;
	boxm[9].mod = 0;

	num_box = 3 * num_size * (num_size - 1) + 1;
	num_flag = 0;

	// 绘制参数初始化
	setlinecolor (BLACK);
	setlinestyle (PS_SOLID, 1);
	settextstyle (20, 0, _T ("Consolas"));
	// 第一次绘制
	draw_scene ();

	// 重置
	setfillcolor (WHITE);
	fillrectangle (0, 0, 400, 400);

	// 平静脸
	setfillcolor (WHITE);
	setlinecolor (WHITE);
	fillrectangle (50, 20, 75, 45);
	settextstyle (30, 0, _T ("Wingdings"));
	setbkmode (TRANSPARENT);
	settextcolor (BLACK);
	outtextxy (50, 20, 0x4B);
	setbkmode (OPAQUE);
	settextstyle (20, 0, _T ("Consolas"));

	// 格子初始化
	for (t = 0; t <= 999; t++)
	{
		// 已翻开
		box[t].mod_life = 1;
		// 城墙
		box[t].mod_mine = 2;
		// 坐标,点不到
		box[t].posx = -200;
		box[t].posy = -200;
	}

	// 初始化
	for (i = 0; i < num_size; i++)
	{
		for (j = 0; j < num_size; j++)
		{
			for (k = 0; k < num_size; k++)
			{
				// 特征值至少一个为零
				if (i == 0 || j == 0 || k == 0)
				{
					// 编号
					t = i * 100 + j * 10 + k;
					// 特征值
					box[t].i = i;
					box[t].j = j;
					box[t].k = k;
					// 未翻开
					box[t].mod_life = 0;
					// 不是雷
					box[t].mod_mine = 0;
					// 未标记
					box[t].mod_flag = 0;
					// 坐标
					box[t].posx = 200 + 22 * (j - k);
					box[t].posy = 200 - 25 * i + 13 * (j + k);
					// 周围雷数初始化
					box[t].num_mine = 0;
					box[t].num_peace = 0;
					// 绘制地块
					draw_box (t);
				}
			}
		}
	}
	// 地雷生成函数
	create ();
}

// 窗口主视角函数,获取用户操作
void Gary::move ()
{
	// 鼠标定义
	ExMessage m;
	TCHAR ss[15];
	int i, t;
	exit_move = 0;
	exit_game = 0;
	while (exit_move == 0)
	{
		// 鼠标信息
		if (peekmessage (&m, EM_MOUSE | EM_KEY))
		{
			// 左键单击判断
			if (m.message == WM_LBUTTONDOWN)
			{
				// 判断是否点击了格子
				if (m.x > 0 && m.y > 0 && m.x < 400 && m.y < 400 && exit_game == 0)
				{
					for (t = 0; t <= 888; t++)
					{
						// 成功点击未标记的空格子
						if ((m.x - box[t].posx) * (m.x - box[t].posx) + (m.y - box[t].posy) * (m.y - box[t].posy) <= 144 && box[t].mod_life == 0 && box[t].mod_flag == 0)
						{
							// 点击的格子不是雷
							if (box[t].mod_mine == 0)
							{
								// 绿色,安全,绘制
								settextcolor (LIGHTGREEN);
								draw_num (t, box[t].num_peace);
								// 改为翻开
								box[t].mod_life = 1;
							}
							// 点击的格子雷
							else if (box[t].mod_mine == 1)
							{
								// 扣除生命值
								num_life--;
								// 黑色,危险,绘制
								settextcolor (BLACK);
								draw_num (t, box[t].num_mine);
								// 改为翻开
								box[t].mod_life = 1;
								// 生命值减为零
								if (num_life <= 0)
								{
									// 失败标志:哭脸
									setfillcolor (WHITE);
									setlinecolor (WHITE);
									fillrectangle (50, 20, 75, 45);
									settextstyle (30, 0, _T ("Wingdings"));
									setbkmode (TRANSPARENT);
									settextcolor (BLACK);
									outtextxy (50, 20, 0x4C);
									setbkmode (OPAQUE);
									settextstyle (20, 0, _T ("Consolas"));
									// 失败
									exit_game = 1;
									boxm[1].mod = 0;
									boxm[2].mod = 0;
									boxm[3].mod = 0;
									boxm[6].mod = 0;
									boxm[7].mod = 1;
									num_flag = 0;
								}
								// 绘制
								draw_scene ();
							}
							// 成功结束判断
							check_over ();
							break;
						}
					}
				}

				// 判断是否点击了可点击按钮
				for (i = 0; i < num_button; i++)
				{
					if (m.x > boxm[i].posx1 && m.y > boxm[i].posy1 && m.x < boxm[i].posx2 && m.y < boxm[i].posy2 && boxm[i].mod == 0)
					{
						break;
					}
				}

				// 点击矩形按钮
				switch (i)
				{
				// 生命值:num_life
				case 1:
				{
					// 输入
					InputBox (ss, 10, _T ("输入生命值(1 ~ 999)"));
					_stscanf_s (ss, _T ("%d"), &i);
					if (i > 0 && i <= 999)
					{
						num_life = i;
					}
					else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
					// 绘制
					draw_scene ();
					break;
				}
				// 地图边长:num_size
				case 2:
				{
					// 输入
					InputBox (ss, 10, _T ("输入边长(2 ~ 8)"));
					_stscanf_s (ss, _T ("%d"), &i);
					if (i > 1 && i <= 8)
					{
						num_size = i;
						num_box = 3 * num_size * (num_size - 1) + 1;
					}
					else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
					// 绘制
					draw_scene ();
					break;
				}
				// 总地雷数:num_mine
				case 3:
				{
					InputBox (ss, 10, _T ("输入地雷数(1 ~ 总格子数)"));
					_stscanf_s (ss, _T ("%d"), &i);
					if (i > 0 && i < num_box)
					{
						num_mine = i;
					}
					else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
					// 绘制
					draw_scene ();
					break;
				}
				// 开始
				case 6:
				{
					num_box = 3 * num_size * (num_size - 1) + 1;
					if (num_mine < num_box && num_life > 0)
					{
						exit_game = 0;
						// 初始化
						initialization ();
					}
					else
					{
						MessageBox (hOut, _T ("请将雷数修改为小于格子数或将生命值修改为大于零"), _T ("来自小豆子的提醒"), MB_OK);
					}
					break;
				}
				// 重置
				case 7:
				{
					// 结束游戏进程,进入准备阶段
					if (exit_game == 0)
					{
						exit_game = 1;
						boxm[1].mod = 0;
						boxm[2].mod = 0;
						boxm[3].mod = 0;
						boxm[6].mod = 0;
						boxm[7].mod = 1;
						num_flag = 0;
						// 绘制
						draw_scene ();
					}
					break;
				}
				// 截图
				case 8:
				{
					saveimage (_T ("image.png"));
					break;
				}
				// 退出
				case 9:
				{
					exit_game = 1;
					exit_move = 1;
					exit_carry = 1;
					break;
				}
				default:break;
				}
			}
			// 右键,且处于游戏进行状态
			else if (m.message == WM_RBUTTONDOWN && exit_game == 0)
			{
				for (t = 0; t <= 888; t++)
				{
					// 成功点击空格子
					if ((m.x - box[t].posx) * (m.x - box[t].posx) + (m.y - box[t].posy) * (m.y - box[t].posy) <= 144 && box[t].mod_life == 0)
					{
						// 标记状态转换
						box[t].mod_flag = (box[t].mod_flag == 0 ? 1 : 0);
						// 绘制
						draw_box (t);
						// 画小旗子
						if (box[t].mod_flag == 1)
						{
							draw_flag (t);
							num_flag++;
						}
						else
						{
							num_flag--;
						}
						// 绘制
						draw_scene ();
					}
				}
			}
		}
	}
}

// 主进程
void Gary::carry ()
{
	// 窗口定义
	hOut = initgraph (651, 401);
	SetWindowText (hOut, _T ("六边形扫雷:扫雷模式"));
	// 参数初始化
	num_size = 5;
	num_mine = 10;
	num_life = 3;
	// 背景绘制
	setbkcolor (WHITE);
	cleardevice ();
	// 进程控制
	exit_carry = 0;
	while (exit_carry == 0)
	{
		initialization ();
		move ();
	}
	closegraph ();
}

// 主函数
int main (void)
{
	Gary G;
	G.carry ();
	return 0;
}


           

Let's try it now!

In addition, I also share with you the other resources I have collected, from tutorials starting from the ground up to C++ project cases in C to help you overcome difficulties on the road to learning C!

Programming Learning Book Sharing:

【Project Practice】C language + easyX takes you to realize: Minesweeper Game (Hexagon Upgrade Version)!

Coding Learning Video Sharing:

【Project Practice】C language + easyX takes you to realize: Minesweeper Game (Hexagon Upgrade Version)!

Organize and share (years of learning source code, project practical videos, project notes, basic introductory tutorials) The most important thing is that you can communicate and ask programming questions in the group!

If you are interested in C/C++, you can follow Xiaobian to send me a private message in the background: [Programming Exchange] Let's learn together! You can get some C/C++ project learning videos! Keyword autoresponders have been set up, and automatic collection is good!