前段时间玩天天爱消除,玩的实在弱渣了,感觉写个自动爱消除的程序应该很简单,闲来无事的时候就写了一个,效果还是蛮不错的,很容易就Rank1了。
因为对移动端开发并不是很熟悉,我是在Windows上用QT来实现的,运行游戏采用的是BlueStack安卓模拟器,程序只能算是Demo版,很多功能都不完善,只是实现了基本的功能。
程序流程其实很简单,大致可以分为以下几步:
1.获得游戏区域图像
2.识别色快,转化成数组
3.找出交换后可以消除的两个色块
4.发出鼠标指令点击这两个色块
下面介绍一下各个步骤在Qt中的实现
获得游戏区域图像
不得不说Qt很方便,获得指定区域图像的代码仅一行而已,其中qs
,qe
都是QPoint
类型,分别代表图像左上角和右下角在桌面上的位置。为了方便,我这里是写死的,因为BlueStack每次启动的初始化坐标都是一样的,每次游戏区域在我PC上的坐标都是一样的。1
2
3
4
5x=qs.x();
y=qs.y();
w=qe.x()-x;
h=qe.y()-y;
pix = QGuiApplication::primaryScreen()->grabWindow(QApplication::desktop()->winId(),x,y,w,h);
截取的图片部分如下所示,可以看到,是一个7*7的矩阵。(并没有真的截图存下来,只是放在内存里而已)
识别色块
Qt中使用QImage
类型可以方便的对色素点操作,而之前获得的图像是QPixmap
类型,先进行转换。
因为实际上每个色块的边长不一定是等长整数,而且前面获得的左上角坐标也不一定准确,所以很难根据某个点的色素就判断是否是同一个色快。我这里对每个大块中取一个相对位置相同小块色素的R、G、B值进行平均,并且只要误差在一定范围内就认为是相同的色块。最后将相同的色块标记为同一个数字,存在col数组中。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
34void ImageAna::initimage(){
QImage img = pix.toImage();
memset(col, -1, sizeof col);
int wd = w/7;
int rgb[50][3];
int rgbs= 0;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 7; j++) {
int qr = 0, qg = 0, qb = 0;
for (int k1 = 0;k1 < 5; k1++) {
for (int k2 = 0; k2 < 5; k2++) {
int RGB = img.pixel(wd*i+20+k1, wd*j+20+k2);
qr += qRed(RGB);
qg += qGreen(RGB);
qb += qBlue(RGB);
}
}
qr /= 25, qg /= 25, qb /= 25;
//qDebug()<<i<<" "<<j<<" "<<qr<<" "<<qg<<" "<<qb;
int k = 0;
for (k = 0; k < rgbs; k++) {
if (abs(rgb[k][0]-qr)<=20&&abs(rgb[k][1]-qg)<=20&&abs(rgb[k][2]-qb)<=20)
break;
}
if (k == rgbs) {
rgb[k][0] = qr, rgb[k][1] = qg, rgb[k][2] = qb;
rgbs++;
}
col[j][i] = k;
}
}
}
找出可交换色块
这一块的代码没有什么好说的,就是暴力找就行了,复杂度也没有多高。做了一点小优化,就是会依次从四个角进行查找,这是因为如果刚从左上消了一个块,这时候还处于动画阶段,再从左上去搜可能会出现错误的识别,另外这也是为了尽可能的将能消的块都消除掉。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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118int ImageAna::dir = 0;
bool ImageAna::calswap(){
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
int tmp;
dir++;
if (dir == 4) dir = 0;
if (dir == 0) {
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 7; j++) {
for (int k = 0; k < 4; k++) {
int ii = i + dx[k];
int jj = j + dy[k];
if (ii < 0 || ii >= 7 || jj < 0 || jj >= 7) continue;
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
if (comp(i, j) || comp(ii, jj)) {
swap1 = QPoint(i, j);
swap2 = QPoint(ii, jj);
return true;
}
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
}
}
}
} else if (dir == 1){
for (int i = 6; i >= 0; i--) {
for (int j = 6; j >= 0; j--) {
for (int k = 0; k < 4; k++) {
int ii = i + dx[k];
int jj = j + dy[k];
if (ii < 0 || ii >= 7 || jj < 0 || jj >= 7) continue;
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
if (comp(i, j) || comp(ii, jj)) {
swap1 = QPoint(i, j);
swap2 = QPoint(ii, jj);
return true;
}
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
}
}
}
} else if (dir == 2) {
for (int j = 0; j < 7; j++) {
for (int i = 6; i >= 0; i--) {
for (int k = 0; k < 4; k++) {
int ii = i + dx[k];
int jj = j + dy[k];
if (ii < 0 || ii >= 7 || jj < 0 || jj >= 7) continue;
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
if (comp(i, j) || comp(ii, jj)) {
swap1 = QPoint(i, j);
swap2 = QPoint(ii, jj);
return true;
}
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
}
}
}
} else if (dir == 3) {
for (int j = 6; j >= 0; j--) {
for (int i = 0; i < 7; i++) {
for (int k = 0; k < 4; k++) {
int ii = i + dx[k];
int jj = j + dy[k];
if (ii < 0 || ii >= 7 || jj < 0 || jj >= 7) continue;
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
if (comp(i, j) || comp(ii, jj)) {
swap1 = QPoint(i, j);
swap2 = QPoint(ii, jj);
return true;
}
tmp = col[i][j];
col[i][j] = col[ii][jj];
col[ii][jj] = tmp;
}
}
}
}
return false;
}
bool ImageAna::comp(int i ,int j) {
if (j>0&&j<6 && col[i][j] == col[i][j-1] && col[i][j] == col[i][j+1]) return true;
if (j>1 && col[i][j] == col[i][j-2] && col[i][j] == col[i][j-1]) return true;
if (j<5 && col[i][j] == col[i][j+2] && col[i][j] == col[i][j+1]) return true;
if (i>0&&i<6 && col[i][j] == col[i-1][j] && col[i][j] == col[i+1][j]) return true;
if (i>1 && col[i][j] == col[i-2][j] && col[i][j] == col[i-1][j]) return true;
if (i<5 && col[i][j] == col[i+2][j] && col[i][j] == col[i+1][j]) return true;
return false;
}
控制鼠标点击
调用WindowsAPI来控制鼠标点击相应的坐标,注意API中的坐标是以整个屏幕长宽都是65536的,要将对应的像素点坐标进行转换。中间加sleep是为了给系统响应时间(太快了爱消除有时候会判游戏无效。。)。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
29void ImageAna::clickit(int xx, int yy) {
int xxx = xx, yyy = yy;
xx = x + w/7 * yyy + 30;
yy = y + w/7 * xxx + 30;
int msw = 65536;
int scw = QApplication::desktop()->width();
int sch = QApplication::desktop()->height();
xx = (int)(xx * msw * 1.0/ scw);
yy = (int)(yy * msw * 1.0/ sch);
mouse_event(
MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MOVE, // flags specifying various motion/click variants
xx, // horizontal mouse position or position change
yy, // vertical mouse position or position change
0, // amount of wheel movement
0 // 32 bits of application-defined information
);
Sleep(10);
mouse_event(
MOUSEEVENTF_LEFTUP|MOUSEEVENTF_ABSOLUTE, // flags specifying various motion/click variants
xx, // horizontal mouse position or position change
yy, // vertical mouse position or position change
0, // amount of wheel movement
0 // 32 bits of application-defined information
);
Sleep(5);
}
游戏时间控制
我比较懒,没有写中断代码,我这边的PC每分钟大概能进行1000次交换操作,所以我就一个for循环写死了,基本是游戏结束差不多几秒钟程序就结束了。
OVER
总的来说,这个程序还是十分简单的,感兴趣的同学无聊的时候也可以取写一个。具体能打多少分跟PC的配置有些关系,如果连BlueStack模拟器跑起来都卡的话。。。我这边不用道具一般在180W左右,用了道具一般都是200W+吧,另外友情提示一下,如果不幸打到250W的话,会被Tencent清空等级和金钱的(我就这么悲剧了一次,之后就不用道具了….=_=)。
最后,健康游戏,远离外挂。。(对那周被我抢走Rank1的DY童鞋表示道歉。。。)
最后的最后,附上顺便录的视频,230W的,勿喷。。