头脑王者辅助

-这是 小明同学 2018年第 1 篇文章-

#废话慎读

随着微信小游戏的出现,最近各种外挂又开始盛行了,听到的最多的外挂就是《跳一跳》的外挂了,看似很简单的游戏,但是玩儿起来却一点都不简单,我也像很多人一样,当分数越高,就会越紧张,至今为止自己玩儿还从未突破过200分,是不是很菜,看到朋友圈那么多好几千分的,自己也不甘心,于是就各种倒腾外挂,开始刷分,刷排名。一个同学问我,很多人都玩儿到了好几千分,这些人也太无聊了,听到这句话,真是笑出了声,她居然不知道有外挂这种东西。外挂盛行之后,腾讯也各种打击外挂,后来就发现朋友圈的分数没有以前那么离谱的高了,在微信公开课上,张小龙,居然说自己最高分数是6000多分,而现场也是当众玩儿到了900多分,这该是一种什么心态,他说这款游戏本来是让大家放松玩儿的,但是大多数人却分数越高越紧张,很容易就死掉了。也有人说,用个外挂,刷那么高的分数,有什么意思,实际上的确没啥意思,其实有意思的并不是刷那么高的分,而是外挂本身,他是怎么实现自己玩那么高分儿的,原理是什么,该如何实现,作为一名程序员,这才是我们要玩儿的东西,分数多少,排名高低,都不重要,重要的是图一乐呗。

其实原理很简单,我并没有仔细研究过github上的外挂的源代码,也是因为懒的原因吧,我只是在我自己机器上跑了起来,当然也入了很多坑,因为当时用的是python版的,而自己并不是用python做开发的,所以很多东西都是现查现使用。最后也只是把安卓的搞定了,IOS更是一窍不通,各种安装,最后还是报错了,也没再去管他。

最近《冲顶大会》,让很多人拿钱拿到手软,我也一样,到现在一分钱也没得到,实在是太笨了,于是乎我就想,这要是有外挂就好了,那真是太爽了,但是这种题目有时间限制的,外挂其实还是比较困难的,因为响应的时间可能会超出题目规定的答题时间,但也只是起到一个辅助的作用,而且有的题目也并不是能直接找到答案的,比如:下面4个选项中哪个答案是错误的(),这种怎么去搜索,所以外挂这种东西啊玩玩儿就好了,还是要靠真材实料。

同样的在小程序里面也有款类似的游戏《头脑王者》,拿它当作实验,自己做一个外挂,但是对我来说并未那么简单,其实学习python也有一段时间了(不应该这么说,是从开始看python已经很久了,但至于学了多少了,就不多说了,实在是太懒了。。。),但还是想用它来实现这个小小的外挂,下面就开始切入正题。

我们先来分析下流程,其实跟《跳一跳》外挂很相似,首先我们还是要去截图,题目界面如下图所示:

截完图后,将题目的题干和答案取出,这里也就会用到图像识别了,然后去百度或其他搜索引擎去搜索题目,得到正确答案和选项比较,最终返回正确答案,通过坐标我们也可以知道每个选项的大体位置,然后再去模拟点击答案,完成操作。其实步骤就是这么简单。

开始->截图->识别文字->搜索题目并返回答案->模拟点击答案->结束

开发工具:PyCharm 2017.3

开发语言:Python 3.6

开发环境:MacOS

图像识别:腾讯优图AI 通用ORC识别模块 地址:http://open.youtu.qq.com/#/develop/api-ocr-general

调试工具:adb  关于adb在mac上如何使用请查看我简书上一片杂乱的文章,地址:https://www.jianshu.com/p/2a0cb004792d

测试手机:锤子坚果Pro

搜索引擎:百度 or 必应

OK,下面我们进入正题,我们创建一个名为MindKing(大概是头脑王者的英文名吧,请忽略)的项目,创建一个Python脚本MindKingExt.py,然后将优图的OCR识别模块引入到项目中,大概就是酱紫:

