秒消QQ连连看(C/C++)

完整源码(vs2105)下载地址:https://download.csdn.net/download/u011081622/87797095

一、问题分解:
1、从游戏界面上获取数据,转换成二维数组 (Windows API,和easyx库)
2、使用连连看的算法,消除二维数组 (简单的二维数组遍历)
3、将消除的过程反馈给游戏界面 (Windows API移动鼠标和模拟点击)

二、使用到的第三方库:

easyx,EasyX Graphics Library for C++傻瓜式安装,按照后#include "easyx.h",便可使用

三、连连看算法,参考:

https://blog.csdn.net/zhelong19830726/article/details/98394184

四、代码实现

1、从游戏界面上获取数据(图片),然后切图

	//1、查找连连看的窗口句柄
	HWND hWin = FindWindowExA(NULL, NULL, NULL, "QQ游戏 - 连连看角色版");

	//2、把连连看的窗口提到最前面
	ShowWindow(hWin, SW_SHOWNORMAL);
	SetForegroundWindow(hWin);
	Sleep(300);

	//3、获得窗口的绘图句柄
	HDC hWinDC = GetDC(hWin);

	//创建一张空白图片
	IMAGE image(800, 600);
	HDC imageDC = GetImageHDC(&image);

	BitBlt(imageDC, 0, 0, 800, 600, hWinDC, 0, 0, SRCCOPY);

    if (1)
	{
		SetWorkingImage(&image);
		setorigin(LEFT_X, LEFT_Y);
		for (int y = 0; y < ROW; ++y)
		{
			for (int x = 0; x < COL; ++x)
			{
				getimage(&img[y][x], x*BLOCK_W, y*BLOCK_H, BLOCK_W, BLOCK_H);
			}
		}
		SetWorkingImage(nullptr);
	}

 效果图,程序拉取到游戏数据:

转成二维数组,方法是,给每张小图编号,空白小图的编号为0,第一张小图的编号设为1,如果第二张与第一张小图相同,则编号也为1,否则设置为2,以此类推:

//切图编号转成amp
void image_to_map()
{
	bool isfirst = true;
	int num = 1;

	for (int i = 0; i < ROW; ++i)
	{
		for (int j = 0; j < COL; ++j)
		{
			if (is_blank(&img[i][j]))
			{
				array[i][j] = 0;
			}
			else if (isfirst)
			{
				array[i][j] = num++;
				isfirst = false;
			}
			else
			{
				bool is_ok = false;
				for (int m = 0; m < ROW; ++m)
				{
					for (int n = 0; n < COL; ++n)
					{
						if (is_blank(&img[m][n]))
						{
							continue;
						}
						else if (false == (m == i && n == j))
						{
							if (is_same(&img[i][j], &img[m][n]))
							{
								array[i][j] = array[m][n];
								is_ok = true;
								break;
							}
						}
						else//自己
						{
							array[i][j] = num++;
							is_ok = true;
							break;
						}
					}
					if (is_ok)
					{
						break;
					}
				}
			}
		}
	}
}

效果图:

2、使用连连看算法消除,二维数组的遍历,共有4种情况:水平、垂直、1个拐角和2个拐角:

//位置(x, y)上有无障碍物:
bool is_block(int y, int x)
{
	if (array[y][x] == 0)
	{
		return false;
	}
	return true;
}

//水平检测
bool horizon(int y1, int x1, int y2, int x2)
{
	if (y1 == y2 && x1 != x2)
	{
		int startX = min(x1, x2);
		int endX = max(x1, x2);
		for (int i = startX + 1; i < endX; i++)
		{
			if (is_block(y1, i))
			{
				return false;
			}
		}
		return true;
	}
	else
	{
		return false;
	}
}

//垂直检测
bool vertical(int y1, int x1, int y2, int x2)
{
	if (y1 != y2 && x1 == x2)
	{
		int startY = min(y1, y2);
		int endY = max(y1, y2);
		for (int i = startY + 1; i < endY; i++)
		{
			if (is_block(i, x1))
			{
				return false;
			}
		}
		return true;
	}
	else
	{
		return false;
	}
}

