微信跳一跳之线性回归算法优化
最近微信上线了一款小应用---“跳一跳”,这个规则简单,让人上瘾的小游戏和2048一样魔性,朋友圈也是各路小伙伴各显神通:硬件流(树莓派+步进电机)、日天流(篡改http请求)、软件流(adb控制手机模拟点击)。
今天我们也来实践下,当然选择最顺手的Python来搞咯,直接找到开源项目wechat_jump_game (opens new window)进行优化改造。此项目有个pull request[优化]跑分17000+ 新增AI模块,机器人自主学习生成跳跃公式 (opens new window),看到AI我们就来了兴趣,只见过理论,还没有实践过,可以拿这个实践下。
这个pull request介绍如下:
机器人精确采集跳跃结果并自主学习,使用线性回归方法 拟合出最优 [按压时间]->[弹跳距离] 线性公式 Y =kX + b 本优化无需修改config文件,可以适配所有手机,经过十次以上跳跃学习,机器人即可 模拟出相对稳定的线性公式。随着采集结果越多,跳跃也越精确,后期基本连续命中靶心。 理论上只要目标点获取无误,会一直跳下去。
工作两年多,一直在做服务端后台应用相关的测试,没接触过移动端测试呢,正好趁这次机会学习下怎么通过代码自动化控制安卓手机。 下面来动手试一下,找出下岗多年的MX3,充电开机。
# 第一次调试
- 安装好adb,配置好环境变量。
- 手机打开开发者模式,连接PC。
- 命令行测试是否连接成功: adb devices,手机弹出是否信任窗口,点击确定,已经链接成功。
- 测试一些adb命令是否正常: adb shell wm size,返回信息:Physical size: 1080x1800,完美。
- 通过virtualenv建立虚拟环境,安装项目所需的第三方库。
- 手机微信打开跳一跳,点击开始游戏。
- 运行wechat_jump_auto_ai.py,报错T_T...
查看代码发现是截图部分操作不适配MX3,手动修改代码后成功截图运行
将修改为
screenshot = screenshot.replace(b'\r\n', b'\n')1screenshot = screenshot.replace(b'\r\r\n', b'\n')1
# 第二次调试
按照程序逻辑,运行十次之后即可采用线性回归算法学习得到的公式,根据已知距离得出按压时间,但实际结果却和一个弱智一样,2分就挂掉了... 查看代码发现有个魔法数字要自己设置,程序根据这个数字进行截图计算误差:time.sleep(0.2)。
# 在跳跃落下的瞬间 摄像机移动前截图 这个参数要自己校调
time.sleep(0.2)
pull_screenshot_temp()
im_temp = Image.open('./autojump_temp.png')
temp_piece_x, temp_piece_y = find_piece(im_temp)
debug.computing_error(press_time, board_x, board_y, piece_x, piece_y, temp_piece_x, temp_piece_y)
2
3
4
5
6
经过debug截图不断调整,得出我的PC和MX3配合的最佳数值是0.04。 删除学习数据jump_range.csv,重新开始训练,程序终于能磕磕绊绊达到400分左右,但是大概需要1小时左右,对学习数据通过pandas和matplotlib进行绘图,看到训练采集到的数据离散程度很高,明显学习效果不佳。
# 第三次调试(重点)
再次阅读代码,发现这个AI版本的代码有瑕疵:
def computing_error(last_press_time, target_board_x, target_board_y, last_piece_x, last_piece_y, temp_piece_x, temp_piece_y):
"""
计算跳跃实际误差
"""
target_distance = math.sqrt(abs(target_board_x - last_piece_x) ** 2 + abs(target_board_y - last_piece_y) ** 2) # 上一轮目标跳跃距离
actual_distance = math.sqrt(abs(temp_piece_x - last_piece_x) ** 2 + abs(temp_piece_y - last_piece_y) ** 2) # 上一轮实际跳跃距离
jump_error_value = math.sqrt(abs(target_board_x - temp_piece_x) ** 2 + abs(target_board_y - temp_piece_y) ** 2) # 跳跃误差
print "目标距离: {0}, 实际距离: {1}, 误差距离: {2}, 蓄力时间: {3}ms".format(round(target_distance), round(actual_distance), round(jump_error_value), round(last_press_time))
# 将结果采集进学习字典
if last_piece_x > 0 and last_press_time > 0 :
ai.add_data(round(actual_distance, 2), round(last_press_time))
2
3
4
5
6
7
8
9
10
11
12
13
# 问题1
target_distance与actual_distance有可能会等于0.0或者一个超出实际范围的值,这些值会在游戏失败重新开始时出现,原程序没有进行过滤;
# 问题2
jump_error_value作为一个很重要的值,没有进行有效利用。
上述两个问题会导致数据质量不高,干扰项太多,目标函数拟合速度太慢。
因此改进数据采集策略:
if jump_error_value < 5 and last_piece_x > 0 and last_press_time > 0 and target_distance > 0 and actual_distance > 0:
ai.add_data(round(actual_distance, 2), round(last_press_time))
2
# 改进1
要求target_distance与actual_distance必须要大于0才是有效值,剔除干扰项;
# 改进2
jump_error_value合理利用,开始时将此参数放大到50左右,后续根据学习效果,逐步缩小jump_error_value的阈值,提高精确度,加速训练效果。
# 效果展示
# 训练数据拟合函数
经过大概四五轮训练,采样到571条数据,拟合出线性回归函数如图

# debug误差效果
运行debug截图, 基本完美命中目标

运行debug日志,误差基本控制在40以内

# 自动运行视频
# 成绩
因为MX3手机硬件有问题,电量低于75%就会出现屏幕抖动,所以目前成绩如此

# 项目地址
优化后的代码以及训练数据: https://github.com/toddlerya/WechatJumpAI
# 后记
不得不承认,机器学习在合适的领域使用合适方式,达到的效果非常棒!只用了简单的线性回归算法,最经典最基本的机器学习算法,达到的效果已经秒杀人类上千倍。 为了不被这个时代淘汰,一定要跟上节奏,加油~
2018年01月07日 于 南京
Email
GitHub (opens new window)