每次在部落冲突搜索对手,都要花上不少时间。尤其是在发展中期的时候,特别难搜索到肥猪。像我本身的十本大号,在十本中期发展。前期还好,搜到的基本上是十本初的死鱼,随随便便就是五六十万。后来把兵营什么的升级上来,杯一冲到大师杯后,搜索到的基本上是各种十几万,打一把亏一把。搜个几十分钟都不一定碰到一个肥的。作为一个时间就是生命的码农,这肯定是难以接受的,所以就抽空写了这个脚本。
思路
首先要保证的一点是:一定不能封号。好不容易玩了两年发展到十本,封号了岂不是凉凉?所以从原理上,坚决不能读取内存数据。读内存数据相当容易翻车。
所以只能另辟蹊径,走文字识别这条路。原理大概是:
- 获取游戏中的截图
- 模拟点击进行搜索对手
- 图像识别对手的资源
- 判断资源,发现肥猪提醒
平台选择
由于获取截图需要一定的系统权限,因此平台只能选定安卓平台。在安卓平台下允许使用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文字识别服务
- 百度的文字识别服务入门,根据教程注册并安装Python SDK即可
https://cloud.baidu.com/doc/OCR/OCR-Python-SDK.html#.E9.85.8D.E7.BD.AEAipOcr
注册完成后开通相应的服务,拿到AppID、API Key和Secret Key就行。后续需要使用到百度的文字识别服务来识别资源。
图形处理库Pillow
获取到的屏幕截图还包含有其他文字,这些文字不是所需要的,所以需要使用图形处理库将资源数据裁剪出来,再通过文字识别转换为数字,这样大大提高了识别的精度和效率。
pip install Pillow
我在Mac下安装提示各种缺失,根据缺失将缺失的模块(MySQL-python)补上即可。
安卓手机
理论上只要是安卓手机基本都OK,版本在4.4以上的、能够启用开发者选项的就行。因为adb工具需要使用开发者选项来操控手机,所以需要启动开发者选项。不同手机有不同启用方法,具体就不详细赘述了,百度手机型号+启用开发者选项基本都能够搜索到。启用开发者选项后,在开发者选项中启用USB调试即可。
注意:开启前务必关闭关闭(最好卸载)360、毒霸等各种国产安卓软件,关闭其他不必要的优化软件,否则这些软件有权限在你手机上乱搞,比如安装各种乱七八糟的软件,所以开启前务必关闭!!!
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程序在那,搜到肥猪会自动弹出对话框提示。不过开始之前,必须回到大本营界面。
Just Enjoy!
所以以后就不需要不停地盯着屏幕看了,虽然可能会漏鱼,不过也大大节省了时间,以后只要边看视频别挂着搜肥猪,遇到肥猪就打就行。