用python玩微信跳一跳游戏

前言

本篇博客介绍了如何使用python代码玩微信跳一跳游戏。很早之前就听说过使用python脚本玩跳一跳游戏刷分,当时对相关原理一窍不通,但这完全不影响我的好奇心和求知欲。如今终于有机会探求其中的奥妙。

百度上一搜python、跳一跳关键词,就弹出不知多少相关教程。但我发现很多代码用的方法似乎有一定缺陷,在遇到一些情况时就无法起作用了。而这些情况在游戏过程中不可避免的会出现,这就导致最终的分数可能并不会很高。

本篇博客综合多方面的idea,主要参考此源码(下提到此源码一词,即指代此处),基于自己的理解做了一些修改和简化。原文中分别给出了在 Android 和 iOS 下运行的代码,和多种不同思路的方法。此处仅介绍Android下的一种方法。


环境配置要求

  1. 安装python3

  2. 安装ADB,并设置好环境变量,确保在CMD中adb命令可识别。安装完后插入安卓设备,且打开USB调试模式,在终端中输入: adb devices. 显示类似如下表明设备已连接

1
2
List of devices attached
6934dc33 device
  1. 安装其他依赖库:opencv, numpy

代码实现

分析

  1. 在本项目中,adb工具的作用是将游戏截图发送到电脑端、模拟手指按压使棋子跳跃。adb工具的使用在本项目中不起关键作用,即使不了解adb工具也可完成本项目。(博主就完全不了解adb命令)
  2. 电脑依据游戏截图,识别棋子所处位置和下一步落点中心位置(称棋盘),计算两者距离,再计算按压时间。最后执行相应的adb命令,手机端棋子即会进行跳跃。

显然,本项目的关键点有三个:

  1. 识别棋子所处位置
  2. 识别棋盘中心位置
  3. 计算按压时间

识别棋子所处位置

我使用的方法是利用opencv中的模板匹配函数cv2.matchTemlpate(),只需要提供一张棋子的图像即可,准确度非常高。左一图为棋子图像,左二图为识别结果。

棋子

棋子位置已经有了,但我们要的是棋子所处的坐标点。取方框X方向的中点为X坐标,取方框Y方向 9/10 处为Y坐标。棋子所处的坐标点就是(X, Y),如右一图。

识别棋盘中心位置

这部分比较复杂,我参照了此源码的实现方法,但二者又有明显不同。

据观察,我们认为,在Y方向上(竖直方向)棋盘出现的位置在1/3 - 2/3处之间。而在X方向上(水平方向),如果棋子在屏幕偏左方,棋盘必在棋子的右方;如果棋子在屏幕偏右方,棋盘必在棋子的左方。那么我们搜索的范围就大大缩小,如下图红框(此时棋子在屏幕偏左方):

棋盘可能的位置

寻找上顶点

我们先找到棋盘的上顶点。

显然,棋盘上顶点颜色与背景色差异较大,可依据这一特点在区域内搜索。从红框左上角像素开始逐行搜索,取每行最左端点为初始点记录像素值,与该行内的每个像素进行比对,如果二者差异较大,就认为找到了上顶点。

为提高搜索速度,可以每50行取一行搜索,如果出现差异较大的点,在重新搜索这50行,取第一个差异点为上顶点。

这种方法在棋盘是菱形时是有效的,但棋盘是椭圆形时是否有效呢?你可能认为也有效,因为椭圆形也只具有一个上顶点。实际上,我发现椭圆形棋盘在截图中没有上顶点,最上面是一条短线(把截图放大也能发现)。因此,在用上面的方法时,得到的上顶点会是这条短线的最左端的点。

椭圆棋盘的短线

上面的方法是,在行内找到一个差异点就终止搜索,认为这个差异点是上顶点。我们修改一下,在行内找差异点后继续搜索该行,并记录差异点,知道该行搜索完毕终止搜索。把记录的所有差异点的取平均值,就能得到短线的中点,即椭圆的上顶点。

寻找中心点

好了,我们找到棋盘的上顶点了,无论它是椭圆形还是菱形,结果都是准确的。

