Coder Social home page Coder Social logo

wechat_micro_jump_game_hero's Introduction

声明

请勿将此用于任何商业用途,仅供个人使用。

使用此工程带来的后果,由使用者自己承担,与本工程作者完全无关。

克隆、fork、下载,即代表您遵循此声明。

目录结构说明

.
├── docs  # 源码分析
├── game.beautified.js  # 美化后的小游戏源码[移除了 three.js 的库代码]
├── game_js_beautifier.py  # 美化 game.js
├── LICENSE
├── main_cv.py  # 自动跳 opencv 版本
├── main.py  # 自动跳 pillow 版本
├── Pipfile
├── Pipfile.lock
└── README.md

游戏源码分析

请前往源码分析

原理说明

普通版本(main.py,已废弃):

普通版本

优化版本(main_cv.py):

优化版本

调试时,修改 debug 为 True,真实运行时,设置为 False。停顿时间设置为 2s,如果电脑运算速度太快,保险起见可以设置为 2s。

原理:

Android:

  1. adb 截图
  2. 找到小人臀部坐标(hsv 色相紫黑色)
  3. 找到下一桥墩的顶点坐标
  4. 找到下一桥墩的极左/极右点坐标
  5. 计算出下一桥墩的中心点坐标
  6. 从小人臀部到下一桥墩中心点计算出跳远距离
  7. 将距离换算为长按时长
  8. 使用 adb 跳

iOS:

使用 wda 截图与事件点击,比 adb 快很多。

16.png.canny.png

16.png.debug.png

更新日志

2018-01-04

由于微信的小游戏的更新,基于背景 hsv 查找极点的方式,对于亮色背景,已经不再适用。

更新为使用轮廓区域获取顶点,再由商高定理计算中心点坐标。

2018-01-01

目的只是找到极点,并不需要二值化,去除相关代码。速度快。

目前以 距离 x 1.5 作为长按时长,基本能跳到目标中心位置。

2017-12-30

去除垂直投影图的方案,使用直接识别背景的方式去除背景,并二值化,以识别“极点”。速度仍然慢。

2017-12-29

第一可用版本,准确性不高,速度慢,使用垂直投影图进行目标查找。

开发环境搭建

系统环境

笔者环境:

$ uname -a
Darwin rmbp-finn.lan 17.3.0 Darwin Kernel Version 17.3.0: Thu Nov  9 18:09:22 PST 2017; root:xnu-4570.31.3~1/RELEASE_X86_64 x86_64

$ python --version
Python 2.7.10

$ python3 --version
Python 3.6.1

$ adb version
Android Debug Bridge version 1.0.39

$ 测试机
小米5

安装 Python 2.7+ 或 Python 3.5+

略。

(可选安装) virtualenv, virtualenvwrapper

略。

安装 adb

略。

工程搭建

先 fork 一份到自己账户。然后:

$ mkvirtualenv wechat_micro_jump_game_hero
$ cdvitualenv
$ git clone ...
$ cd wechat_micro_jump_game_hero
$ echo `pwd` > ../.project
$ pip install pipenv
$ pipenv install

手机连上电脑,确保

$ adb devices

显示了安卓设备。

设备打开跳一跳小游戏,并点击开始,之后:

# 普通版本(使用去除的背景的方式进行极点查找)
$ python main.py

# 优化版本(使用Canny算子找顶点,使用直角定理找极点)
$ python main_cv.py

调试说明

修改 debug 为 true 将进入启动调试模式。

同时需要修改 fn 变量为想要调试的图片(一般是上一次跳失败的图片,用来调试为什么失败),如 "25.png"。

调试模式中,会打开 fn 指定的图片,并画出相关坐标点,以及背景识别图。(将会打开两个图片用于调试)

注意事项

根据不同的手机型号(主要是屏幕分辨率)可能需要修改的几个地方:

  1. 请关闭手机的悬浮球,或者将其放置于屏幕最下方中间位置,以防止干扰。
  2. 指定 screenshot_directory 为截图保存路径。程序运行时,可以随时打开此目录查看其中生成的图片。
  3. 函数 find_hero(self) 中小人色取的是紫黑色:self.pixels[x, y] == (56, 56, 97, 255)。不知道不同手机显示的颜色是否一样。
  4. 函数 get_background_hsv(self) 中获取背景色的坐标,代码中为 (10, 800)。此参数是一个大概位于您手机屏幕竖直方向中间的某个点,确保此点一直是背景。大致坐标为(10, self.h * 0.42),即(10, 屏幕高 * 0.42),目前屏幕已经做了自适应,一般无需再设置。
  5. 函数 is_same_color(self, h, s, v, bg_hsv) 关于颜色是否相同的对比,其中的 h 和 s 两个值需要根据情况修改,一般来说,亮背景(灰白、黄色)时大一点点的值效果会好些,暗背景(紫色、蓝色等)时小一点点会好些,代码已经做了一部分的自适应,目前此 hsv 不太适用于粉色。
  6. 函数 get_holding(self) 中距离转换按键时长的系数,以及 max 和 min 中的两个参数,需要自己修改尝试。