创建完成后,下面我们完成第一个任务,手机截屏

关于手机截屏并保存,熟悉安卓开发的朋友应该都很清楚,使用使用adb的相关命令即可,我们可以直接在控制台来测试,首先我们查看手机是否连接成功(手机需要打开开发者模式,并开启USB调试,这些应该都不用说了),使用 adb devices 如果连接成功是介样的,刚开始连接时会先运行daemon,然后显示此时的设备ID,这样也就代表连接成功了,下面我们看下直接在控制台中使用截屏命令时,是怎样的,命令:

adb shell screencap -p 


没错会出现一堆乱码,并且显示了截图的宽高等信息,下一步我们将此命令在我们的程序中执行,首先需要导入一个模块,它的名字叫做创建附加进程模块,subprocess,在这里我们使用的是它的直接处理管道的方法,叫做subprocess.Popen(),如何使用它执行截屏呢,代码如下:

# 第一个参数为命令行,安卓手机截屏命令;
# 第二个参数shell=True,在Windows下表示cmd.exe /c即在这里执行的是cmd命令;
# 第三个参数建立管道,这里通过将stdout重定向到subprocess.PIPE上来取得adb命令的输出
process = subprocess.Popen('adb shell screencap -p', shell = True, stdout = subprocess.PIPE)
然后我们我们可以从process这个变量中取到截图的二进制数据,读取二进制数据:
# 读取二进制数据
screenshot = process.sdtout.read()
这时我们可以直接将screenshot保存图片了,关于文件读写的操作就是IO编程的部分了,这里不过多解释了,
# 可直接保存至文件
    with open('screenshot.png','wb') as f:
        f.write(screenshot)
我们是推荐这种写法的,旧方法中代码是比较长的,关于读写文件操作,你需要加try finally 以及要将对象进行Close操作,而使用with这种语法就不用那么麻烦了,这跟C# 中using 的语法是相似的。下面是我们手机的截图,其实在这里呢直接这样保存图片是不合适的。

因为这样势必会浪费时间,我们本来答题是有时间限制的,而直接保存图片会耗费不必要的时间,所以在这里我们可以把图片加载到内存中操作,保存至内存需要引入另一个模块BytesIO ,它支持的是二进制数据,如果要将字符串写入内存的话要使用StringIO,如何使用这个模块呢,首先创建一个变量指向这个对象,再把刚才读取到的二进制数据写入这个变量中,

# 将二进制读进内存中
imgbyte = BytesIO()
imgbyte.write(screenshot)
到这一步,我们算是取到了直接截取的图片了,下面我们开始处理这张图片,通过上图,我们可以看出,在这张图中有诸多的干扰信息,那么这会在我们后面图像识别中造成麻烦,我们要的只是题干和选项,所以我们需要通过截图以及拼接将我们的图片重新整合,只保留题干和答案选项部门,提高识别度,那怎么去截取这张图片的有效信息呢,这一步操作与你手机的实际分辨率会有关系,我们需要定位题干和答案的位置,使用像素去进行定位,下面我们将这张图用画图工具打开,


上下两个红色框标注的内容就是我们想要的了,关于这个坐标的定位,在画图工具中可以直接显示,大家请根据自己的手机的分辨率自行调节,这里给出我使用的手机的大体位置坐标(实际上这个也不是我自己截取的,从别处看到的正好也是我手机的分辨率\ganga\),为了方便我们将截取图片相应的参数单独放到配置信息里面,

