淘寶玩?zhèn)球這個(gè)游戲雖然簡單,但是想要玩高分還是很難的,畢竟有好幾億的人和你一起玩游戲,那么問題來了,淘寶玩?zhèn)球有輔助么?目前來說排名前進(jìn)的分?jǐn)?shù)還是很高的,想要玩的這個(gè)上萬分還是很難的!
淘寶玩?zhèn)球輔助有嗎?
淘寶小游戲"玩?zhèn)球"自動(dòng)執(zhí)行
本文記錄了為實(shí)現(xiàn)本游戲的自動(dòng)執(zhí)行而做的探索過程
第一階段: 通過截屏進(jìn)行判斷
1.1 基本步驟
1) 通過adb shell截屏
2) 判斷特定行的藍(lán)色和紅色像素數(shù)量
3) 通過adb shell發(fā)送指令
1.1.1 截屏
首先獲得用su獲得root權(quán)限(后面的步驟需要) 然后用screencap命令截屏, 圖片放到放到手機(jī)SD卡里, 然后通過pull命令將圖片復(fù)制到電腦上(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共用的一個(gè)類,其頭文件為atlimage.h,主要用于圖片文件的打開,顯示與保存。這里需要注意的是,在VS2010和VS2012的MFC編程中,不需要將頭文件包含進(jìn)來。MFC中要使用CImage類,必須先將頭文件包含進(jìn)來,可以包含在當(dāng)前代碼的CPP文件中,也可以包含在所屬類的頭文件中,不過最好還是包含在工程的stdafx.h文件中。CImage總共有39個(gè)成員函數(shù)。
(百度百科)
首先執(zhí)行上面的命令, 然后進(jìn)行圖片的判斷
system("D:\\input.bat");image.Load(_T("D:\\ss.png"));bool result = check(895, image);
其中check函數(shù)定義如下, 判斷第row行紅色像素和藍(lán)色像素哪個(gè)多一些.
// 返回 false代表藍(lán)色, 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);// 當(dāng)時(shí)考慮到方塊表面可以有一些輕微的漸變效果 所以設(shè)置了RGB的范圍 后來發(fā)現(xiàn)是純色 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 命令發(fā)送
使用adb提供的input命令可以模擬觸摸操作(需要root權(quán)限)
沒用root權(quán)限直接使用input tap只會(huì)顯示一個(gè)killed, 手機(jī)上沒有任何反應(yīng). 獲得root權(quán)限之后手機(jī)就有反應(yīng)了, 電腦上沒有任何報(bào)錯(cuò).
代碼如下, 首先打開一個(gè)文件 向里面寫入root授權(quán)命令和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 出現(xiàn)的問題及優(yōu)化
1.2.1 出現(xiàn)的問題
程序根本無法使用! 因?yàn)樘? root授權(quán)需要1s左右, 截屏需要1s左右, tap命令從發(fā)出到執(zhí)行也至少需要1秒左右~
所以我從針對(duì)上面的問題進(jìn)行了如下優(yōu)化
1.2.2 改用java語言
C++似乎無法獲取到adb命令的輸入流,所以只能講命令寫到文件里,adb執(zhí)行完這幾條命令就退出了;要執(zhí)行新的命令必須重啟adb,重啟就意味著要重新進(jìn)行root授權(quán),極其浪費(fèi)時(shí)間。
Java語言的優(yōu)勢是不僅可以執(zhí)行外部程序,還能獲得輸入流輸出流,可以在其它程序執(zhí)行時(shí)向其動(dòng)態(tài)寫入命令(代碼的參考資料)
try {Process mainProcess = Runtime.getRuntime().exec("adb shell");DataOutputStream os = new DataOutputStream(mainProcess.getOutputStream());os.writeBytes("su" + "\n");os.flush();//處理錯(cuò)誤輸出流final BufferedReader brError = new BufferedReader(new InputStreamReader(mainProcess.getErrorStream()));ReaderThread t2 = new ReaderThread(brError, "error");t2.start();//處理標(biāo)準(zhǔn)輸出流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語言之后, 原來的圖像處理庫就不能用了。經(jīng)過搜索發(fā)現(xiàn)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// 省略
判斷函數(shù)如下, 讀取一行像素緩存到數(shù)組中, 然后判斷這一行有多少個(gè)紅色, 多少個(gè)藍(lán)色
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個(gè)方塊的顏色, 所以首先想到的是一次輸出4個(gè)命令.
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++;}
這樣做的結(jié)果還是失敗. 設(shè)4個(gè)方塊為一組, 組內(nèi)的問題解決了,組之間仍然需要root授權(quán)、截屏等漫長的操作。
解決方案是3個(gè)方塊為一組. 在剛跳到方塊2, 還沒開始到方塊3的起跳時(shí)馬上進(jìn)行截圖, 并發(fā)出命令(要過一會(huì)才會(huì)真正執(zhí)行)
if (firstTime)result[0] = handle(image, 895); // 判斷第一行result[1] = handle(image, 815); // 判斷第二行result[2] = handle(image, 737); // 判斷第三行result[3] = handle(image, 658); // 判斷第四行
1.3 本階段總結(jié)
步數(shù)越多,小球下落的速度就越快。受限于截圖速度和發(fā)送命令的速度,做到這里程序可以實(shí)現(xiàn)跳140步。
第二階段: 經(jīng)過拍照進(jìn)行判斷
2.1 基本步驟
由于截屏速度太慢, 所以我想對(duì)手機(jī)屏幕拍照, 然后用照片來判斷, 這樣獲得照片的延遲就很小了.
基本步驟如下
1) 拍照
2) 判斷顏色
3) 發(fā)送命令
2.1.1 拍照
JavaCV是一款開源的視覺處理庫,基于GPLv2協(xié)議,對(duì)各種常用計(jì)算機(jī)視覺庫封裝后的一組jar包,封裝了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等計(jì)算機(jī)視覺編程人員常用庫的接口。
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);grabber.start(); //開始獲取攝像頭數(shù)據(jù)CanvasFrame canvas = new CanvasFrame("攝像頭");//新建一個(gè)窗口canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);canvas.setAlwaysOnTop(true);Frame f = grabber.grab(); // 獲得一幀圖像canvas.showImage(f); // 顯示到窗口中
2.1.2 判斷顏色
經(jīng)過攝像頭拍照, 方塊的顏色已經(jīng)不是純色, 外加攝像頭有自動(dòng)調(diào)節(jié)色溫和亮度和功能, 游戲背景的變化讓攝像頭不斷進(jìn)行調(diào)節(jié), 導(dǎo)致直接判斷某一塊像素的顏色是否在某個(gè)區(qū)間已經(jīng)很不準(zhǔn)確了.
我的方案是將兩個(gè)紅色矩形圈住的像素顏色的平均值作為參數(shù)(共6個(gè),R1 G1 B1 R2 G2 B2),進(jìn)行線性分類。
從攝像頭采集大量數(shù)據(jù)(共8000幀)進(jìn)行訓(xùn)練,4種情況 (左藍(lán) 右藍(lán) 左紅 右紅)各2000幀。使用某人寫的一個(gè)fisher線性判別法的分類器(鏈接)求出線性分類器所需的參數(shù)