淘宝玩个球这个游戏虽然简单,但是想要玩高分还是很难的,毕竟有好几亿的人和你一起玩游戏,那么问题来了,淘宝玩个球有辅助么?目前来说排名前进的分数还是很高的,想要玩的这个上万分还是很难的!
淘宝玩个球辅助有吗?
淘宝小游戏"玩个球"自动执行
本文记录了为实现本游戏的自动执行而做的探索过程
第一阶段: 通过截屏进行判断
1.1 基本步骤
1) 通过adb shell截屏
2) 判断特定行的蓝色和红色像素数量
3) 通过adb shell发送指令
1.1.1 截屏
首先获得用su获得root权限(后面的步骤需要) 然后用screencap命令截屏, 图片放到放到手机SD卡里, 然后通过pull命令将图片复制到电脑上(d:\ss.png)
suadb shell screencap sdcard/#swap/ss.pngadb pull /sdcard/#swap/ss.png d:\ss.png
1.1.2 加载图片, 判断颜色
最开始使用C语言编写, 使用altimage.h提供的库.
CImage类是ATL和MFC共用的一个类,其头文件为atlimage.h,主要用于图片文件的打开,显示与保存。这里需要注意的是,在VS2010和VS2012的MFC编程中,不需要将头文件包含进来。MFC中要使用CImage类,必须先将头文件包含进来,可以包含在当前代码的CPP文件中,也可以包含在所属类的头文件中,不过最好还是包含在工程的stdafx.h文件中。CImage总共有39个成员函数。
(百度百科)
首先执行上面的命令, 然后进行图片的判断
system("D:\\input.bat");image.Load(_T("D:\\ss.png"));bool result = check(895, image);
其中check函数定义如下, 判断第row行红色像素和蓝色像素哪个多一些.
// 返回 false代表蓝色, true代表红色bool check(int row, CImage& image) { int blue = 0; int red = 0; for (int i = 0; i < 1080; ++i) { COLORREF color = image.GetPixel(i, row); BYTE r = GetRValue(color); BYTE g = GetGValue(color); BYTE b = GetBValue(color);// 当时考虑到方块表面可以有一些轻微的渐变效果 所以设置了RGB的范围 后来发现是纯色 if (r >= 250 && g >= 94 && g <= 103 && b >= 97 && b <= 103) { red++; } if (r >= 50 && r <= 56 && g >= 250 && b >= 250) { blue++; } } return red > blue;}
1.1.3 命令发送
使用adb提供的input命令可以模拟触摸操作(需要root权限)
没用root权限直接使用input tap只会显示一个killed, 手机上没有任何反应. 获得root权限之后手机就有反应了, 电脑上没有任何报错.
代码如下, 首先打开一个文件 向里面写入root授权命令和input命令, 然后将adb shell命令的输入定向到该文件
ofstream f("D:\\.input");bool result = check(895, image);f << "su" << endl;if (result[i])f << "input tap " << 284 << " " << 1606 << endl;elsef << "input tap " << 797 << " " << 1608 << endl;system("adb shell < D:\\.input");
1.2 出现的问题及优化
1.2.1 出现的问题
程序根本无法使用! 因为太慢了. root授权需要1s左右, 截屏需要1s左右, tap命令从发出到执行也至少需要1秒左右~
所以我从针对上面的问题进行了如下优化
1.2.2 改用java语言
C++似乎无法获取到adb命令的输入流,所以只能讲命令写到文件里,adb执行完这几条命令就退出了;要执行新的命令必须重启adb,重启就意味着要重新进行root授权,极其浪费时间。
Java语言的优势是不仅可以执行外部程序,还能获得输入流输出流,可以在其它程序执行时向其动态写入命令(代码的参考资料)
try {Process mainProcess = Runtime.getRuntime().exec("adb shell");DataOutputStream os = new DataOutputStream(mainProcess.getOutputStream());os.writeBytes("su" + "\n");os.flush();//处理错误输出流final BufferedReader brError = new BufferedReader(new InputStreamReader(mainProcess.getErrorStream()));ReaderThread t2 = new ReaderThread(brError, "error");t2.start();//处理标准输出流final BufferedReader br = new BufferedReader(new InputStreamReader(mainProcess.getInputStream()));ReaderThread t1 = new ReaderThread(br, "std");t1.start();os.writeBytes("input tap " + (797 + random.nextInt(30) - 15) + " " + (1608 + random.nextInt(30) - 15) + "\n");os.flush();} catch (IOException e) {e.printStackTrace();}
改用Java语言之后, 原来的图像处理库就不能用了。经过搜索发现java提供图片读取的处理的功能。
import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;Process captureProcess = Runtime.getRuntime().exec(captureCommand);// TRYcaptureProcess.waitFor(); // 等待截图完成File f = new File("D:\\ss.png");BufferedImage image = ImageIO.read(f);result = handle(image, 815);// CATCH// 省略
判断函数如下, 读取一行像素缓存到数组中, 然后判断这一行有多少个红色, 多少个蓝色
static int[] colors = new int[1080];// 处理图片 返回true代表红色public static boolean handle(BufferedImage image, int row) throws Exception {int blue = 0;int red = 0;image.getRGB(0, row, 1080, 1, colors, 0, image.getWidth()); // 获得第row行像素for (int i = 0; i < 1080; ++i) {Color color = new Color(colors[i]);int r = color.getRed();int g = color.getGreen();int b = color.getBlue();if (r >= 250 && g >= 94 && g <= 103 && b >= 97 && b <= 103) red++;if (r >= 50 && r <= 56 && g >= 250 && b >= 250) blue++;}if (red < 10 && blue < 10)throw new Exception("异常状况! blue=" + blue + " red=" + red);return red > blue;}
1.2.3 一次判断多行
从每一张截图都可以得到4个方块的颜色, 所以首先想到的是一次输出4个命令.
bool result[4];result[0] = check(895, image);result[1] = check(815, image);result[2] = check(737, image);result[3] = check(658, image);while (i < 4) {if (result[i])// f << "input swipe 615 1600 615 500" << endl;f << "input tap " << 284 + rand() % 30 - 15 << " " << 1606 + rand() % 30 - 15 << endl;elsef << "input tap " << 797 + rand() % 30 - 15 << " " << 1608 + rand() % 30 - 15 << endl;i++;}
这样做的结果还是失败. 设4个方块为一组, 组内的问题解决了,组之间仍然需要root授权、截屏等漫长的操作。
解决方案是3个方块为一组. 在刚跳到方块2, 还没开始到方块3的起跳时马上进行截图, 并发出命令(要过一会才会真正执行)
if (firstTime)result[0] = handle(image, 895); // 判断第一行result[1] = handle(image, 815); // 判断第二行result[2] = handle(image, 737); // 判断第三行result[3] = handle(image, 658); // 判断第四行
1.3 本阶段总结
步数越多,小球下落的速度就越快。受限于截图速度和发送命令的速度,做到这里程序可以实现跳140步。
第二阶段: 经过拍照进行判断
2.1 基本步骤
由于截屏速度太慢, 所以我想对手机屏幕拍照, 然后用照片来判断, 这样获得照片的延迟就很小了.
基本步骤如下
1) 拍照
2) 判断颜色
3) 发送命令
2.1.1 拍照
JavaCV是一款开源的视觉处理库,基于GPLv2协议,对各种常用计算机视觉库封装后的一组jar包,封装了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等计算机视觉编程人员常用库的接口。
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);grabber.start(); //开始获取摄像头数据CanvasFrame canvas = new CanvasFrame("摄像头");//新建一个窗口canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);canvas.setAlwaysOnTop(true);Frame f = grabber.grab(); // 获得一帧图像canvas.showImage(f); // 显示到窗口中
2.1.2 判断颜色
经过摄像头拍照, 方块的颜色已经不是纯色, 外加摄像头有自动调节色温和亮度和功能, 游戏背景的变化让摄像头不断进行调节, 导致直接判断某一块像素的颜色是否在某个区间已经很不准确了.
我的方案是将两个红色矩形圈住的像素颜色的平均值作为参数(共6个,R1 G1 B1 R2 G2 B2),进行线性分类。
从摄像头采集大量数据(共8000帧)进行训练,4种情况 (左蓝 右蓝 左红 右红)各2000帧。使用某人写的一个fisher线性判别法的分类器(链接)求出线性分类器所需的参数