python玩微信跳一跳_Python 玩微信跳一跳

Python 玩微信跳一跳

wechat_jump

环境:Python, Android SDK

方法:利用Python识别棋子和落点位置,自动执行跳一跳

棋子落稳之后,截图

根据像素 RGB,计算棋子的坐标和下一个落点的坐标

根据两个点的距离乘以一个时间系数获得长按的时间

识别棋子:通过棋子底部(大概是一条直线)颜色识别位置

识别落点:根据背景色和落点的色差识别

文件下载:

与手机相关函数

函数

说明

get_device_id()

获取手机ID

get_screen_size()

获取手机屏幕大小

pull_screenshot()

获取屏幕截图

check_screenshot()

检查获取截图的方式

def get_device_id():

"""获取手机ID,若连接多个手机,按1/2/3...选择"""

devices_list = os.popen('adb devices').read().split('\n')

devices = []

for loop in range(1, len(devices_list)):

if devices_list[loop]:

devices.append(devices_list[loop].split('\t')[0])

if 0 == len(devices):

print('\r未找到设备!')

return(-1)

elif 1 == len(devices):

return(devices[0])

else:

prompt = ''

for num in range(len(devices)):

prompt += '%d. %s\n' % (num+1, devices[num])

prompt += '请选择设备: '

select = input(prompt)

return(devices[int(select)-1])

def get_screen_size(device_id):

"""获取手机屏幕大小"""

size_str = os.popen('adb -s %s shell wm size' % device_id).read()

if not size_str:

print('\r获取屏幕分辨率失败!')

sys.exit()

m = re.search(r'(\d+)x(\d+)', size_str)

width, height = m.group(1), m.group(2)

return(width, height)

def pull_screenshot(device_id, image_phone_path, image_pc_path):

"""获取屏幕截图,目前有 0 1 2 3 四种方法

添加新的平台监测方法时,可根据效率及适用性由高到低排序"""

global SCREENSHOT_WAY

if 1 <= SCREENSHOT_WAY <= 3:

process = subprocess.Popen('adb -s %s shell screencap -p' % device_id, \

shell=True, \

stdout=subprocess.PIPE)

binary_screenshot = process.stdout.read()

if SCREENSHOT_WAY == 2:

binary_screenshot = binary_screenshot.replace(b'\r\n', b'\n')

elif SCREENSHOT_WAY == 1:

binary_screenshot = binary_screenshot.replace(b'\r\r\n', b'\n')

fp = open(image_pc_path, 'wb')

fp.write(binary_screenshot)

fp.close()

elif SCREENSHOT_WAY == 0:

os.system('adb -s %s shell screencap -p %s' % (device_id, image_phone_path))

os.system("adb -s %s pull %s %s" % \

(device_id, image_phone_path, image_pc_path))

def check_screenshot(device_id, image_phone_path, image_pc_path):

"""检查获取截图的方式"""

global SCREENSHOT_WAY

if os.path.isfile(image_pc_path):

try:

os.remove(image_pc_path)

except Exception:

pass

if SCREENSHOT_WAY < 0:

print('\r暂不支持当前设备')

sys.exit()

pull_screenshot(device_id, image_phone_path, image_pc_path)

try:

Image.open(image_pc_path)

print('\r采用方式 %d 获取截图' % SCREENSHOT_WAY)

except Exception:

SCREENSHOT_WAY -= 1

check_screenshot(device_id, image_phone_path, image_pc_path)

获取设置

使用 ConfigParser 读取配置文件参数

def get_settings(config_file, device_id):

config = ConfigParser.ConfigParser()

config.read(config_file)

width, height = get_screen_size(device_id)

screen_size = '%s-%s' % (width, height)

settings = {}

settings['man_color'] = config.get('game', 'man_color').split(';')

settings['man_color'] = list(map(int, settings['man_color']))

settings['next_center_rgb'] = config.get('game', 'next_center_rgb').split(';')

settings['next_center_rgb'] = list(map(int, settings['next_center_rgb']))

settings['next_center_offset'] = config.getint('game', 'next_center_offset')

settings['pixel_offset'] = config.getint('game', 'pixel_offset')

settings['side_scale'] = config.getint('game', 'side_scale')

settings['board_color_offset'] = config.getint('game', 'board_color_offset')

settings['press_min_time'] = config.getint('game', 'press_min_time')

settings['screenshot_way'] = config.getint('game', 'screenshot_way')

settings['image_phone_path'] = config.get('game', 'image_phone_path')

settings['image_pc_path'] = config.get('game', 'image_pc_path')

settings['press_coefficient'] = config.getfloat(screen_size, 'press_coefficient')

settings['man_base_height'] = config.getfloat(screen_size, 'man_base_height')

settings['man_body_width'] = config.getfloat(screen_size, 'man_body_width')

settings['target_height'] = config.getint('game', 'target_height')

return(settings)

默认配置文件:settings.conf