wechat_micro_jump_game_hero's People

Contributors

dependabot[bot] avatar shu-ji avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wechat_micro_jump_game_hero's Issues

代码

兄弟,你这代码在小程序上怎么跑不了

被微信封了吗?

跳了 900 多分,好友的排名却并没有更新,是被微信识别到了吗? 求解...........................

问题

Windows可以用吗,还有就是那个命令是用什么软件执行的,谢谢!

红米3跳不成功

模拟点击已打开,截图可以正常保存,从截图来看位置计算也是正确的,但是每次跳都是跳不到第一块上边。设置debug的时候,弹出两个截图,第一个正常,第二个中间位置全白,不知道是什么原因?

3.X 代码

# coding: u8

from __future__ import print_function, unicode_literals

import colorsys
import os
import itertools
import math
import time
import traceback

from PIL import Image, ImageDraw


class Otsu(object):

    def __init__(self, path, debug=True):
        print(path)
        self.im = Image.open(path)

        self.w, self.h = self.im.size
        self.pixels = self.im.load()
        self.draw = ImageDraw.Draw(self.im)

        self.hero_pos = self.find_hero()
        print('hero pos:', self.hero_pos)

        is_hero_on_left = self.hero_pos[0] < self. w / 2

        bg_hsv = self.get_background_hsv()
        print('background hsv:', bg_hsv)

        top_most, lr_most = self.find_most(is_hero_on_left, bg_hsv)
        print('top most pos:', top_most)
        print('left/right most pos', lr_most)

        self.center_pos = top_most[0], lr_most[1]
        print('center pos', self.center_pos)

        if debug:
            #self.erase_background(bg_hsv)
            self.draw_pos(self.hero_pos)
            self.draw_pos(top_most)
            self.draw_pos(lr_most)
            self.draw_pos(self.center_pos)

            cx, cy = self.center_pos
            hx, hy = self.hero_pos
            self.draw.line((cx, cy, hx, hy), fill=(0, 255, 0), width=8)

            self.im.show()

    def find_hero(self):
        hero_poses = []
        for y in range(self.h / 3, self.h * 2 / 3):
            for x in range(self.w):
                # is purple
                if self.pixels[x, y] == (56, 56, 97, 255):
                    hero_poses.append((x, y))
        # calc the avg pos
        return map(lambda i: sum(i) / len(i), itertools.izip(*hero_poses))

    def rgb_to_hsv(self, r, g, b, a=255):
        h, s, v = colorsys.rgb_to_hsv(r / 255., g / 255., b / 255.)
        return int(h * 255.), int(s * 255.), int(v * 255.)

    def get_background_hsv(self):
        # use the (10, 800) as the background color
        bg_color = self.pixels[10, 800]
        return self.rgb_to_hsv(*bg_color)

    def erase_background(self, bg_hsv):
        for y in range(self.h / 4, self.h * 2 / 3):
            for x in range(self.w):
                h, s, v = self.rgb_to_hsv(*self.pixels[x, y])
                if self.is_same_color(h, s, v, bg_hsv):
                    self.im.putpixel((x, y), (0, 0, 0))
                else:
                    self.im.putpixel((x, y), (255, 255, 255))

    def find_most(self, is_hero_on_left, bg_hsv):
        hero_r = 15
        if is_hero_on_left:
            # top most is on the right, scan from right
            from_x = self.w - 1
            to_x = self.hero_pos[0] + hero_r
            step = -1
        else:
            # top most is on the left, scan from left
            from_x = 0
            to_x = self.hero_pos[0] - hero_r
            step = 1

        from_y, to_y = self.h / 4, self.hero_pos[1]

        top_most = self.find_top_most(bg_hsv, from_x, to_x, from_y, to_y, step)
        lr_most = self.find_lr_most(bg_hsv, from_x, to_x, from_y, to_y, step)
        return top_most, lr_most

    def find_top_most(self, bg_hsv, from_x, to_x, from_y, to_y, step):
        for y in range(from_y, to_y):
            for x in range(from_x, to_x, step):
                h, s, v = self.rgb_to_hsv(*self.pixels[x, y])
                if not self.is_same_color(h, s, v, bg_hsv):
                    return x, y

    def find_lr_most(self, bg_hsv, from_x, to_x, from_y, to_y, step):
        for x in range(from_x, to_x, step):
            for y in range(from_y, to_y):
                h, s, v = self.rgb_to_hsv(*self.pixels[x, y])
                if not self.is_same_color(h, s, v, bg_hsv):
                    return x, y

    def is_same_color(self, h, s, v, bg_hsv):
        bg_h, bg_s, bg_v = bg_hsv
        return (abs(h - bg_h) < 15) and (abs(s - bg_s) < 20)

    def draw_pos(self, pos, color=(0, 255, 0)):
        x, y = pos
        r = 25
        self.draw.ellipse((x, y, x + r, y + r), fill=color, outline=color)

    def get_holding(self):
        line_length = int(math.sqrt(
            pow(self.center_pos[0] - self.hero_pos[0], 2) + \
            pow(self.center_pos[1] - self.hero_pos[1], 2)
        ))

        length_time = line_length * 1.5

        holding = min(950, max(length_time, 300))

        print('length, duration, holding: ', line_length, length_time, holding)
        print()

        return int(holding)


