Python部落冲突自动搜索对手

每次在部落冲突搜索对手,都要花上不少时间。尤其是在发展中期的时候,特别难搜索到肥猪。像我本身的十本大号,在十本中期发展。前期还好,搜到的基本上是十本初的死鱼,随随便便就是五六十万。后来把兵营什么的升级上来,杯一冲到大师杯后,搜索到的基本上是各种十几万,打一把亏一把。搜个几十分钟都不一定碰到一个肥的。作为一个时间就是生命的码农,这肯定是难以接受的,所以就抽空写了这个脚本。

思路

首先要保证的一点是:一定不能封号。好不容易玩了两年发展到十本,封号了岂不是凉凉?所以从原理上,坚决不能读取内存数据。读内存数据相当容易翻车。
所以只能另辟蹊径,走文字识别这条路。原理大概是:

  • 获取游戏中的截图
  • 模拟点击进行搜索对手
  • 图像识别对手的资源
  • 判断资源,发现肥猪提醒

平台选择

由于获取截图需要一定的系统权限,因此平台只能选定安卓平台。在安卓平台下允许使用adb命令连接Android系统截图,并将截图发送到Mac或Windows上来,进行其他的逻辑判断。

截图获取

获取截图可以使用安卓的虚拟机、模拟器,在Windows和Mac下均有相应的Android虚拟机。但如果使用模拟器运行部落冲突,意味着搜索到对手也必须在模拟器下发动进攻。而模拟器下对多指操控很不友好。而且模拟器运行占用资源也会比较大,毕竟码农,还是不希望电脑卡顿的,因此最后选定使用实体机来跑。

好在我有一个闲置下来的小米5,因此可以通过获取小米5的截图,来判断对手资源,发现合适的肥猪再进行提醒。这样也能在手机上操控,最大限度保证能够榨干肥猪。

前期准备

Python

首先你得有一个Python,毕竟这是一个Python脚本。同时还要选择一个好的IDE,这里我选择的是Pycharm。这样在写代码的时候能够省下不少功夫。
Python和Pycharm的安装我就不啰嗦了,百度上一大把。需要注意的是Python最好选择Python3版本,这里我选择的是Python3.6版本,2.7版本在库和语法上与3.6略有不同。

pip

因为需要使用一些Python库,使用pip的安装必不可少。同理,百度一大把。不过安装完成后记得换为国内源,这样下载速度会快很多很多。

  • 传送门

https://www.jianshu.com/p/9d986bc8ba69

注册百度的AI文字识别服务

注册完成后开通相应的服务,拿到AppID、API Key和Secret Key就行。后续需要使用到百度的文字识别服务来识别资源。

_apikey_1530979649_2147066274.png

图形处理库Pillow

获取到的屏幕截图还包含有其他文字,这些文字不是所需要的,所以需要使用图形处理库将资源数据裁剪出来,再通过文字识别转换为数字,这样大大提高了识别的精度和效率。

pip install Pillow

我在Mac下安装提示各种缺失,根据缺失将缺失的模块(MySQL-python)补上即可。


_图片中还含有其他文字_1531030593_729756971.png

安卓手机

理论上只要是安卓手机基本都OK,版本在4.4以上的、能够启用开发者选项的就行。因为adb工具需要使用开发者选项来操控手机,所以需要启动开发者选项。不同手机有不同启用方法,具体就不详细赘述了,百度手机型号+启用开发者选项基本都能够搜索到。启用开发者选项后,在开发者选项中启用USB调试即可。

注意:开启前务必关闭关闭(最好卸载)360、毒霸等各种国产安卓软件,关闭其他不必要的优化软件,否则这些软件有权限在你手机上乱搞,比如安装各种乱七八糟的软件,所以开启前务必关闭!!!

_启用usb调试_1531044449_422037486.png

adb工具环境配置

  • 检测adb环境是否配置完毕

在Mac的终端或Windows的命令提示符下输入

adb --version

如果能过显示相应的adb版本号,如

Android Debug Bridge version 1.0.40
Version 4797878
Installed as /Users/zinc/Library/Android/sdk/platform-tools/adb

则说明adb环境已经配置完毕。否则需要配置环境

Windows系统下比较方便,百度adb工具箱下载即可,大小10mb左右,再根据教程配置adb环境即可

  • Windows下的adb环境配置

https://jingyan.baidu.com/article/17bd8e52f514d985ab2bb800.html

  • Mac下的adb环境配置