# 配置坐标信息,根据手机分辨率的不同调整,此坐标可适用于1080 X 1920
config = {
    '头脑王者':{
        'title': (80, 500, 1000, 880),
        'answer': (80, 960, 1000, 1720),
        'point': [
            (316, 993, 723, 1078),
            (316, 1174, 723, 1292),
            (316, 1366, 723, 1469),
            (316, 1570, 723, 1657)
        ]
    }
}
title就是我们要的题干部分,answer就是答案的部分了,而下面的point是我们要点击的4个答案选项的大体坐标。切割以及拼接图片的代码如下,不详细说明了,很简单,但是有6步操作:

    # 图片处理
    img = Image.open(imgbyte)
    # 切出题目,左上角,右下角的点
    img_Prob = img.crop((config['头脑王者']['title']))
    # 切出答案,左上角,右下角
    img_Ans = img.crop((config['头脑王者']['answer']))
    # 拼接
    new_img = Image.new('RGBA', (920, 1140)) #创建一个新的画布,宽920,高1140
    new_img.paste(img_Prob, (0, 0, 920, 380))
    new_img.paste(img_Ans, (0, 380, 920, 1140))
从内存中打开图片,切割题干部分,切割答案部分,创建一个新的Image对象,粘贴题干,粘贴答案,注意下这个坐标的含义,前两个为左上角的点的左边,后两个为右下角的点的坐标。另外操作图片我们还需要引入一个模块,就是Image模块,完成后我们再将新的对象保存至内存中:

    # 内存对象
    new_img_byte = BytesIO()
    #保存为png格式至内存中
    new_img.save(new_img_byte, 'png')
下面我们将它保存成图片看效果:

看样子应该是达到了我们预期的效果,最后我们将这个对象返回 return new_img_byte 。继续下一步操作,识别图像。关于识别图像,我用的是腾讯的优图AI开放平台的通用OCR识别,关于这个的使用呢,大家可以直接去官网看官方文档,用法也都很简单,返回的数据也都是标准的json对象,处理起来也很方便,下面直接贴代码了:

# 这里使用的是腾讯的优图开放平台的图像识别SDK,该appid应该会有上弦,具体不知,如果不能使用了,请自行申请更换
    """ 以下为开放平台返回的识别文本,题目和答案根据此内容解析
    {
        "errorcode":0,
        "errormsg":"OK",
        "items":
                [
                    {
                        "itemstring":"手机",
                        "itemcoord":{"x" : 0, "y" : 1, "width" : 2, "height" : 3},
                        "words": [{"character": "手", "confidence": 98.99}, {"character": "机", "confidence": 87.99}]
                    },
                    {
                        "itemstring":"姓名",
                        "itemcoord":{"x" : 0, "y" : 1, "width" : 2, "height" : 3},
                        "words": [{"character": "姓", "confidence": 98.99}, {"character": "名", "confidence": 87.99}]
                    }
                ],
        "session_id":"xxxxxx"
    """
    appid = '10115709'
    secret_id = 'AKIDY0HNJ482FJI8mJcqpdIpCPQFwTs6d2kM'
    secret_key = 'fAVfdP1Rlur03vfifR0U5Y1Qwv2yiWHs'
    userid = 'myApp1' # 自行命名,以上三个参数均在开放平台申请

    end_point = TencentYoutuyun.conf.API_YOUTU_END_POINT  # 优图开放平台

    youtu = TencentYoutuyun.YouTu(appid, secret_id, secret_key, userid, end_point)

    with open('screenshot.png', 'wb') as fileReader:
        fileReader.write(img.getvalue())
    # 在执行generalocr()方法时,由于request对象的问题,返回中文时乱码,需要手动再指定返回对象的编码格式:r.encoding='utf-8' 详见youtu,py脚本文件
    ocrinfo = youtu.generalocr('screenshot.png', 0)
    # print(ocrinfo['items'])
    return ocrinfo['items']
