朋友圈曬跳一跳成績好久了,今天無意中看到以前一個同事小妞曬用代碼刷分的視頻,百度了一下果然看到了代碼(代碼在最後),幾經波折,終於成功運行,刷了一點分數。
首先大概說一下步驟:
1.百度下載刷分代碼
2.安裝adb
3.找個手機使用USB調試模式連接電腦
4.啟動跳一跳微信小程序
5.在eclipse中運行代碼(此處要不斷調試根據手機屏幕大小修改參數)
結果就是你的手機屏幕會自動按壓然後讓棋子跳。
再說下問題:
一、安裝adb問題集:
下載adb工具地址
在此處的設備管理器中,如果沒有安裝在其他設備這裡,adb就是一個感嘆號,安裝完畢後如圖所示,會出現android device 這一行
安裝的話在adb一欄裡右擊選擇屬性,彈出如下界面,點擊更新驅動程序,選擇瀏覽計算機選擇程序(也就是第二個選項),此時會彈出一個瀏覽計算機上的驅動程序選項,選擇安裝包所在地,然後一切放行,就能安裝。
問題來了:
安裝完後,你在cmd命令窗口下能使用adb,但是在eclipse中運行代碼完全沒有效果(程序不報錯,手機裡也沒有截圖),然後eclipse控制台就顯示圖片不存在,
此時需要把你安裝好的一個exe程序,兩個動態鏈接庫dll拷貝到如下兩個目錄下:(找不到就在c盤全局搜一下)
adb.exe
AdbWinApi.dll
AdbWinUsbApi.dll
C:/Windows/System32
C:/Windows/SysWOW64
此時一定要放在SysWOW64下,我是win7 64位,所以有這個目錄(網上其他人說win32就不需要放這個了,我沒試過),
如果沒有放SysWOW64目錄,此時eclipse運行依舊沒有效果:但如果你在cmd中運行adb shell screencap -p /sdcard/tencent/customerpic/current.png這條命令,發現手機裡面會有current.png圖片,這說明eclipse沒有找到對應的adb工具。
我找出這個問題是通過cd到System32和安裝目錄(C:/Program Files (x86)/Thunder Network/Thunder/Program)下,我在Program中運行上述命令成功,在System32中運行報錯:
---------------------------adb.exe - 系統錯誤---------------------------
無法啟動此程序,因為計算機中丟失AdbWinApi.DLL。嘗試重新安裝該程序以解決此問題。
---------------------------
確定
---------------------------
OK,這說明System32中的adb找不到AdbWinApi.Dll動態鏈接文件,但明明就有,機緣巧合之下,老夫看到了SysWOW64這個目錄,然後百度這個目錄是什麼意思,什麼作用,OK,將拷貝到System32目錄下的三個文件再次拷貝一份到此SysWOW64目錄下,搞定。
問題二: device offline
在cmd命令窗口運行adb shell結果報錯device offline,我以為是我adb安裝有問題,百度了一大堆,試過adb kill server,adb remount等命令都沒用,後來換了發現代碼裡面是/sdcard/,一看這應該是外置SD卡吧,是不是路徑不對,(我用的是vivo x9,這手機沒有外置SD卡選項),換了舊一點的手機(vivo y27)後運行adb shell可以了,不過/sdcard不是外置SD卡路徑,而是手機U盤路徑。
那麼就說明不應該是代碼路徑問題,又是百度了一番,被告知是adb工具太老,adb version得到版本1.0.26,好吧,我也懶得去找新版adb,用老的vivo y27調試了下能刷就行。
列舉一下我學到的:
1.知道有adb這東西,也知道使用adb shell可以得到手機的bash會話,可以截圖,使用adb pull可以從手機裡面得到文件,更多命令的話官網有,我看多了也記不住。
2.知道原來代碼裡面java使用Runtime.getRuntime().exec()可以在windows中調用系統命令:
process = Runtime.getRuntime().exec(command); System.out.println("exec command start: " + command); process.waitFor(); process.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = bufferedReader.readLine(); 3.明白代碼中通過計算截圖的RGB顏色值等分析一張圖片int pixel = bufferedImage.getRGB(x, y);
全部代碼:
package com.lw.test; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; /** * 參考知乎* * @link <a href="https://zhuanlan.zhihu.com/p/32452473" rel="external nofollow" target="_blank">https://zhuanlan.zhihu.com/p/32452473</a> * * 跳一跳輔助* * @author LeeHo */ public class JumpJumpHelper { private static final String IMAGE_NAME = "current.png"; private static final String STORE_DIR = "d:/jump_screencapture"; //數量private static final int imageLengthLength = 5; //存放圖片的大小private static final long[] imageLength = new long[imageLengthLength]; private final RGBInfo rgbInfo = new RGBInfo(); private final String path = "/sdcard/tencent/customerpic/"; private final String[] ADB_SCREEN_CAPTURE_CMDS = {"adb shell screencap -p "+path + IMAGE_NAME, "adb pull "+path+"current.png " + STORE_DIR }; //截屏中游戲分數顯示區域最下方的Y坐標,300是1920x1080的值,根據實際情況修改private final int gameScoreBottomY = 300; //按壓的時間係數,可根據具體情況適當調節private final double pressTimeCoefficient = 2.05; //按壓的起始點坐標,也是再來一局的起始點坐標private final int swipeX = 280; private final int swipeY = 600; //二分之一的棋子底座高度private final int halfBaseBoardHeight = 20; //棋子的寬度,從截屏中量取,自行調節private final int halmaBodyWidth = 74; //遊戲截屏裡的兩個跳板的中點坐標,主要用來計算角度,可依據實際的截屏計算,計算XY的比例private final int boardX1 = 813; private final int boardY1 = 1122; private final int boardX2 = 310; private final int boardY2 = 813; /** * 獲取跳棋以及下一塊跳板的中心坐標* * @return * @author LeeHo * @throws IOException * @update 2017年12月31日下午12:18:22 */ private int[] getHalmaAndBoardXYValue(File currentImage) throws IOException { BufferedImage bufferedImage = ImageIO.read(currentImage); int width = bufferedImage.getWidth(); int height = bufferedImage.getHeight(); System.out.println("寬度:" + width + ",高度:" + height); int halmaXSum = 0; int halmaXCount = 0; int halmaYMax = 0; int boardX = 0; int boardY = 0; //從截屏從上往下逐行遍歷像素點,以棋子顏色作為位置識別的依據,最終取出棋子顏色最低行所有像素點的平均值,即計算出棋子所在的坐標for (int y = gameScoreBottomY; y < height; y++) { for (int x = 0; x < width; x++) { processRGBInfo(bufferedImage, x, y); int rValue = this.rgbInfo.getRValue(); int gValue = this.rgbInfo.getGValue(); int bValue = this.rgbInfo.getBValue(); //根據RGB的顏色來識別棋子的位置, if (rValue > 50 && rValue < 60 && gValue > 53 && gValue < 63 && bValue > 95 && bValue < 110) { halmaXSum += x; halmaXCount++; //棋子底行的Y坐標值halmaYMax = y > halmaYMax ? y : halmaYMax; } } } if (halmaXSum != 0 && halmaXCount != 0) { //棋子底行的X坐標值int halmaX = halmaXSum / halmaXCount; //上移棋子底盤高度的一半int halmaY = halmaYMax - halfBaseBoardHeight; //從gameScoreBottomY開始for (int y = gameScoreBottomY; y < height; y++) { processRGBInfo(bufferedImage, 0, y); int lastPixelR = this.rgbInfo.getRValue(); int lastPixelG = this.rgbInfo.getGValue(); int lastPixelB = this.rgbInfo.getBValue(); //只要計算出來的boardX的值大於0,就表示下個跳板的中心坐標X值取到了。 if (boardX > 0) { break; } int boardXSum = 0; int boardXCount = 0; for (int x = 0; x < width; x++) { processRGBInfo(bufferedImage, x, y); int pixelR = this.rgbInfo.getRValue(); int pixelG = this.rgbInfo.getGValue(); int pixelB = this.rgbInfo.getBValue(); //處理棋子頭部比下一個跳板還高的情況if (Math.abs(x - halmaX) < halmaBodyWidth) { continue; } //從上往下逐行掃描至下一個跳板的頂點位置,下個跳板可能為圓形,也可能為方框,取多個點,求平均值if ((Math.abs(pixelR - lastPixelR) + Math.abs(pixelG - lastPixelG) + Math.abs(pixelB - lastPixelB)) > 10) { boardXSum += x; boardXCount++; } } if (boardXSum > 0) { boardX = boardXSum / boardXCount; } } //按實際的角度來算,找到接近下一個board 中心的坐標boardY = (int) (halmaY - Math.abs(boardX - halmaX) * Math.abs(boardY1 - boardY2) / Math.abs(boardX1 - boardX2)); if (boardX > 0 && boardY > 0) { int[] result = new int[4]; //棋子的X坐標result[0] = halmaX; //棋子的Y坐標result[1] = halmaY; //下一塊跳板的X坐標result[2] = boardX; //下一塊跳板的Y坐標result[3] = boardY; return result; } } return null; } /** * 執行命令* * @param command * @author LeeHo * @update 2017年12月31日下午12:13:39 */ private void executeCommand(String command) { Process process = null; try { process = Runtime.getRuntime().exec(command); System.out.println("exec command start: " + command); process.waitFor(); process.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = bufferedReader.readLine(); if (line != null) { System.out.println(line); } bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line02 = bufferedReader.readLine(); if (line02 != null) { System.out.println(line02); } System.out.println("exec command end: " + command); } catch (Exception e) { e.printStackTrace(); } finally { if (process != null) { process.destroy(); } } } /** * ADB獲取安卓截屏* * @author LeeHo * @update 2017年12月31日下午12:11:42 */ private void executeADBCaptureCommands() { for (String command : ADB_SCREEN_CAPTURE_CMDS) { executeCommand(command); } } /** * 跳一下* * @param distance * @author LeeHo * @update 2017年12月31日下午12:23:19 */ private void doJump(double distance) { System.out.println("distance: " + distance); //計算按壓時間,最小200毫秒int pressTime = (int) Math.max(distance * pressTimeCoefficient, 200); System.out.println("pressTime: " + pressTime); //執行按壓操作String command = String.format("adb shell input swipe %s %s %s %s %s", swipeX, swipeY, swipeX, swipeY, pressTime); System.out.println(command); executeCommand(command); } /** * 再來一局* * @author LeeHo * @update 2017年12月31日下午12:47:06 */ private void replayGame() { String command = String.format("adb shell input tap %s %s", swipeX, swipeY); executeCommand(command); } /** * 計算跳躍的距離,也即兩個點之間的距離* * @param halmaX * @param halmaY * @param boardX * @param boardY * @return * @author LeeHo * @update 2017年12月31日下午12:27:30 */ private double computeJumpDistance(int halmaX, int halmaY, int boardX, int boardY) { return Math.sqrt(Math.pow(Math.abs(boardX - halmaX), 2) + Math.pow(Math.abs(boardY - halmaY), 2)); } public static void main(String[] args) { JumpJumpHelper jumpjumpHelper = new JumpJumpHelper(); // String command = "adb shell screencap -p "+jumpjumpHelper.path + IMAGE_NAME; //// command = "adb devices"; // jumpjumpHelper.executeCommand(command); // // if(true){return ;} try { File storeDir = new File(STORE_DIR); if (!storeDir.exists()) { boolean flag = storeDir.mkdir(); if (!flag) { System.err.println("創建圖片存儲目錄失敗"); return; } } //執行次數int executeCount = 0; for (;;) { //執行ADB命令,獲取安卓截屏jumpjumpHelper.executeADBCaptureCommands(); File currentImage = new File(STORE_DIR, IMAGE_NAME); if (!currentImage.exists()) { System.out.println("圖片不存在"); continue; } long length = currentImage.length(); imageLength[executeCount % imageLengthLength] = length; //查看是否需要重新開局jumpjumpHelper.checkDoReplay(); executeCount++; System.out.println("當前第" + executeCount + "次執行!"); //獲取跳棋和底板的中心坐標int[] result = jumpjumpHelper.getHalmaAndBoardXYValue(currentImage); if (result == null) { System.out.println("The result of method getHalmaAndBoardXYValue is null!"); continue; } int halmaX = result[0]; int halmaY = result[1]; int boardX = result[2]; int boardY = result[3]; System.out.println("halmaX: " + halmaX + ", halmaY: " + halmaY + ", boardX: " + boardX + ", boardY: " + boardY); //計算跳躍的距離double jumpDistance = jumpjumpHelper.computeJumpDistance(halmaX, halmaY, boardX, boardY); jumpjumpHelper.doJump(jumpDistance); //每次停留2.5秒TimeUnit.MILLISECONDS.sleep(2500); } } catch (Exception e) { e.printStackTrace(); } } /** * 檢查是否需要重新開局* * @author LeeHo * @update 2017年12月31日下午1:39:18 */ private void checkDoReplay() { if (imageLength[0] > 0 && imageLength[0] == imageLength[1] && imageLength[1] == imageLength[2] && imageLength[2] == imageLength[3] && imageLength[3] == imageLength[4]) { //此時表示已經連續5次圖片大小一樣了,可知當前屏幕處於再來一局Arrays.fill(imageLength, 0); //模擬點擊再來一局按鈕重新開局replayGame(); } } /** * 獲取指定坐標的RGB值* * @param bufferedImage * @param x * @param y * @author LeeHo * @update 2017年12月31日下午12:12:43 */ private void processRGBInfo(BufferedImage bufferedImage, int x, int y) { this.rgbInfo.reset(); int pixel = bufferedImage.getRGB(x, y); //轉換為RGB數字this.rgbInfo.setRValue((pixel & 0xff0000) >> 16); this.rgbInfo.setGValue((pixel & 0xff00) >> 8); this.rgbInfo.setBValue((pixel & 0xff)); } class RGBInfo { private int RValue; private int GValue; private int BValue; public int getRValue() { return RValue; } public void setRValue(int rValue) { RValue = rValue; } public int getGValue() { return GValue; } public void setGValue(int gValue) { GValue = gValue; } public int getBValue() { return BValue; } public void setBValue(int bValue) { BValue = bValue; } public void reset() { this.RValue = 0; this.GValue = 0; this.BValue = 0; } } }當然,現在刷了一會就被清空成績,但作為一個程序員知道還是好的。從一開始的post提交漏洞,讓電腦作為代理抓包修改數據,現在代碼模擬點擊(雖然不會生效。)
更多內容大家可以參考專題《微信跳一跳》進行學習。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。