在Mac下的配置相对比较麻烦,我个人倾向于安装Android Studio,这样能够集成adb工具,也会自动配置好adb环境。如果安装完成后仍未配置好,参照:

https://blog.csdn.net/wwf0123/article/details/52497885

代码实战

首先需要弄清楚总一个流程:

  • 获取手机分辨率
  • 知道相应的按钮的x、y位置
  • 模拟点击左下角进攻按钮
  • 点击搜索对手按钮
  • 手机截图
  • 将手机截图发送至电脑端
  • 将资源信息裁剪出来
  • 用百度AI识别信息
  • 根据信息提醒或继续搜索下一个对手

因此分出三个类,ScreenCapturer类负责获取屏幕截图、以及模拟点击操作,PicScanner类负责图片处理、以及上传识别,而autoFind则进行逻辑处理,控制整个流程

ScreenCapturer.py

from os import *
from time import sleep

# adb shell screencap -p /sdcard/1.png(保存到SDCard)
# adb pull /sdcard/screenshot.png d:/screenshot.png(保存到电脑)

class ScreenCapturer:

    # 获取的截图文件在手机中的保存位置(默认保存在内置存储根目录下)
    ANDRIOD_PIC_PATH = "/sdcard/screenshot.png"
    # 文件保存在电脑中的目录
    TRANS_PIC_PATH = "/Users/zinc/Documents/autoFind/screenshot.png"
    # 获取截图的adb命令
    SCREEN_CAP_CMD = "adb shell screencap -p "
    # 传输文件至电脑adb命令
    TRANS_FILE_CMD = "adb pull "
    # 获取Android手机分辨率命令
    GET_RESOLUTION_CMD = "adb shell dumpsys window displays"
    # 模拟点击命令
    POINT_SCREEN_CMD = "adb shell input tap"
    # 手机分辨率
    phoneResolution = []

    def __init__(self,andriod="",trans=""):
        if andriod!="" and trans != "":
            # 判断是否为默认参数
            self.ANDRIOD_PIC_PATH = andriod
            self.TRANS_PIC_PATH = trans

    def getPhoneScreen(self):
        # 获取屏幕截图
        command = self.SCREEN_CAP_CMD + self.ANDRIOD_PIC_PATH
        system(command)

    def transPhoneScreen(self):
        # 将截图传输至电脑
        command = self.TRANS_FILE_CMD + self.ANDRIOD_PIC_PATH + " " +self.TRANS_PIC_PATH
        system(command)
        print("截图已获取")

    # 模拟点击某一位置
    def pointOnPhone(self,x=0.0,y=0.0):
        strX = str(x)
        strY = str(y)
        command = self.POINT_SCREEN_CMD + " " + strX + " " + strY
        system(command)
        pass
    
    # 获取屏幕分辨率
    def getPhoneResolution(self):
        # 获取命令行的打印值
        r = popen(self.GET_RESOLUTION_CMD)
        text = str(r.read())
        # 查找init=字符串,其后为手机分辨率情况
        beginC = text.find("init=")
        # 获取其后的10个字符
        line = text[beginC+5:beginC+15]
        resolution = line.split("x")
        self.phoneResolution.append(float(resolution[0]))
        self.phoneResolution.append(float(resolution[1]))
        print("weight =",self.phoneResolution[0],"\nheight =",self.phoneResolution[1])
        r.close()
        pass
    
    # 点击进攻按钮
    def pointAttackBtn(self):
        # 保留两位小数
        x = 0.07 * self.phoneResolution[1]
        y = 0.9 * self.phoneResolution[0]
        self.pointOnPhone(x,y)
        print("点击进攻按钮")
    
    # 点击搜索对手按钮
    def pointSearchAttacker(self):
        # 保留两位小数
        x = 0.23 * self.phoneResolution[1]
        y = 0.72 * self.phoneResolution[0]
        self.pointOnPhone(x, y)
        print("点击搜索对手按钮")

    # 点击搜索下一个按钮
    def pointNextAttacker(self):
        # 保留两位小数
        x = 0.925 * self.phoneResolution[1]
        y = 0.732 * self.phoneResolution[0]
        self.pointOnPhone(x, y)
        print("点击搜索下一个按钮")

    # 点击结束战斗按钮
    def pointEndAttack(self):
        # 保留两位小数
        x = 0.075 * self.phoneResolution[1]
        y = 0.745 * self.phoneResolution[0]
        self.pointOnPhone(x, y)
        print("点击结束战斗按钮")