def run_cmd(cmd):
    return os.system(cmd)
    # return commands.getstatusoutput(cmd)


jump_times = itertools.count(0)
while True:
    try:
        debug = True

        if debug:
            fn = 's'
        else:
            fn = str(next(jump_times))
            run_cmd('adb shell screencap -p /sdcard/s.png')
            run_cmd('adb pull /sdcard/s.png /tmp/{}.png'.format(fn))
            run_cmd('cp /tmp/{}.png /tmp/s.png'.format(fn))
            print(fn)

        holding = Otsu('/tmp/{}.png'.format(fn), debug=debug).get_holding()

        if debug:
            raise KeyboardInterrupt
        else:
            run_cmd('adb shell input swipe 255 255 0 0 {}'.format(holding))
            time.sleep(2)
    except KeyboardInterrupt:
        raise KeyboardInterrupt
    except Exception as e:
        traceback.print_exc()
        time.sleep(2)

代码修改了,更换了3.X环境下的依赖包
小米5s跑的,找不到文件错误

python3.0+ 修改 可用代码

# coding: u8

from __future__ import print_function, unicode_literals

import colorsys
import subprocess
import itertools
import math
import time
import traceback

from PIL import Image, ImageDraw


class Otsu(object):

    def __init__(self, path, debug=False):
        print(path)
        self.im = Image.open(path)

        self.w, self.h = self.im.size
        self.pixels = self.im.load()
        self.draw = ImageDraw.Draw(self.im)

        self.hero_pos = self.find_hero()
        print('hero pos:', self.hero_pos)

        is_hero_on_left = self.hero_pos[0] < self. w / 2

        bg_hsv = self.get_background_hsv()
        print('background hsv:', bg_hsv)

        top_most, lr_most = self.find_most(is_hero_on_left, bg_hsv)
        print('top most pos:', top_most)
        print('left/right most pos', lr_most)

        self.center_pos = top_most[0], lr_most[1]
        print('center pos', self.center_pos)

        if debug:
            #self.erase_background(bg_hsv)
            self.draw_pos(self.hero_pos)
            self.draw_pos(top_most)
            self.draw_pos(lr_most)
            self.draw_pos(self.center_pos)

            cx, cy = self.center_pos
            hx, hy = self.hero_pos
            self.draw.line((cx, cy, hx, hy), fill=(0, 255, 0), width=8)

            self.im.show()

    def find_hero(self):
        hero_poses = []
        # python3 取消了xrange 改为range 且参数必须为整数
        for y in range(int(self.h / 3), int(self.h * 2 / 3)):
            for x in range(self.w):
                # is purple
                if self.pixels[x, y] == (56, 56, 97, 255):
                    hero_poses.append((x, y))
        # calc the avg pos
        # python3 map处理改变  并且取消了 itertools.izip
        # return map(lambda i: sum(i) / len(i), itertools.izip(*hero_poses))
        return [sum(i) / len(i) for i in zip(*hero_poses)]

    def rgb_to_hsv(self, r, g, b, a=255):
        h, s, v = colorsys.rgb_to_hsv(r / 255., g / 255., b / 255.)
        return int(h * 255.), int(s * 255.), int(v * 255.)

    def get_background_hsv(self):
        # use the (10, 800) as the background color
        bg_color = self.pixels[10, 800]
        return self.rgb_to_hsv(*bg_color)

    def erase_background(self, bg_hsv):
        for y in range(int(self.h / 4), int(self.h * 2 / 3)):
            for x in range(int(self.w)):
                h, s, v = self.rgb_to_hsv(*self.pixels[x, y])
                if self.is_same_color(h, s, v, bg_hsv):
                    self.im.putpixel((x, y), (0, 0, 0))
                else:
                    self.im.putpixel((x, y), (255, 255, 255))

    def find_most(self, is_hero_on_left, bg_hsv):
        hero_r = 15
        if is_hero_on_left:
            # top most is on the right, scan from right
            from_x = self.w - 1
            to_x = self.hero_pos[0] + hero_r
            step = -1
        else:
            # top most is on the left, scan from left
            from_x = 0
            to_x = self.hero_pos[0] - hero_r
            step = 1

        from_y, to_y = self.h / 4, self.hero_pos[1]

        top_most = self.find_top_most(bg_hsv, from_x, to_x, from_y, to_y, step)
        lr_most = self.find_lr_most(bg_hsv, from_x, to_x, from_y, to_y, step)
        return top_most, lr_most

    def find_top_most(self, bg_hsv, from_x, to_x, from_y, to_y, step):
        for y in range(int(from_y),int(to_y)):
            for x in range(int(from_x),int(to_x), step):
                h, s, v = self.rgb_to_hsv(*self.pixels[x, y])
                if not self.is_same_color(h, s, v, bg_hsv):
                    return x, y

    def find_lr_most(self, bg_hsv, from_x, to_x, from_y, to_y, step):
        for x in range(int(from_x), int(to_x), step):
            for y in range(int(from_y), int(to_y)):
                h, s, v = self.rgb_to_hsv(*self.pixels[x, y])
                if not self.is_same_color(h, s, v, bg_hsv):
                    return x, y

    def is_same_color(self, h, s, v, bg_hsv):
        bg_h, bg_s, bg_v = bg_hsv
        return (abs(h - bg_h) < 15) and (abs(s - bg_s) < 20)

    def draw_pos(self, pos, color=(0, 255, 0)):
        x, y = pos
        r = 25
        self.draw.ellipse((x, y, x + r, y + r), fill=color, outline=color)

    def get_holding(self):
        line_length = int(math.sqrt(
            pow(self.center_pos[0] - self.hero_pos[0], 2) + \
            pow(self.center_pos[1] - self.hero_pos[1], 2)
        ))

        # 不同分辨率系数不同 
        length_time = line_length * 1.4

        holding = min(950, max(length_time, 300))

        print('length, duration, holding: ', line_length, length_time, holding)
        print()

        return int(holding)