通用配置

说明

man_color

棋子底座颜色范围,默认50;60;53;63;95;110表示R: 50-60, G: 53-63, B: 95-110

pixel_offset

纵向探测步长,默认50

side_scale

两侧空隙占比,默认8

board_color_offset

颜色对比差值,默认10

next_center_rgb

如果上一跳命中中间,则下个目标中心会出现 RGB(245, 245, 245) 的点,默认245;245;245

next_center_offset

下个目标中心偏差范围,默认200

press_min_time

最小的按压时间,默认200ms

target_height

落点目标的最大高度,取开局时最大的方块的上下顶点距离,默认274

screenshot_way

截图方式,默认3,不要修改

image_phone_path

手机端截图存放路径,默认/sdcard/Download/screenshot.png

image_pc_path

PC端截图存放路径,默认./screenshot.png

[game]

man_color = 50;60;53;63;95;110

pixel_offset = 50

side_scale = 8

board_color_offset = 10

next_center_rgb = 245;245;245

next_center_offset = 200

press_min_time = 200

target_height = 274

screenshot_way = 3

image_phone_path = /sdcard/Download/screenshot.png

image_pc_path = ./screenshot.png

分辨率:宽-长

说明

press_coefficient

长按的时间系数

man_base_height

棋子底座高度

man_body_width

棋子的宽度

[540-960]

press_coefficient = 2.732

man_base_height = 40

man_body_width = 70

[720-1280]

press_coefficient = 2.099

man_base_height = 26

man_body_width = 47

[720-1440]

press_coefficient = 2.099

man_base_height = 26

man_body_width = 47

[1080-1920]

press_coefficient = 1.392

man_base_height = 40

man_body_width = 70

[1080-2160]

press_coefficient = 1.372

man_base_height = 50

man_body_width = 85

[1440-2560]

press_coefficient = 1.475

man_base_height = 56

man_body_width = 110

游戏相关

函数

说明

calc_man_board_position()

计算棋子以及落点坐标

set_button_position()

点击的位置以及“再来一局”按钮的位置

jump()

跳跃一定的距离

def calc_man_board_position(image, settings):

"""计算棋子以及落点坐标"""

width, height = image.size

image_pixel = image.load() # 图像像素点RGB集合

top_space = 0 # 棋子或下一步落点顶部空白高度,起始 y 坐标

side_space = int(float(width) / settings['side_scale']) # 棋子左右两侧空白宽度

man_x_sum = 0 # 棋子底部 x 坐标总和

man_x_count = 0 # 棋子底部 x 坐标个数

man_y_max = 0 # 棋子底部 y 坐标最大值

board_x = 0 # 落点 x 坐标

board_y = 0 # 落点 y 坐标

##################################################

# 计算棋子坐标

##################################################

# 纵向探测屏幕中间 1/3 处

# 计算棋子或下一步落点顶部空白高度

for y in range(int(height / 3), int(height*2 / 3), settings['pixel_offset']):

last_pixel = image_pixel[0, y] # 记录最左侧像素点 RGB

for x in range(1, width):

pixel = image_pixel[x, y] # 获取像素点 RGB

# 与最左侧像素点 RGB 不一致

# 找到棋子或下一步落点最顶部的位置

if pixel != last_pixel:

top_space = y - settings['pixel_offset']

break

if top_space:

break

# 纵向探测屏幕中间 1/3 处剩下的部分,计算棋子坐标

for y in range(top_space, int(height * 2 / 3)):

for x in range(side_space, width - side_space):

pixel = image_pixel[x, y] # 获取像素点 RGB

# 根据棋子的最低行的颜色,找棋子底部那些点的平均值

# 这个颜色这样应该 OK,暂时不提出来

if (settings['man_color'][0] < pixel[0] < settings['man_color'][1]) and \

(settings['man_color'][2] < pixel[1] < settings['man_color'][3]) and \

(settings['man_color'][4] < pixel[2] < settings['man_color'][5]):

man_x_sum += x

man_x_count += 1

man_y_max = max(y, man_y_max)

if not all((man_x_sum, man_x_count)):

return(0, 0, 0, 0)

# 棋子坐标

man_x = int(man_x_sum / man_x_count)

# 上移棋子底盘高度的一半

man_y = man_y_max - settings['man_base_height'] / 2

##################################################

# 计算落点坐标

##################################################

# 限制落点扫描的横坐标,避免音符 bug

if man_x < width/2:

board_x_start = man_x

board_x_end = width

else:

board_x_start = 0

board_x_end = man_x

for y in range(int(height / 3), int(height * 2 / 3)):

last_pixel = image_pixel[0, y] # 记录最左侧像素点 RGB

if board_x or board_y:

break

board_x_sum = 0

board_x_count = 0

for x in range(int(board_x_start), int(board_x_end)):

pixel = image_pixel[x, y] # 获取像素点 RGB

# 修掉脑袋比下一个小格子还高的情况的 bug

