用python玩微信跳一跳游戏
前言
本篇博客介绍了如何使用python代码玩微信跳一跳游戏。很早之前就听说过使用python脚本玩跳一跳游戏刷分,当时对相关原理一窍不通,但这完全不影响我的好奇心和求知欲。如今终于有机会探求其中的奥妙。
百度上一搜python、跳一跳关键词,就弹出不知多少相关教程。但我发现很多代码用的方法似乎有一定缺陷,在遇到一些情况时就无法起作用了。而这些情况在游戏过程中不可避免的会出现,这就导致最终的分数可能并不会很高。
本篇博客综合多方面的idea,主要参考此源码(下提到此源码一词,即指代此处),基于自己的理解做了一些修改和简化。原文中分别给出了在 Android 和 iOS 下运行的代码,和多种不同思路的方法。此处仅介绍Android下的一种方法。
环境配置要求
List of devices attached
6934dc33 device
代码实现
分析
- 在本项目中,adb工具的作用是将游戏截图发送到电脑端、模拟手指按压使棋子跳跃。adb工具的使用在本项目中不起关键作用,即使不了解adb工具也可完成本项目。(博主就完全不了解adb命令)
- 电脑依据游戏截图,识别棋子所处位置和下一步落点中心位置(称棋盘),计算两者距离,再计算按压时间。最后执行相应的adb命令,手机端棋子即会进行跳跃。
显然,本项目的关键点有三个:
- 识别棋子所处位置
- 识别棋盘中心位置
- 计算按压时间
识别棋子所处位置
我使用的方法是利用opencv中的模板匹配函数cv2.matchTemlpate()
,只需要提供一张棋子的图像即可,准确度非常高。左一图为棋子图像,左二图为识别结果。
棋子位置已经有了,但我们要的是棋子所处的坐标点。取方框X方向的中点为X坐标,取方框Y方向 9/10 处为Y坐标。棋子所处的坐标点就是(X, Y),如右一图。
识别棋盘中心位置
这部分比较复杂,我参照了此源码的实现方法,但二者又有明显不同。
据观察,我们认为,在Y方向上(竖直方向)棋盘出现的位置在1/3 - 2/3处之间。而在X方向上(水平方向),如果棋子在屏幕偏左方,棋盘必在棋子的右方;如果棋子在屏幕偏右方,棋盘必在棋子的左方。那么我们搜索的范围就大大缩小,如下图红框(此时棋子在屏幕偏左方):
寻找上顶点
我们先找到棋盘的上顶点。
显然,棋盘上顶点颜色与背景色差异较大,可依据这一特点在区域内搜索。从红框左上角像素开始逐行搜索,取每行最左端点为初始点记录像素值,与该行内的每个像素进行比对,如果二者差异较大,就认为找到了上顶点。
为提高搜索速度,可以每50行取一行搜索,如果出现差异较大的点,在重新搜索这50行,取第一个差异点为上顶点。
这种方法在棋盘是菱形时是有效的,但棋盘是椭圆形时是否有效呢?你可能认为也有效,因为椭圆形也只具有一个上顶点。实际上,我发现椭圆形棋盘在截图中没有上顶点,最上面是一条短线(把截图放大也能发现)。因此,在用上面的方法时,得到的上顶点会是这条短线的最左端的点。
上面的方法是,在行内找到一个差异点就终止搜索,认为这个差异点是上顶点。我们修改一下,在行内找差异点后继续搜索该行,并记录差异点,知道该行搜索完毕终止搜索。把记录的所有差异点的取平均值,就能得到短线的中点,即椭圆的上顶点。
寻找中心点
好了,我们找到棋盘的上顶点了,无论它是椭圆形还是菱形,结果都是准确的。
但如何寻找棋盘的中心点?我也不知道。起初我的想法是,找到棋盘的上顶点和右端点,二者分别做个辅助线交点就是棋盘中心点了。于是我用类似寻找上顶点的方法,寻找右端点,从右向左逐列搜索,没错是找到了。但我发现,少部分情况下会出问题。当棋盘位于屏幕偏右方,结果完全准确;当棋盘位于屏幕偏左方,因为我们限制了搜索范围(只搜索棋子的左方),结果大部分情况下也是准确的。什么时候不准确呢?当棋子和棋盘靠的很近,而棋盘又比较大的时候,棋盘的右端点被棋子遮住了…这我还搜索个啥?虽然这种情况很少出现,但不得不说他不是种好方法。
那搜索棋盘左端点行不行?也不行。当棋盘位于屏幕偏右方,棋盘左端点也有被遮住的可能。
这个时候,此源码使用的方法就比较高端,出现上述情况时也能准确计算出中心点。下面我讲解一下。
得到了棋盘的上顶点坐标,它的X坐标就是棋盘中心点的X坐标,那么只要计算出Y坐标。我理解这位牛人的意思应该是,游戏有一个对称中心,并且该对称中心到棋盘中心的连线与水平方向的夹角是固定的。你可能认为对称中心是屏幕的中点,但这位牛人是这样计算的:
center_x = w / 2 + (24 / screen_width) * w
center_y = h / 2 + (17 / screen_height) * h
给出的角度是tan θ = 25.5 / 43.5 。
咋来的?不知道,真不知道。算了,先用吧。
既然知道了这个规律,那么我们就可以这样计算出棋盘中心点的坐标(X, Y ):
这种规律到底是不是存在的?我们可以验证一下,下面是我调试代码时得到的一些图像:
仅用肉眼来看的话,我们几乎可以认为两个棋盘中心点的连线是平行的(此时棋子处于中心点)。而大量事实也证明了,牛人给出的规律是有效的,计算出的中心点位置很准确。
计算按压时间
好了,我们终于得到了棋子起始坐标点、棋盘中心点坐标,那么就可以计算出二者的直线距离了。然后根据直线距离,计算按压时间。
说实话,这位大牛计算按压时间的代码,我实在没看懂…不过我猜测,也许是考虑不同手机屏幕分辨率适配等等问题。大家有兴趣可以看一下。
我就比较粗暴,勾股定理计算直线距离,直线距离乘以时间系数等于按压时间(我这种方法的效果似乎也不差)。这里的时间系数,是大牛源码里提到的。似乎是不同屏幕大小的手机时间系数略有不同,此源码里已经给了配置文件,我也在我的源码里放了,略微做了些修改。
最终效果
最终效果比较依赖于配置文件中的数据,尤其是按压时间系数,大家可以根据自己的实际情况进行修改。
我的手机是Huawei nova 2s,屏幕大小是2160x1080,实际分数随随便便能够达到三四千分,最后一次游戏的分数是8000多分。欢迎大家在评论区晒出自己的分数哦!
源码奉上
使用说明
- 安卓手机通过数据线与电脑相连,打开手机的USB调试
- 电脑端执行
adb devices
,确保手机已连接 - config 文件夹下有对应于手机分辨率的配置文件。可以将config.json文件复制到项目根目录下,代码会优先读取;也可不复制,代码自动在config文件夹下搜索匹配的配置文件;实际效果不好时,可手动调整配置文件。
- 手机界面切换到微信跳一跳游戏,点击开始游戏
- 执行
python wechat_jump_auto.py
总结
特别鸣谢这位大牛,没有ta本篇博客无法顺利完成。本篇博客是博主基于自身理解写成,难免出现错误,望各位批评指正。无论是建议、想法和疑问,各位皆可在评论区提出,集思广益,互帮互助。
欢迎访问我的个人博客