完整源码(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库(这个库的使用也很简单),其核心思想是将复杂的问题,分解成相对简单的几个问题