但如何寻找棋盘的中心点?我也不知道。起初我的想法是,找到棋盘的上顶点和右端点,二者分别做个辅助线交点就是棋盘中心点了。于是我用类似寻找上顶点的方法,寻找右端点,从右向左逐列搜索,没错是找到了。但我发现,少部分情况下会出问题。当棋盘位于屏幕偏右方,结果完全准确;当棋盘位于屏幕偏左方,因为我们限制了搜索范围(只搜索棋子的左方),结果大部分情况下也是准确的。什么时候不准确呢?当棋子和棋盘靠的很近,而棋盘又比较大的时候,棋盘的右端点被棋子遮住了…这我还搜索个啥?虽然这种情况很少出现,但不得不说他不是种好方法。

假装有图,实际上玩了半天游戏没找到好的示意图

那搜索棋盘左端点行不行?也不行。当棋盘位于屏幕偏右方,棋盘左端点也有被遮住的可能。

这个时候,此源码使用的方法就比较高端,出现上述情况时也能准确计算出中心点。下面我讲解一下。

得到了棋盘的上顶点坐标,它的X坐标就是棋盘中心点的X坐标,那么只要计算出Y坐标。我理解这位牛人的意思应该是,游戏有一个对称中心,并且该对称中心到棋盘中心的连线与水平方向的夹角是固定的。你可能认为对称中心是屏幕的中点,但这位牛人是这样计算的:

1
2
center_x = w / 2 + (24 / screen_width) * w
center_y = h / 2 + (17 / screen_height) * h

给出的角度是tan θ = 25.5 / 43.5 。

咋来的?不知道,真不知道。算了,先用吧。

既然知道了这个规律,那么我们就可以这样计算出棋盘中心点的坐标(X, Y ):

数学算术

这种规律到底是不是存在的?我们可以验证一下,下面是我调试代码时得到的一些图像:

图像1

仅用肉眼来看的话,我们几乎可以认为两个棋盘中心点的连线是平行的(此时棋子处于中心点)。而大量事实也证明了,牛人给出的规律是有效的,计算出的中心点位置很准确。

计算按压时间

好了,我们终于得到了棋子起始坐标点、棋盘中心点坐标,那么就可以计算出二者的直线距离了。然后根据直线距离,计算按压时间。

说实话,这位大牛计算按压时间的代码,我实在没看懂…不过我猜测,也许是考虑不同手机屏幕分辨率适配等等问题。大家有兴趣可以看一下。

我就比较粗暴,勾股定理计算直线距离,直线距离乘以时间系数等于按压时间(我这种方法的效果似乎也不差)。这里的时间系数,是大牛源码里提到的。似乎是不同屏幕大小的手机时间系数略有不同,此源码里已经给了配置文件,我也在我的源码里放了,略微做了些修改。


最终效果

最终效果比较依赖于配置文件中的数据,尤其是按压时间系数,大家可以根据自己的实际情况进行修改。

我的手机是Huawei nova 2s,屏幕大小是2160x1080,实际分数随随便便能够达到三四千分,最后一次游戏的分数是8000多分。欢迎大家在评论区晒出自己的分数哦!

游戏分数

源码奉上

GitHub


使用说明

  1. 安卓手机通过数据线与电脑相连,打开手机的USB调试
  2. 电脑端执行adb devices,确保手机已连接
  3. config 文件夹下有对应于手机分辨率的配置文件。可以将config.json文件复制到项目根目录下,代码会优先读取;也可不复制,代码自动在config文件夹下搜索匹配的配置文件;实际效果不好时,可手动调整配置文件。
  4. 手机界面切换到微信跳一跳游戏,点击开始游戏
  5. 执行python wechat_jump_auto.py

总结

特别鸣谢这位大牛,没有ta本篇博客无法顺利完成。本篇博客是博主基于自身理解写成,难免出现错误,望各位批评指正。无论是建议、想法和疑问,各位皆可在评论区提出,集思广益,互帮互助。

欢迎访问我的博客: www.charloe.top