人生真是寂寞如雪啊。。。

思路是找到棋盘位置->读取棋盘数据->计算可消除的格子->向窗口发送鼠标点击消息

Tip:可以点击练习来进行单人游戏。

找棋盘位置

找棋盘位置本来想下rand或者消息。。。结果断不了。。。只能用最简单的CE了。。。 跟金山游侠一样哦,选一个进程,然后搜索内存~ 预测棋盘应该是一个数组,就用棋盘最左上角的那个位置做参照,预测有东西应该是非0,没有应该大于0。
利用CE不断搜索,没搜一次再点练习更新一次棋盘 经过试验value type是byte哦 最后找到棋盘地址如下,这是x64的,x86的要再找下。。

读取棋盘数据

读棋盘数据首先要获得进程pid,可以通过Findwindow先找到窗口句柄,然后再GetWindowThreadProcessId找到pid。 最后ReadProcessMemory把棋盘数据读到数组里就好了。棋盘大小是11*19

1
2
3
4
5
6
7
8
9
10
11
hWnd = FindWindow(NULL, \_T(GAME\_CAPTION));
if (hWnd == NULL)
{
\_tprintf\_s(_T("Cant find windown"));
return FALSE;
}
GetWindowThreadProcessId(hWnd, &pid);
hGame = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, pid);
ReadProcessMemory(hGame, lpChess, chess, 11 * 19, NULL);
CloseHandle(hGame);

计算可消除的格子

棋盘数据已经存在11*19的数组里了,0代表空格,非零的情况对应的号码代表一个块,同号码代表同块 我是用一个深搜,先将棋盘外围填上一个边界值-1,代表到边界。

1
2
3
4
5
6
7
8
memset(chess2, -1, sizeof(chess2));
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 19; j++)
{
chess2\[i + 1\]\[j + 1\] = chess\[i\]\[j\];
}
}

之后从棋盘左上角开始遍历,判断每一个方块是否可以被消除。判断是否可以被消除的函数为bCheck()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int x1, x2, y1, y2;
for (x1 = 1; x1 <= 11; x1++)
{
for (y1 = 1; y1 <= 19; y1++)
{
if (chess2\[x1\]\[y1\] == 0)
continue;
for (x2 = 1; x2 <= 11; x2++)
{
for (y2 = 1; y2 <= 19; y2++)
{
if (!(x1 == x2 && y1 == y2) && chess2\[x1\]\[y1\] == chess2\[x2\]\[y2\])
{
if (bCheck(x1, y1, x2, y2, 0, DIRECT_BEGIN))
{
\_tprintf\_s(_T("GET:%d,%d and %d,%dn"), x1, y1, x2, y2);
CloseHandle(hWnd);
return TRUE;
}
}
}
}
}
}

下面是关键的bCheck,采用深搜。依次向上下左右递归搜索。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
BOOL ClearLianliankan::bCheck(int x1, int y1, int x2, int y2, int cTurn, int eDirect)
{
if (cTurn > 3)
return FALSE;
if (x1 == x2 && y1 == y2)
return TRUE;
//UP
if ((chess2\[x1 - 1\]\[y1\] == 0 || chess2\[x1 - 1\]\[y1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_DOWN)
{
if (eDirect != DIRECT_UP)
{
if (bCheck(x1 - 1, y1, x2, y2, cTurn + 1, DIRECT_UP))
return TRUE;
}
else
{
if (bCheck(x1 - 1, y1, x2, y2, cTurn, DIRECT_UP))
return TRUE;
}
}
//DOWN
if ((chess2\[x1 + 1\]\[y1\] == 0 || chess2\[x1 + 1\]\[y1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_UP)
{
if (eDirect != DIRECT_DOWN)
{
if (bCheck(x1 + 1, y1, x2, y2, cTurn + 1, DIRECT_DOWN))
return TRUE;
}
else
{
if (bCheck(x1 + 1, y1, x2, y2, cTurn, DIRECT_DOWN))
return TRUE;
}
}
//LEFT
if ((chess2\[x1\]\[y1 - 1\] == 0 || chess2\[x1\]\[y1 - 1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_RIGHT)
{
if (eDirect != DIRECT_LEFT)
{
if (bCheck(x1, y1 - 1, x2, y2, cTurn + 1, DIRECT_LEFT))
return TRUE;
}
else
{
if (bCheck(x1, y1 - 1, x2, y2, cTurn, DIRECT_LEFT))
return TRUE;
}
}
//RIGHT
if ((chess2\[x1\]\[y1 + 1\] == 0 || chess2\[x1\]\[y1 + 1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_LEFT)
{
if (eDirect != DIRECT_RIGHT)
{
if (bCheck(x1, y1 + 1, x2, y2, cTurn + 1, DIRECT_RIGHT))
return TRUE;
}
else
{
if (bCheck(x1, y1 + 1, x2, y2, cTurn, DIRECT_RIGHT))
return TRUE;
}
}
return FALSE;
}

向窗口发送鼠标点击消息

用PostMessage就搞定啦。

1
2
3
4
PostMessage(hWnd, WM\_LBUTTONDOWN, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y1 - 1), 195 + BLOCK_HIGHT*(x1 - 1)));
PostMessage(hWnd, WM\_LBUTTONUP, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y1 - 1), 195 + BLOCK_HIGHT*(x1 - 1)));
PostMessage(hWnd, WM\_LBUTTONDOWN, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y2 - 1), 195 + BLOCK_HIGHT*(x2 - 1)));
PostMessage(hWnd, WM\_LBUTTONUP, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y2 - 1), 195 + BLOCK_HIGHT*(x2 - 1)));

这边要注意发送的坐标信息要我们自己去收集,可以用VS自带的Spy++,找到每个格子的长宽以及左上角格子的坐标。

完整代码

添加了热键什么的

https://github.com/yufanpi/lianliankan/