def run_cmd(cmd):
    # python3 取消了commands 用subprocess 代替
    p = subprocess.Popen([cmd],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True)
    p.stdin.flush()
    # 截图 及 上传图片需要耗时  这里每条命令手动加上延时  不然可能图片未上传上去 读取图片失败
    time.sleep(0.5)


jump_times = itertools.count(0)
while True:
    try:
        debug = False

        if debug:
            fn = 's'
        else:
            fn = str(next(jump_times))
            run_cmd('adb shell screencap -p /sdcard/screenshot.png')
            # 不同手机图片存放的实际位置不同  建议先手动命令行执行下上面的命令  再到文件管理中找到图片的位置 填入下面的位置
            # 这里是华为p9的真实存放位置
            run_cmd('adb pull /storage/emulated/0/screenshot.png /tmp/{}.png'.format(fn))
            # run_cmd('cp /tmp/{}.png /tmp/s.png'.format(fn))
            print(fn)

        

        holding = Otsu('/tmp/{}.png'.format(fn), debug=debug).get_holding()

        if debug:
            raise KeyboardInterrupt
        else:
            run_cmd('adb shell input swipe 255 255 0 0 {}'.format(holding))
            time.sleep(3)
    except KeyboardInterrupt:
        raise KeyboardInterrupt
    except Exception as e:
        traceback.print_exc()
        time.sleep(2)

针对python3 修改了一下代码 并且微调了一下 亲测可用

cmd异常

p install pillow
Collecting pillowpip
Could not find a version that satisfies the requirement pillowpip (from versions: )
No matching distribution found for pillowpip

No such file or directory: u'/tmp/0.png'

Traceback (most recent call last):
File "D:/PythonTest/python/main.py", line 156, in
holding = Otsu('/tmp/{}.png'.format(fn), debug=debug).get_holding()
File "D:/PythonTest/python/main.py", line 18, in init
self.im = Image.open(path)
File "D:\PythonTest\venv\lib\site-packages\PIL\Image.py", line 2543, in open
fp = builtins.open(filename, "rb")
IOError: [Errno 2] No such file or directory: u'/tmp/0.png'

SyntaxError: encoding problem: u8

e:\GitHubProjects\wechat_micro_jump_game_hero-master>python android_jump.py
File "android_jump.py", line 1
SyntaxError: encoding problem: u8
麻烦请教一下这个是怎么回事?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.