# 测试代码,完成类的测试后注释

# capture = ScreenCapturer()
#
# capture.getPhoneScreen()
# capture.transPhoneScreen()

# capture.getPhoneResolution()
# capture.pointAttackBtn()
# capture.pointSearchAttacker()
# for i in range(0,5):
#     sleep(5)
#     capture.pointNextAttacker()
# sleep(5)
# capture.pointEndAttack()

去掉最底部的测试注释,直接运行ScreenCapturer.py,测试功能是否正常

一些常量的注释

  • 默认的截图保存地址,截图将会保存在这个位置下,/sdcard/位置默认为安卓手机的内置储存,即此时保存在内置储存根目录下
ANDRIOD_PIC_PATH = "/sdcard/screenshot.png"
  • 截图传输到电脑后的保存位置
TRANS_PIC_PATH = "/Users/zinc/Documents/autoFind/screenshot.png"

PicScanner.py

from aip import AipOcr
from PIL import Image

class PicScanner:

    """ 你的 APPID AK SK """
    APP_ID = '这里填写百度AI的APPID'
    API_KEY = "百度AI的API_KEY"
    SECRET_KEY = "百度AI的SECRET_KEY"

    # 初始化AipFace对象
    # aipOcr = AipOcr(APP_ID, API_KEY, SECRET_KEY)

    # 定义参数变量
    options = {
        'detect_direction': 'true',
        'language_type': 'CHN_ENG',
    }

    # 读取图片
    # 文件保存在电脑中的目录
    filePath = "/Users/zinc/Documents/autoFind/screenshot.png"

    def __init__(self,path=""):
        if path!="":
            self.filePath = path

    def get_file_content(self,filePath):
        with open(filePath, 'rb') as fp:
            return fp.read()

    def readPicNum(self,path=""):
        if path != "":
            self.filePath = path
        aipOcr = AipOcr(self.APP_ID, self.API_KEY, self.SECRET_KEY)
        # 调用通用文字识别接口
        result = aipOcr.basicAccurate(self.get_file_content(self.filePath), self.options) # 高精度
        #result = aipOcr.basicGeneral(self.get_file_content(self.filePath), self.options) # 高速
        return result

        # 将截图裁剪至仅剩资源的部分,方便于图片识别
    def cutPicToSource(self):
        im = Image.open(self.filePath, 'r')
        if im:
            width, height = im.size
            cropedIm = im.crop((0.046 * width, 0.125 * height, 0.135 * width, 0.268 * height))
            cropedIm.save(self.filePath)
        else:
            print("图片文件打开失败")

# scanner = PicScanner()
# # scanner.cutPicToSource()
# scanner.readPicNum()
  • 根据百度控制台的提示填写相应的APP_ID、API_KEY、SECRET_KEY,用来请求百度AI服务器。
    APP_ID = '这里填写百度AI的APPID'
    API_KEY = "百度AI的API_KEY"
    SECRET_KEY = "百度AI的SECRET_KEY"
  • 精度查找
    百度提供了两种识别精度,一种为高速低精度的识别,50000次/日。还有一种是较低速和高精度的识别,500次/日。个人使用下来感觉两种识别速度接近,但识别精度却是天差地别,低精度识别几乎没有正确识别所有信息的情况。所以务必使用高精度的识别,可能对图像处理后识别率可能会提升(比如提高对比度),但我没试过,500次/日我觉得完全够我一天的使用了。
result = aipOcr.basicAccurate(self.get_file_content(self.filePath), self.options) # 高精度
#result = aipOcr.basicGeneral(self.get_file_content(self.filePath), self.options) # 高速
  • 裁切信息位置
    crop命令的裁剪位置是由(信息位置/屏幕分辨率)得到的,不同分辨率下信息在屏幕中的占比是不一样的,需要注意一下。
# 将截图裁剪至仅剩资源的部分,方便于图片识别
def cutPicToSource(self):
im = Image.open(self.filePath, 'r')
# 检测是否打开成功
if im:
    width, height = im.size
    cropedIm = im.crop((0.046 * width, 0.125 * height, 0.135 * width, 0.268 * height))
    cropedIm.save(self.filePath)
else:
    print("图片文件打开失败")

autoFind.py

from PicScanner import PicScanner
from ScreenCapturer import ScreenCapturer
from time import sleep
import tkinter
import tkinter.messagebox