//一个拐角检测
bool turn_once(int y1, int x1, int y2, int x2)
{
	if (y1 == y2 && x1 == x2)
	{
		return false;
	}
	//C点
	int c_y = y1;
	int c_x = x2;
	if (!is_block(c_y, c_x))
	{
		if (horizon(y1, x1, c_y, c_x) && vertical(c_y, c_x, y2, x2))
		{
			return true;
		}
	}
	//D点
	int d_y = y2;
	int d_x = x1;
	if (!is_block(d_y, d_x))
	{
		if (vertical(y1, x1, d_y, d_x) && horizon(d_y, d_x, y2, x2))
		{
			return true;
		}
	}
	return false;
}

//两个拐角检测 == 一个拐角检测 && (水平检测 || 垂直检测)
bool turn_twice(int y1, int x1, int y2, int x2)
{
	if (y1 == y2 && x1 == x2)
	{
		return false;
	}

	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			//水平、垂直分别穿过 A B 共有四条直线,扫描直线上所有不包含 A B 的点,看是否存在一点 C
			if ((i != y1 && i != y2 && j != x1 && j != x2))
			{
				continue;
			}
			if ((i == y1 && j == x1) || (i == y2 && j == x2)) //不是A或B点
			{
				continue;
			}
			if (is_block(i, j))
			{
				continue;
			}
			if (turn_once(y1, x1, i, j) && (horizon(i, j, y2, x2) || vertical(i, j, y2, x2)))
			{
				return true;
			}
			if (turn_once(y2, x2, i, j) && (horizon(i, j, y1, x1) || vertical(i, j, y1, x1)))
			{
				return true;
			}
		}
	}
	return false;
}

//判断两个点是否可以消除
int can_remove(int y1, int x1, int y2, int x2)
{
	if (horizon(y1, x1, y2, x2))
	{
		return 1;
	}
	if (vertical(y1, x1, y2, x2))
	{
		return 2;
	}
	if (turn_once(y1, x1, y2, x2))
	{
		return 3;
	}
	if (turn_twice(y1, x1, y2, x2))
	{
		return 4;
	}
	return 0;
}

void find_and_remove()
{
	while (1)
	{
		int left_count = 0;
		int remove_count = 0;
		for (int y = 0; y < ROW; ++y)
		{
			for (int x = 0; x < COL; ++x)
			{
				if (0 == array[y][x])
				{
					continue;
				}
				++left_count;

				//已确定A点,找B点
				bool findB = false;
				for (int m = 0; m < ROW; ++m)
				{
					for (int n = 0; n < COL; ++n)
					{
						if (y == m && x == n)
						{
							continue;
						}
						if (array[y][x] == array[m][n]) //找到一个B点
						{
							int ret = can_remove(y, x, m, n);
							if (ret)
							{
								printf("消除(%02d,%02d)  (%02d,%02d) 值:%02d,类型:%d\n", y, x, m, n, array[m][n], ret);
								array[y][x] = 0;
								array[m][n] = 0;
								findB = true;
								++remove_count;
								break;
							}
						}
					}
					if (findB)
					{
						break;
					}
				}
			}
		}
		if (0 == left_count)
		{
			break;
		}
		if (0 == remove_count && left_count > 0)
		{
			printf("已经无解!请使用道具重排\n");
			print_array();
			Sleep(999999);
		}
	}
	printf("全部完成\n");
}

3、将消除过程同步到QQ游戏界面,每消除一对,就用Windows API模拟手工移动鼠标和分别左键点击两个点

//数组下标为yx的图片的桌面坐标
void xy_to_pos(int y, int x, LPPOINT lpPoint)
{
	lpPoint->x = LEFT_X + x*BLOCK_W + 12;
	lpPoint->y = LEFT_Y + y*BLOCK_H + 12;
}

//窗口的相对位置
void left_click(const POINT& p)
{
	POINT sp = {};
	sp.x = p.x + rect.left;
	sp.y = p.y + rect.top;
	SetCursorPos(sp.x, sp.y);
	Sleep(1);
	mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
	Sleep(1);
	mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
	Sleep(1);
}

本方法不需要外挂或者破解相关的知识,只用到一般的Windows API和easyx库(这个库的使用也很简单),其核心思想是将复杂的问题,分解成相对简单的几个问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值