优图的SDK中有个bug,但应该是requests模块的问题,当你直接用下载的SDK的时候,识别的文字还是无法正常显示中文,因为这个requests对象无法直接识别你的编码类型,所以要自己指定,修改下它的SDK,在youtu.py脚本中,generalocr方法中,添加一句代码: r.encoding='utf-8',指定为utf-8。关于优图的这个通用识别generalocr ,里面的参数说明是这样说的,如果第二个参数为0,则代表传入的是图像文件,如果是1,则是图片的url,所以在这里我最终还是将内存中图片保存成了实体文件,具体能不能直接读取内存中的文件,我并未深入研究,如果大家知道的话,请留言,谢谢!最终返回的只有我们想要的文字内容,即['items']的内容,在使用OCR识别的时候,注意它是一行一行的识别的,所以识别后的结果你会发现最后就是一个['itemstring']的列表,只分析这一部分就可以了。OK了,这一部分也so easy了。接下来,我们再分析返回后的结果,还是以这张图片为例,识别后的内容为:

{
	'errorcode': 0,
	'errormsg': 'OK',
	'items': [{
		'itemcoord': {
			'x': 109,
			'y': 219,
			'width': 705,
			'height': 53
		},
		'itemstring': '「中国工商银行」的英文缩写是?',
		'coords': [],
		'words': [{
			'character': '「',
			'confidence': 0.9919068813323975
		}, {
			'character': '中',
			'confidence': 0.9999942779541016
		}, {
			'character': '国',
			'confidence': 0.9999985694885254
		}, {
			'character': '工',
			'confidence': 0.9998493194580078
		}, {
			'character': '商',
			'confidence': 0.9999986886978149
		}, {
			'character': '银',
			'confidence': 0.999992847442627
		}, {
			'character': '行',
			'confidence': 0.9999927282333374
		}, {
			'character': '」',
			'confidence': 0.993008017539978
		}, {
			'character': '的',
			'confidence': 0.9999935626983643
		}, {
			'character': '英',
			'confidence': 0.9999959468841553
		}, {
			'character': '文',
			'confidence': 0.9999784231185913
		}, {
			'character': '缩',
			'confidence': 0.9995788931846619
		}, {
			'character': '写',
			'confidence': 0.9999926090240479
		}, {
			'character': '是',
			'confidence': 0.9999960660934448
		}, {
			'character': '?',
			'confidence': 0.9996994733810425
		}],
		'candword': []
	}, {
		'itemcoord': {
			'x': 397,
			'y': 436,
			'width': 126,
			'height': 47
		},
		'itemstring': 'ICCB',
		'coords': [],
		'words': [{
			'character': 'I',
			'confidence': 0.732905924320221
		}, {
			'character': 'C',
			'confidence': 0.9993401169776917
		}, {
			'character': 'C',
			'confidence': 0.9990763664245605
		}, {
			'character': 'B',
			'confidence': 0.9994719624519348
		}],
		'candword': []
	}, {
		'itemcoord': {
			'x': 398,
			'y': 627,
			'width': 125,
			'height': 45
		},
		'itemstring': 'ICBB',
		'coords': [],
		'words': [{
			'character': 'I',
			'confidence': 0.8364823460578918
		}, {
			'character': 'C',
			'confidence': 0.9991937279701233
		}, {
			'character': 'B',
			'confidence': 0.9999769926071167
		}, {
			'character': 'B',
			'confidence': 0.9999263286590576
		}],
		'candword': []
	}, {
		'itemcoord': {
			'x': 399,
			'y': 819,
			'width': 125,
			'height': 45
		},
		'itemstring': 'ICBC',
		'coords': [],
		'words': [{
			'character': 'I',
			'confidence': 0.8958392143249512
		}, {
			'character': 'C',
			'confidence': 0.9992052912712097
		}, {
			'character': 'B',
			'confidence': 0.9998654127120972
		}, {
			'character': 'C',
			'confidence': 0.9920558333396912
		}],
		'candword': []
	}, {
		'itemcoord': {
			'x': 397,
			'y': 1010,
			'width': 126,
			'height': 46
		},
		'itemstring': 'IBCB',
		'coords': [],
		'words': [{
			'character': 'I',
			'confidence': 0.6056796312332153
		}, {
			'character': 'B',
			'confidence': 0.9954431056976318
		}, {
			'character': 'C',
			'confidence': 0.9996951818466187
		}, {
			'character': 'B',
			'confidence': 0.9998865127563477
		}],
		'candword': []
	}],
	'session_id': '',
	'angle': 0.0
}
在返回的结果中,它给出了每一个字母每一个字的可信任度(confidence) ,我们会发现在头脑王者的答题中,好像所有的题目都是4个选项,而且每一个选项都只占一行,而题目可能会有多行,所以我们就按照这个规律,将题干和答案去分割组合,可能并不准确,但这不重要,重要的是我们要实现这个东西。我们通过返回的数据分析:

        # 分割题目和答案
        answers = [x['itemstring'] for x in infos[-4:]] # 后4项为答案
        question = ''.join([x['itemstring'] for x in infos[:-4]]) # 前面为题目
