微信跳一跳輔助Java代碼實現(xiàn)
微信跳一跳輔助的Java具體實現(xiàn)代碼,供大家參考,具體內(nèi)容如下
1.參考知乎教你用Python來玩微信跳一跳,鑒于本人Python一直都是半吊子水平,之前打算用python刷分,可無奈安裝python環(huán)境各種模塊缺失,報錯不停,于是乎,使用Java重新實現(xiàn)了一下。
2.環(huán)境配置及相關(guān)說明:
1)、Windows系統(tǒng),本人win10
2)、AVA環(huán)境安裝,JDK7以上即可
3)、安卓手機一部、數(shù)據(jù)線一條
4)、電腦安裝ADB驅(qū)動,連接安卓手機,同時打開USB調(diào)試模式
5)、打開微信小程序的跳一跳游戲,JAVA程序跑起來,具體代碼往下看
6)、本人所用為魅藍note2安卓手機,屏幕 分辨率1920x1080,不同型號的手機,可能需要調(diào)整相關(guān)參數(shù),具體看代碼注釋
7)、增加了刷分失敗后游戲自動重新開局功能
8)、娛樂而已,不要較真,據(jù)說微信官方已經(jīng)關(guān)注,分數(shù)太高可能會被清零,哈哈
3、廢話不多說,上代碼:
package com.yihusitian.gamehelper;
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 rel="external nofollow" 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";
//數(shù)量
private static final int imageLengthLength = 5;
//存放圖片的大小
private static final long[] imageLength = new long[imageLengthLength];
private final RGBInfo rgbInfo = new RGBInfo();
private final String[] ADB_SCREEN_CAPTURE_CMDS =
{ "adb shell screencap -p /sdcard/" + IMAGE_NAME,
"adb pull /sdcard/current.png " + STORE_DIR };
//截屏中游戲分數(shù)顯示區(qū)域最下方的Y坐標,300是 1920x1080的值,根據(jù)實際情況修改
private final int gameScoreBottomY = 300;
//按壓的時間系數(shù),可根據(jù)具體情況適當調(diào)節(jié)
private final double pressTimeCoefficient = 1.35;
//按壓的起始點坐標,也是再來一局的起始點坐標
private final int swipeX = 550;
private final int swipeY = 1580;
//二分之一的棋子底座高度
private final int halfBaseBoardHeight = 20;
//棋子的寬度,從截屏中量取,自行調(diào)節(jié)
private final int halmaBodyWidth = 74;
//游戲截屏里的兩個跳板的中點坐標,主要用來計算角度,可依據(jù)實際的截屏計算,計算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;
//從截屏從上往下逐行遍歷像素點,以棋子顏色作為位置識別的依據(jù),最終取出棋子顏色最低行所有像素點的平均值,即計算出棋子所在的坐標
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();
//根據(jù)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;
}
/**
* 執(zhí)行命令
*
* @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();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = bufferedReader.readLine();
if (line != null)
{
System.out.println(line);
}
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);
//執(zhí)行按壓操作
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)
{
try
{
File storeDir = new File(STORE_DIR);
if (!storeDir.exists()) {
boolean flag = storeDir.mkdir();
if (!flag) {
System.err.println("創(chuàng)建圖片存儲目錄失敗");
return;
}
}
JumpJumpHelper jumpjumpHelper = new JumpJumpHelper();
//執(zhí)行次數(shù)
int executeCount = 0;
for (;;)
{
//執(zhí)行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 + "次執(zhí)行!");
//獲取跳棋和底板的中心坐標
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])
{
//此時表示已經(jīng)連續(xù)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);
//轉(zhuǎn)換為RGB數(shù)字
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;
}
}
}
更多內(nèi)容大家可以參考專題《微信跳一跳》進行學習。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot?AOP?Redis實現(xiàn)延時雙刪功能實戰(zhàn)
本文主要介紹了SpringBoot?AOP?Redis實現(xiàn)延時雙刪功能實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08
SpringBoot項目執(zhí)行腳本 自動拉取最新代碼并重啟的實例內(nèi)容
在本篇文章里小編給大家整理的是一篇關(guān)于SpringBoot項目執(zhí)行腳本 自動拉取最新代碼并重啟的實例內(nèi)容,有需要的朋友們參考下。2019-12-12
Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換詳解
這篇文章主要給大家介紹了關(guān)于Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11
JAVA入門教學之快速搭建基本的springboot(從spring boot到spring cloud)
本文主要入門者介紹怎么搭建一個基礎(chǔ)的springboot環(huán)境,本文通過圖文并茂的形式給大家介紹從spring boot到spring cloud的完美搭建過程,適用java入門教學,需要的朋友可以參考下2021-02-02
詳解如何將JAVA程序制作成可以直接執(zhí)行的exe文件
這篇文章主要介紹了詳解如何將JAVA程序制作成可以直接執(zhí)行的exe文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09