if abs(x - man_x) < settings['man_body_width']:

continue

# 若像素点 RGB 与最左侧像素点 RGB 偏差大于 board_color_offset,则认定为边界

# 修掉圆顶的时候一条线导致的小 bug,这个颜色判断应该 OK,暂时不提出来

if abs(pixel[0] - last_pixel[0]) \

+ abs(pixel[1] - last_pixel[1]) \

+ abs(pixel[2] - last_pixel[2]) > settings['board_color_offset']:

board_x_sum += x

board_x_count += 1

if board_x_sum:

board_x = board_x_sum / board_x_count

last_pixel = image_pixel[board_x, y]

# 从上顶点往下 +target_height 的位置开始向上找颜色与上顶点一样的点,为下顶点

# 该方法对所有纯色平面和部分非纯色平面有效

# 对高尔夫草坪面、木纹桌面、药瓶和非菱形的碟机(好像是)会判断错误

for k in range(y+settings['target_height'], y, -1):

pixel = image_pixel[board_x, k]

if abs(pixel[0] - last_pixel[0]) + abs(pixel[1] - last_pixel[1]) + abs(pixel[2] - last_pixel[2]) < settings['board_color_offset']:

break

board_y = int((y+k) / 2)

# 如果上一跳命中中间,则下个目标中心会出现 RGB(245, 245, 245) 的点

# 利用这个属性弥补上一段代码可能存在的判断错误

# 若上一跳由于某种原因没有跳到正中间,而下一跳恰好有无法正确识别花纹

# 则有可能游戏失败,由于花纹面积通常比较大,失败概率较低

for k in range(y, y+settings['next_center_offset']):

pixel = image_pixel[board_x, k]

if abs(pixel[0] - settings['next_center_rgb'][0]) + \

abs(pixel[1] - settings['next_center_rgb'][1]) + \

abs(pixel[2] - settings['next_center_rgb'][2]) == 0:

board_y = k + settings['board_color_offset']

break

if not all((board_x, board_y)):

return(0, 0, 0, 0)

return(man_x, man_y, board_x, board_y)

def set_button_position(image):

"""“再来一局”位置"""

width, height = image.size

left = int(width / 2)

top = int(1584 * (height / 1920.0))

left = int(random.uniform(left-50, left+50))

top = int(random.uniform(top-10, top+10)) # 随机防 ban

return(left, top, left, top)

def jump(distance, press_position, press_coefficient):

"""跳跃一定的距离"""

press_time = distance * press_coefficient

press_time = max(press_time, settings['press_min_time'])

press_time = int(press_time)

cmd = 'adb -s %s shell input swipe %s %s %s %s %s' % \

(device_id, \

press_position[0], press_position[1], \

press_position[2], press_position[3], \

press_time)

os.system(cmd)

return(press_time)

主函数

# -*- coding: utf-8 -*-

import os, sys, re, ConfigParser, subprocess, math, random, time

from PIL import Image

def main(device_id, settings):

check_screenshot(device_id, \

settings['image_phone_path'], \

settings['image_pc_path'])

count = 0

next_rest = random.randrange(3, 10)

next_rest_time = random.randrange(5, 10)

while True:

pull_screenshot(device_id, \

settings['image_phone_path'], \

settings['image_pc_path'])

image = Image.open(settings['image_pc_path'])

# 获取棋子和落点的位置

man_x, man_y, board_x, board_y = calc_man_board_position(image, settings)

# 计算距离

distance = math.sqrt((board_x - man_x) ** 2 + (board_y - man_y) ** 2)

# 点击位置的坐标

press_position = set_button_position(image)

press_time = jump(distance, press_position, settings['press_coefficient'])

print('%.2f, %dms: (%d, %d) -> (%d, %d)' % (distance, press_time, man_x, man_y, board_x, board_y))

image.close()

count += 1

if count == next_rest:

print('\r已经连续打了 %s 下,休息 %ss' % (count, next_rest_time))

for i in range(next_rest_time):

sys.stdout.write('\r程序将在 %ds 后继续' % (next_rest_time - i))

sys.stdout.flush()

time.sleep(1)

print('\n继续')

count = 0

next_rest = random.randrange(30, 100)

next_rest_time = random.randrange(10, 20)

# 为了保证截图的时候应落稳了,多延迟一会儿

# 随机值防 ban

time.sleep(random.uniform(0.9, 1.2))

if '__main__' == __name__:

global SCREENSHOT_WAY

if 1 == len(sys.argv):

config_file = './settings.conf'

else:

config_file = sys.argv[1]

if not os.path.isfile(config_file):

print('\r配置文件不存在!')

sys.exit(-1)

device_id = get_device_id()

if -1 == device_id:

print('\r未找到设备!')

sys.exit(-1)

settings = get_settings(config_file, device_id)

SCREENSHOT_WAY = settings['screenshot_way']

main(device_id, settings)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值