class autoFind:

    # 对象实例
    scanner = PicScanner()
    capture = ScreenCapturer()
    # 设置的搜索资源值
    source = {}
    # 获取到的资源值
    int_gold = 0
    int_water = 0
    int_black = 0

    def setSourceValue(self,gold,water,black_w):
        self.source['gold'] = gold
        self.source['water'] = water
        self.source['black_w'] = black_w
        print(self.source)

    def beginFind(self):
        self.showAdvice()
        # 获取屏幕分辨率
        self.capture.getPhoneResolution()
        # 点击进攻按钮
        self.capture.pointAttackBtn()
        # 点击搜索对手按钮
        self.capture.pointSearchAttacker()
        # 延时5s
        sleep(6)
        while(1):
            # 获取屏幕截图并判断资源
            self.capture.getPhoneScreen()
            self.capture.transPhoneScreen()
            self.scanner.cutPicToSource()
            words = self.scanner.readPicNum()
            words_result = words['words_result']
            # 尝试获取资源,如果抛出异常则说明获取失败
            try:
                gold = words_result[0]['words']
                water = words_result[1]['words']
                black_w = words_result[2]['words']
            except:
                print("获取资源失败,开始搜索下一个对手")
                sleep(2)
                continue
            # 尝试将其转换为整数,转换失败则说明识别失败
            try:
                print("当前搜索到的对手资源为",gold, water, black_w)
                self.int_gold = int(gold)
                print(self.int_gold)
                self.int_water = int(water)
                print(self.int_water)
                self.int_black = int(black_w)
                print(self.int_black)
            except:
                print("资源识别失败,搜索下一个对手")
                self.capture.pointNextAttacker()
                sleep(6)
                continue
            # 如果能够成功转化,则判断资源值是否满足设定值
            if self.int_gold > self.source['gold'] and self.int_water > self.source['water'] and self.int_black > self.source['black_w']:
                self.findVictim(str(self.int_gold), str(self.int_water), str(self.int_black))
                # 发现肥猪跳出循环
                break
            else:
                # 如果不满足设定值,则继续搜索下一个对手
                print("这个人是个穷b,懒得打他了")
                self.capture.pointNextAttacker()
                sleep(6)

    # 找到合适对手后进行提醒
    def findVictim(self,gold,water,black_w):
        tkinter.messagebox.showinfo('发现肥猪', '找到一个肥猪,他有'+gold+"金币,"+water+"圣水和"+black_w+"重油,搜索程序已暂停.")

    def showAdvice(self):
        tkinter.messagebox.showinfo('使用前准备', "使用前先回到大本营界面,搜索过程中不要点击其他按钮,搜索到对手后会自动暂停。点击确定开始进行搜索")

find = autoFind()
try:
    find.setSourceValue(300000,300000,1000)
    find.beginFind()
except:
    tkinter.messagebox.showinfo('程序出错!', "程序出错!已暂停搜索,请返回大本营重新运行程序!")
  • 当搜索到肥猪时用thinter来弹出对话框
  • 多使用try语句
    因为多个地方无法确定是否能够获取到相应信息,比如识别信息的时候容易识别错误,识别成了字母或其他字符,因此就需要异常处理来保证程序能够继续运行下去

运行效果

  • 完成后基本只用运行就行了,挂着Python程序在那,搜到肥猪会自动弹出对话框提示。不过开始之前,必须回到大本营界面。
_搜索到的肥猪_1531047047_1147857544.png
_搜索到对手_1531046946_1261457288.png

Just Enjoy!

所以以后就不需要不停地盯着屏幕看了,虽然可能会漏鱼,不过也大大节省了时间,以后只要边看视频别挂着搜肥猪,遇到肥猪就打就行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,907评论 25 707
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,975评论 3 119
  • 无聊的时候画的抽象画,画完才发现,忘记画尾巴了。
    K八画阅读 243评论 1 1
  • 从炉灶边到田野里,无力的诗人喝着鸦片酒,岁月无边黑暗,杯中的酒泛起阵阵涟漪,诗行不再铿锵有力了,处处都是病句,标点...
    耕天下阅读 190评论 2 0
  • 学数学一直是少年的一个痛点, 各种原因纷繁复杂! 去追究终也不是个办法。 现在的问题是一起来面对, 少年心中一点点...
    千吉change阅读 557评论 1 1