forked from Erimus-Koo/autoplay_wechat_jump_game
-
Notifications
You must be signed in to change notification settings - Fork 0
/
wechat_jump_auto_slim.py
199 lines (167 loc) · 6.78 KB
/
wechat_jump_auto_slim.py
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = 'Erimus'
'''
这个是精简版本,只取x轴距离。
可以适配任意屏幕。
把磁盘读写截图改为内存读写。
可以防止被ban(从抓包数据看没有返回Error)。
'''
import os,sys,subprocess,time,random
from PIL import Image,ImageDraw
from io import BytesIO
# ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
screenshot_way = 2
# 检查获取截图的方式
def check_screenshot():
global screenshot_way
if (screenshot_way < 0):
print('暂不支持当前设备')
sys.exit()
binary_screenshot = pull_screenshot()
try:
im = Image.open(BytesIO(binary_screenshot)).load() #直接使用内存IO 不需要写入硬盘
print('Capture Method: {}'.format(screenshot_way))
except Exception:
screenshot_way -= 1
check_screenshot()
# 新的方法请根据效率及适用性由高到低排序
def pull_screenshot():
global screenshot_way
if screenshot_way in [1,2]:
process = subprocess.Popen('adb shell screencap -p', shell=True, stdout=subprocess.PIPE)
screenshot = process.stdout.read()
if screenshot_way == 2:
binary_screenshot = screenshot.replace(b'\r\n', b'\n')
else:
binary_screenshot = screenshot.replace(b'\r\r\n', b'\n')
return binary_screenshot
elif screenshot_way == 0:
os.system('adb shell screencap -p /sdcard/autojump.png')
os.system('adb pull /sdcard/autojump.png .')
# ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
# 寻找起点和终点坐标
def find_piece_and_board(im):
w, h = im.size #图片宽高
im_pixel = im.load()
def find_piece(pixel): #棋子取色精确范围
return (40 < pixel[0] < 65) and (40 < pixel[1] < 65) and (80 < pixel[2] < 105)
# 寻找棋子 ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
# 粗查棋子位置
piece_found,piece_fx,piece_fy = 0,0,0
scan_piece_unit = w//40 #间隔单位
ny = (h+w)//2 #寻找下限 从画面中央的正方形的下缘开始
while ny>(h-w)//2 and not piece_found:
ny -= scan_piece_unit
for nx in range(0,w,scan_piece_unit):
pixel = im_pixel[nx,ny]
if find_piece(pixel):
piece_fx,piece_fy = nx,ny
piece_found = True
break
print('%-12s %s,%s'%('piece_fuzzy:',piece_fx,piece_fy))
if not piece_fx: return 0, 0 #没找到棋子
# 精查棋子位置
piece_x,piece_x_set = 0,[] #棋子x/棋子坐标集合
piece_width = w//14 #估算棋子宽度
piece_height = w//5 #估算棋子高度
for ny in range(piece_fy+scan_piece_unit,piece_fy-piece_height,-4):
for nx in range(max(piece_fx-piece_width,0),min(piece_fx+piece_width,w)):
pixel = im_pixel[nx,ny]
# print(nx,ny,pixel)
if find_piece(pixel):
piece_x_set.append(nx)
if len(piece_x_set)>10:
piece_x = sum(piece_x_set)/len(piece_x_set)
break
print('%-12s %s'%('p_exact_x:',piece_x))
# 寻找落点 ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
board_x = 0
# 限制棋盘扫描的横坐标,避免音符 bug
if piece_x < w/2:
board_x_start,board_x_end = round(piece_x),w
else:
board_x_start,board_x_end = 0,round(piece_x)
# 寻找落点顶点
board_x_set = [] #目标坐标集合/改为list避免去重
for by in range((h-w)//2,(h+w)//2,4):
bg_pixel = im_pixel[0, by]
for bx in range(board_x_start, board_x_end):
pixel = im_pixel[bx, by]
# 修掉脑袋比下一个小格子还高的情况 屏蔽小人左右的范围
if abs(bx - piece_x) < piece_width: continue
# 修掉圆顶的时候一条线导致的小 bug,这个颜色判断应该 OK,暂时不提出来
if abs(pixel[0]-bg_pixel[0]) + abs(pixel[1]-bg_pixel[1]) + abs(pixel[2]-bg_pixel[2]) > 10:
board_x_set.append(bx)
if len(board_x_set)>10:
board_x = sum(board_x_set)/len(board_x_set)
print('%-12s %s'%('target_x:',board_x))
break #找到了退出
return piece_x, board_x
# ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
# 重设点击位置 再来一局位置
# 其实这个界面,应该是标准的9:16,然后fit当前窗体(600:800测试结果)。长窄屏已经特殊处理。
def set_button_position(im,gameover=0):
w, h = im.size
if h//16>w//9+2: #长窄屏 2px容差 获取ui描绘的高度
uih = int(w/9*16)
else:
uih = h
uiw = int(uih/16*9)
# 如果游戏结束 点击再来一局
left = int(w/2) #按钮半宽约uiw//5
top = int((h-uih)/2+uih*0.825) #根据9:16实测按钮高度中心0.825 按钮半高约uiw//28
if gameover: return left,top
# 游戏中点击 随机位置防 ban
left = random.randint(w//4,w-20) #避开左下角按钮
top = random.randint(h*3//4,h-20)
return left, top
# ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
def jump(piece_x,board_x,im,swipe_x1,swipe_y1):
distanceX = abs(board_x-piece_x) #起点到目标的水平距离
shortEdge = min(im.size) #屏幕宽度
jumpPercent = distanceX/shortEdge #跳跃百分比
jumpFullWidth = 1700 #跳过整个宽度 需要按压的毫秒数
press_time = round(jumpFullWidth*jumpPercent) #按压时长
press_time = 0 if not press_time else max(press_time,200) #press_time大于0时限定最小值
print('%-12s %.2f%% (%s/%s) | Press: %sms'%('Distance:',jumpPercent*100,distanceX,shortEdge,press_time))
cmd = 'adb shell input swipe {x1} {y1} {x2} {y2} {duration}'.format(
x1 = swipe_x1,
y1 = swipe_y1,
x2 = swipe_x1+random.randint(-10,10), #模拟位移
y2 = swipe_y1+random.randint(-10,10),
duration = press_time
)
# print(cmd)
os.system(cmd)
# ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
def main():
check_screenshot() #检查截图
count = 0
while True:
count += 1
print('---\n%-12s %s (%s)'%('Times:',count,int(time.time())))
# 获取截图
binary_screenshot = pull_screenshot()
im = Image.open(BytesIO(binary_screenshot))
w,h = im.size
if w>h: im = im.rotate(-90,expand=True) #添加图片方向判断
# print('image | w:%s | h:%s'%(w,h))
# 获取棋子和 board 的位置
piece_x, board_x = find_piece_and_board(im)
gameover = 0 if all((piece_x,board_x)) else 1
swipe_x1,swipe_y1 = set_button_position(im,gameover=gameover) #随机点击位置
# 标注截图并显示
# draw = ImageDraw.Draw(im)
# draw.line([piece_x,0,piece_x,h],fill='blue',width=1) #start
# draw.line([board_x,0,board_x,h],fill='red',width=1) #end
# draw.ellipse([swipe_x1-16,swipe_y1-16,swipe_x1+16,swipe_y1+16],fill='red') #click
# im.show()
jump(piece_x,board_x,im,swipe_x1,swipe_y1)
wait = (random.random())**5*9+1 #停1~9秒 指数越高平均间隔越短
print('---\nWait %.3f s...'%wait)
time.sleep(wait)
print('Continue!')
# ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
if __name__ == '__main__':
main()