在这里infos就是刚刚返回的ocrinfo['items']了,然后每一行都是一个itemstring,按照上面的规则,后面4个为选项,前面的为题干,这样得出结果:
「中国工商银行」的英文缩写是?
['ICCB', 'ICBB', 'ICBC', 'IBCB']
走到这一步后,下面就是要去搜索题目的答案了,可能第一反应就是, 将题目放到百度或其他搜索引擎中直接查找答案,然后再去跟选项对比,但是我们知道搜索引擎返回的答案是千变万化的,比如一个日期可能是纯数字表示,也可能是汉字表示,这样我们就不知道该怎么对比了,所以在这里的处理方式是这样的,去搜索题目答案,然后从返回的结果中,查找每一个选项出现的次数,那么很容易想到的就是出现次数最多的就是正确答案咯。还是简单粗暴的贴代码吧:

# url = 'https://www.baidu.com/s' # 百度搜索
    url = 'https://www.bing.com/search' # 必应搜索

    # 请求头文件
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7'
    }
    data = {
        # 'wq':question 百度搜索
        'q': question  # 必应搜索
    }
    response = requests.get(url,params=data,headers=headers)
    response.encoding = 'utf-8'
    # 返回请求的文本
    html = response.text
    # print(html)
    # 查找答案并按照答案出现的次数排序
    for i in range(len(answers)):
        answers[i] = (html.count(answers[i]),answers[i],i)
    answers.sort(reverse=True)
    # 打印输出题目和答案
    print(question)
    print(answers)
    # 返回正确答案,即第一个答案
    return answers[0]
这里需要引入一个requests模块,去请求搜索引擎,查找我们要的信息。在该题中返回的结果为:

「中国工商银行」的英文缩写是?
[(2, 'ICBC', 2), (0, 'ICCB', 0), (0, 'ICBB', 1), (0, 'IBCB', 3)]
在返回的结果中,第一个参数为答案出现的次数,第二个为答案选项,第三个参数为选项的索引,这样我们就可以通过正确答案的索引,去实现最后一步,模拟点击选项了。根据配置信息config中,当我们知道选项的索引后,就可以知道选项所在的位置,然后去自动点击选项答案,点击手机使用的命令为:adb shell input swipe 坐标 延迟时间,注意需要引入os系统模块,以执行手机自己模拟点击,代码如下:

    cmd = 'adb shell input swipe %s %s %s %s %s' %(
        point[0],
        point[1],
        point[0]+random.randint(0,3), # 右下角的坐标随机点击
        point[1]+random.randint(0,3), # 右下角的坐标随机点击
        200 # 延迟200ms
    )
    # 执行cmd命令,根据指定的坐标在手机上模拟点击
    os.system(cmd)

最后一步我们也完成了,这样一个简单的外挂就实现了,当然它也只是一个辅助性的工具,因为实测,耗费时间太长,总是让别人抢先了,这并不重要,重要的是实现它的乐趣。

太晚了,睡觉了,晚安。

源码地址:https://github.com/Allen0910/MindKing   有问题请提Issue,谢谢!

最后再说一句,哎呀,CSDN的UI换了啊,看来是招到前端了!!!



















 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值