Java实现自动玩王铲铲的致富之路小程序游戏

文章目录

  • 前言
  • 1.调用adb命令截屏并读取到电脑
  • 2.打开游戏,提前截几个图,准备好相应的按钮位置颜色
  • 3.根据图片路径和x,y坐标,读取图片相应位置的颜色值
  • 4.根据颜色值判断如何进行触摸操作
  • 5.程序效果分析
  • 5.存在的问题
  • 6.改进思路
  • 7.改进版本,待续……
    • 7.1完善了看广告的判断,依然是基于颜色值,基于ocr的版本估计够呛了

前言

最近坐车刷到一个小游戏,王铲铲的致富之路,就是一个人拿着铁锹,背着背篓挖地,随着级别的提升,可以雇佣更多人一起挖地,挖出的土可以出售给工厂获得收益(游戏货币),货币可以用来升级人物,升级仓库,升级工厂,升级运输队,后面还可以获得一辆挖掘机,加快挖掘速度。

游戏中挖掘机需要加油,这个只能每天领取5桶,再需要加油就得看视频广告来获得了,虚拟货币无法购买。挖掘可以加速,但是也需要看视频广告,看完基本只能看着大家挖土,久而久之,等待很乏味,于是就有了编写个辅助自己玩的想法。

基本思路就是Java调用adb连接手机,查看对应按钮的位置,模拟点击加延时操作,逐渐实现了,连续加速功能,加速结束后,接着点击加速,在等待加速的过程中会出现挖出的古董,可以直接点击捐赠,挖出的土疙瘩,直接关闭页面,这样不影响游戏的流程。

硬件方面:android系统手机一个,电脑(Windows,Linux,Mac均可)一个,usb调试线(wifi也行)
软件方面:jdk8,android8.0,Ubuntu20.04
前置知识:什么是adb,如何使用adb命令

1.调用adb命令截屏并读取到电脑

不啰嗦了,直接上代码了。这个类有Java版,参考:链接: Java 调用 ADB 命令截取安卓手机屏幕到PC,详细代码见文章最后。

核心原理

adb shell screencap -p /sdcard/scrsnp.png
# 截屏用时:3231 ms
adb pull /sdcard/scrsnp.png ./capture/a.png
# pull文件用时:215 ms
adb shell rm /sdcard/scrsnp.png
#删除sd卡文件

2.打开游戏,提前截几个图,准备好相应的按钮位置颜色

  这个步骤,打开游戏,截屏即可,主要关注右下角的超级加速按钮,广告页面的关闭按钮,底部中间的关闭按钮,古董捐赠按钮,等等按钮的位置,可以参考代码。

  为了方便查找触摸坐标,开启了触摸位置显示。

3.根据图片路径和x,y坐标,读取图片相应位置的颜色值

void getColor() {
        try {
            // 读取图片文件
            BufferedImage image = ImageIO.read(
                    new File("/home/xxx/图片/wcc/screenshotwcc02.png")
            );

            // 定义需要提取颜色的坐标
            int x = 302;
            int y = 314;

            // 提取坐标颜色
            int color = image.getRGB(x, y);

            // 打印颜色值
            System.out.println("The color at (" + x + ", " + y + ") is: " + color);

            int alpha = (color >> 24) & 0xff;
            int red = (color >> 16) & 0xff;
            int green = (color >> 8) & 0xff;
            int blue = color & 0xff;
            System.out.printf("argb:%x,%x,%x,%x,", alpha, red, green, blue);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4.根据颜色值判断如何进行触摸操作

核心原理如下,Java代码在后面

adb shell input tap $x $y  

5.程序效果分析

把这2个类复制到IDE中,运行即可实现功能。

  • 经过实际测试发现,基于固定坐标颜色判定的辅助方式,在关闭土疙瘩和捐赠挖掘到的古董方面还是很稳定的,基本没有遇到错误。

  • 然而在点击加速的时候就很不靠谱了,首先广告有多种:视频、直播、小说;有的时候是分享,并不是看广告,这块处理起来比较麻烦。

  • 有条件的可以试试图片文字识别,这样更稳定一些,还有不太好判断游戏是否在前台,游戏退后台了,不加判断,容易误触。

5.存在的问题

  基于颜色识别准确度不可靠,有时候游戏退后台了,程序还在敬业的点击操作,点击位置已经是驴唇不对马嘴了。而且胡乱点击也比较危险。

6.改进思路

  后续应该改为图片文字识别,借助Tess4J或者百度ai的开放api,这样更加准确和可靠。下面是代码:Wcc.java

  这个类用到了上面的截屏类ScreenCapture.kt。代码比较简陋,基本实现了所需功能,可靠性还有待提升。本文代码取色代码部分参考文心一言。

package learn;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 王铲铲的致富之路Java辅助
 */
public class Wcc {

    Color COLOR_TUGEDA = new Color(0xF0, 0xE1, 0x53);
    Color COLOR_GUDONG = new Color(0xCC, 0x5E, 0x5B);
    Color COLOR_CJJS = new Color(0xE1, 0x90, 0x25);
    Color COLOR_JSLQ = new Color(0x60, 0x34, 0x2c);
    private int c = 0;


    public static void main(String[] args) {
        Wcc wcc = new Wcc();
        wcc.digHole();
    }

    /**
     * 1.检测当前能不能加速,
     * 能
     *   则看广告加速
     * 不能,
     *   看看是图疙瘩还是古董,
     *   土疙瘩
     *     关闭
     *   古董
     *     捐赠
     *
     * 2.if(处理完毕土疙瘩和古董)
     *     查看加速是否结束
     *     没有,继续等待
     *     有,继续加速
     *
     * 3.(每30s检查一次土疙瘩和古董)
     */
    private void digHole() {
    //读取设备,准备adb操作。
        ScreenCapture capture = new ScreenCapture();
        String[] ds = capture.getDevices();
        System.out.println("设备列表容量:" + ds.length);


        if (ds.length > 0) {
            String model = capture.getModel(ds[0]);
            System.out.println("手机型号:" + model);

            while (true) {
                long beginTime = System.currentTimeMillis();
                System.out.println("---------------------------------------------");
                System.out.println("集中火力干事业,把钱花在刀把上");

                System.out.println("第" + (++c) + "次任务开始:" + getDateTime());
                capture.snapshot(ds[0], "./capture", "a.png");
                cmp(capture);

                System.out.println("睡觉去了,30秒后起床");
                //睡眠30s,相当于每隔30s处理一下土疙瘩和古董
                sleep(30);
                //睡眠10分钟,等待加速效果结束
//                sleep(60 * 10);
            }
        }
    }

    String getDateTime() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //    println(formatDateTime)
        return now.format(formatter);
    }

    void cmp(ScreenCapture capture) {
        File f = new File("./capture/a.png");
        System.out.println(f.toString());
        if (f.exists()) {
            System.out.println("文件存在");
            //加速坐标,超级加速
            int jsX = 968;
            int jsY = 1819;
            Color colorGet = getColor(f.getPath(), jsX, jsY);
            double similarity = ColorSimilarity.compare(COLOR_CJJS, colorGet);
            System.out.println("超级加速相似度:" + similarity);


            //加速冷却按钮,小于0.9表示可点击,否则是冷却状态,不可点击
            Color colorGetJslq = getColor(f.getPath(), 448, 1837);
            double similarityJslq = ColorSimilarity.compare(COLOR_JSLQ, colorGetJslq);
            System.out.println("加速冷却相似度:" + similarityJslq);
            if (similarityJslq < 0.9) {
                System.out.println("加速可点击,可以点击超级加速");
            } else {
                System.out.println("加速冷却中……现在不可加速");
            }

            if (similarity >= 0.90 && similarityJslq < 0.9) {//点击超级加速
                System.out.println("超级加速颜色相似度OK,点击超级加速");
                /* 这里面没有考虑分享功能,尚待改进 */
                /* 还有小说广告、直播广告没有考虑 */
                //此时位于游戏主页面,可以点击超级加速
                capture.touchXY(903, 1825);
                sleep(2);
                //点击观看
                capture.touchXY(575, 1219);
                //等待视频播放结束
                sleep(40);
                //点击视频完成后的关闭按钮
                capture.touchXY(977, 105);
                sleep(5);

                // 点击放弃奖励按钮(只考虑5分钟的情况)
                capture.touchXY(550, 1308);
                sleep(5);

//                //点击第二次观看
//                capture.touchXY(575, 1219);
//                //等待视频播放结束
//                sleep(40);
//                //点击视频完成后的关闭按钮
//                capture.touchXY(977, 105);
//                sleep(3);
            } else {//土疙瘩点击返回,古董点击捐赠,新材料点击知道了
                System.out.println("颜色相似度不符,目前不能点击超级加速,看看是土疙瘩还是古董");
                //游戏被弹出窗口挡住了
                //根据横条的颜色坐标,判断是土疙瘩还是古董
                int htX = 302;
                int htY = 314;
                Color colorGetTG = getColor(f.getPath(), htX, htY);
                double similarityTG = ColorSimilarity.compare(COLOR_TUGEDA, colorGetTG);
                System.out.println("土疙瘩颜色相似度:" + similarityTG);

                if (similarityTG >= 0.9) {//是土疙瘩
                    System.out.println("是土疙瘩,关闭即可:");
                    //点击返回按钮关闭土疙瘩
                    capture.touchXY(76, 121);
                    sleep(3);
                } else {
                    similarityTG = ColorSimilarity.compare(COLOR_GUDONG, colorGetTG);
                    System.out.println("古董颜色相似度:" + similarityTG);
                    if (similarityTG >= 0.9) {//是古董
                        System.out.println("是古董,点击捐赠按钮");
                        //点击捐赠按钮
                        capture.touchXY(376, 1777);
                        sleep(3);
                    } else {//意外
                        System.out.println("不能加速,不是土疙瘩,不是古董,这就奇怪了...莫不是正在加速?");
                    }
                }
            }
        } else {
            System.out.println("截图读取失败,得检查一下问题再继续");
        }
    }

    private static void sleep(int seconds) {
        try {
            Thread.sleep(1000 * seconds);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    Color getColor(String filePath, int x, int y) {
        try {
            // 读取图片文件
            BufferedImage image = ImageIO.read(
                    new File(filePath)
            );

            // 提取坐标颜色
            int color = image.getRGB(x, y);

            // 打印颜色值
            System.out.println("The color at (" + x + ", " + y + ") is: " + color);

            int alpha = (color >> 24) & 0xff;
            int red = (color >> 16) & 0xff;
            int green = (color >> 8) & 0xff;
            int blue = color & 0xff;
            System.out.printf("argb:%x,%x,%x,%x,", alpha, red, green, blue);

            Color color2 = new Color(red, green, blue);

            return color2;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

package learn

import java.io.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*

class ScreenCapture {
    val devices: Array<String>
        get() {
            val command = "adb devices"
            println(command)
            val devices = ArrayList<String>()
            try {
                val process = Runtime.getRuntime().exec(command)
                val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
                var line = bufferedReader.readLine()
                while (line != null) {
                    println(line)
                    if (line.endsWith("device")) {
                        val device = line.substring(0, line.length - "device".length).trim { it <= ' ' }
                        devices.add(device)
                    }
                    line = bufferedReader.readLine()
                }
                process.destroy()
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return devices.toArray(arrayOf())
        }

    fun getModel(device: String): String? {
        val command = "adb -s $device shell getprop"
        println(command)
        var model: String? = null
        try {
            val process = Runtime.getRuntime().exec(command)
            val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
            var line = bufferedReader.readLine()
            while (line != null) {
                if (line.contains("[ro.product.model]:")) {
                    model = line.substring("[ro.product.model]:".length).trim { it <= ' ' }
                    model = model.substring(1, model.length - 1)
                    break
                }
                line = bufferedReader.readLine()
            }
            process.destroy()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return model
    }

    fun snapshot(device: String, toPath: String, toFile: String) {
//        var f = File(toFile)
//        println(f.toString())
//        if (f.exists()) {
//            var delete = f.delete()
//            println("同名文件已经存在,删除它,结果${delete}")
//        }
        val temp = "scrsnp.png"
        val t0 = Date().time
        val command1 = "adb -s $device shell screencap -p /sdcard/$temp"
        println(command1)
        cmdExecute(command1)
        val t1 = Date().time
        println("截屏用时:${t1 - t0} ms")
        val command2 = "adb -s $device pull /sdcard/$temp $toPath/$toFile"
        println(command2)
        cmdExecute(command2)
        val t2 = Date().time
        println("pull文件用时:${t2 - t1} ms")
        val command3 = "adb -s $device shell rm /sdcard/$temp"
        println(command3)
        cmdExecute(command3)
        val t3 = Date().time
        println("删除sd卡图片用时:${t3 - t2} ms")
    }

    private fun cmdExecute(command: String) {
        try {
            val process = Runtime.getRuntime().exec(command)
            process.waitFor()
            process.destroy()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun touchXY(x: Int, y: Int) {
        //adb shell input tap $kspx $kspy
        var device = devices[0]
        var cmd = "adb -s $device shell input tap $x $y"
        cmdExecute(cmd)
    }

}

7.改进版本,待续……

7.1完善了看广告的判断,依然是基于颜色值,基于ocr的版本估计够呛了

基于颜色判断目前,基本满足了需求

package learn;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 王铲铲的致富之路Java辅助
 */
public class Wcc {

    Color COLOR_TUGEDA = new Color(0xF0, 0xE1, 0x53);
    Color COLOR_GUDONG = new Color(0xCC, 0x5E, 0x5B);
    Color COLOR_CJJS = new Color(0xE1, 0x90, 0x25);
    Color COLOR_JSLQ = new Color(0x60, 0x34, 0x2c);
    Color COLOR_XCL = new Color(0xCC, 0x5E, 0x5B);
    private int c = 0;


    public static void main(String[] args) {
        Wcc wcc = new Wcc();
        wcc.digHole();
    }

    /**
     * 1.检测当前能不能加速,
     * 能
     *   则看广告加速
     * 不能,
     *   看看是图疙瘩还是古董,
     *   土疙瘩
     *     关闭
     *   古董
     *     捐赠
     *
     * 2.if(处理完毕土疙瘩和古董)
     *     查看加速是否结束
     *     没有,继续等待
     *     有,继续加速
     *
     * 3.(每30s检查一次土疙瘩和古董)
     */
    private void digHole() {
        ScreenCapture capture = new ScreenCapture();
        String[] ds = capture.getDevices();
        System.out.println("设备列表容量:" + ds.length);


        if (ds.length > 0) {
            String model = capture.getModel(ds[0]);
            System.out.println("手机型号:" + model);

            while (true) {
                long beginTime = System.currentTimeMillis();
                System.out.println("---------------------------------------------");
                System.out.println("集中火力干事业,把钱花在刀把上");

                System.out.println("第" + (++c) + "次任务开始:" + getDateTime());
                capture.snapshot(ds[0], "./capture", "a.png");
                cmp(capture);

                System.out.println("睡觉去了,30秒后起床");
                //睡眠30s,相当于每隔30s处理一下土疙瘩和古董
                sleep(30);
                //睡眠10分钟,等待加速效果结束
//                sleep(60 * 10);
            }
        }
    }

    String getDateTime() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //    println(formatDateTime)
        return now.format(formatter);
    }

    void cmp(ScreenCapture capture) {
        File f = new File("./capture/a.png");
        System.out.println(f.toString());
        if (f.exists()) {
            System.out.println("文件存在");
            //加速坐标,超级加速
            int jsX = 968;
            int jsY = 1819;
            Color colorGet = getColor(f.getPath(), jsX, jsY);
            double similarity = ColorSimilarity.compare(COLOR_CJJS, colorGet);
            System.out.println("超级加速相似度:" + similarity);


            //加速冷却按钮,小于0.9表示可点击,否则是冷却状态,不可点击
            Color colorGetJslq = getColor(f.getPath(), 448, 1837);
            double similarityJslq = ColorSimilarity.compare(COLOR_JSLQ, colorGetJslq);
            System.out.println("加速冷却相似度:" + similarityJslq);
            if (similarityJslq < 0.9) {
                System.out.println("加速可点击,可以点击超级加速");
            } else {
                System.out.println("加速冷却中……现在不可加速");
            }

            if (similarity >= 0.90 && similarityJslq < 0.9) {//点击超级加速
                System.out.println("超级加速颜色相似度OK,点击超级加速");
                /* 这里面没有考虑分享功能,尚待改进 */
                /* 还有小说广告、直播广告没有考虑 */
//                //此时位于游戏主页面,可以点击超级加速
//                capture.touchXY(903, 1825);
//                sleep(2);
//                //点击观看
//                capture.touchXY(575, 1219);
//                //等待视频播放结束
//                sleep(40);
//                //点击视频完成后的关闭按钮
//                capture.touchXY(977, 105);
//                sleep(5);
//
//                // 点击放弃奖励按钮(只考虑5分钟的情况)
//                capture.touchXY(550, 1308);
//                sleep(5);


                /begin增强逻辑判断
                //此时位于游戏主页面,可以点击超级加速
                capture.touchXY(903, 1825);
                sleep(2);
                //点击观看,然后等待5秒
                capture.touchXY(575, 1219);
                sleep(5);
                //再次截屏判断分享、小说、视频
                capture.snapshot(capture.getDevices()[0], "./capture", "b.png");
                File fb = new File("./capture/b.png");
                // 读取图片文件
                BufferedImage image = getBufferedImage(fb.getPath());

                //判断顺序,1.分享2.小说3.视频
                BufferedImage subimage = image.getSubimage(150, 40, 400, 70);
                Color wx = new Color(0xEC, 0xEC, 0xEC);
                boolean bWx = hasOnlyExpectedColor(subimage, wx);
                Color novel = Color.BLACK;
                boolean bNov = hasOnlyExpectedColor(subimage, novel);
                System.out.println("是微信分享吗:"+bWx + ", 是小说吗:"+bNov);
                if (bWx) {
                    System.out.println("是微信分享");
                    //点击微信分享完成后的关闭按钮
                    capture.touchXY(50, 135);
                    sleep(5);

                    // 点击放弃奖励按钮(只考虑5分钟的情况)
                    capture.touchXY(550, 1308);
                    sleep(5);
                } else if (bNov) {
                    System.out.println("是小说广告");
                    //点击小说广告完成后的关闭按钮
                    capture.touchXY(91, 89);
                    sleep(5);

                    // 点击放弃奖励按钮(只考虑5分钟的情况)
                    capture.touchXY(550, 1308);
                    sleep(5);
                } else { // 视频广告
                    System.out.println("是视频广告");
                    //等待视频播放结束,时间缩短,前面休眠时间挺长了
                    sleep(32);
                    //点击视频完成后的关闭按钮
                    capture.touchXY(977, 105);
                    sleep(5);

                    // 点击放弃奖励按钮(只考虑5分钟的情况)
                    capture.touchXY(550, 1308);
                    sleep(5);
                }

                /end增强逻辑判断


//                //点击第二次观看
//                capture.touchXY(575, 1219);
//                //等待视频播放结束
//                sleep(40);
//                //点击视频完成后的关闭按钮
//                capture.touchXY(977, 105);
//                sleep(3);
            } else {//土疙瘩点击返回,古董点击捐赠,新材料点击知道了
                System.out.println("颜色相似度不符,目前不能点击超级加速,看看是土疙瘩还是古董");
                //游戏被弹出窗口挡住了
                //根据横条的颜色坐标,判断是土疙瘩还是古董
                int htX = 302;
                int htY = 314;
                Color colorGetTG = getColor(f.getPath(), htX, htY);
                double similarityTG = ColorSimilarity.compare(COLOR_TUGEDA, colorGetTG);
                System.out.println("土疙瘩颜色相似度:" + similarityTG);

                if (similarityTG >= 0.9) {//是土疙瘩
                    System.out.println("是土疙瘩,关闭即可:");
                    //点击返回按钮关闭土疙瘩
                    capture.touchXY(76, 121);
                    sleep(3);
                } else {
                    similarityTG = ColorSimilarity.compare(COLOR_GUDONG, colorGetTG);
                    System.out.println("古董颜色相似度:" + similarityTG);
                    if (similarityTG >= 0.9) {//是古董
                        System.out.println("是古董,点击捐赠按钮");
                        //点击捐赠按钮
                        capture.touchXY(376, 1777);
                        sleep(3);
                    } else {//看看是不是新材料出现了
                        //根据新材料的条幅位置判断颜色是否是新材料
                        int newX = 210;
                        int newY = 400;
                        Color colorNew = getColor(f.getPath(), newX, newY);
                        double similarityNew = ColorSimilarity.compare(COLOR_XCL, colorNew);
                        System.out.println("新材料颜色相似度:" + similarityNew);
                        if (similarityNew >= 0.9) {
                            System.out.println("是新材料,点击知道了按钮");
                            //点击知道了按钮
                            capture.touchXY(530, 1410);
                            sleep(3);
                        } else {//意外
                            System.out.println("不能加速,不是土疙瘩,不是古董,不是新材料,这就奇怪了...莫不是正在加速?");
                        }
                    }
                }
            }
        } else {
            System.out.println("截图读取失败,得检查一下问题再继续");
        }
    }

    private static void sleep(int seconds) {
        try {
            Thread.sleep(1000 * seconds);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    Color getColor(String filePath, int x, int y) {
        // 读取图片文件
        BufferedImage image = getBufferedImage(filePath);

        // 提取坐标颜色
        int color = image.getRGB(x, y);

        // 打印颜色值
        System.out.println("The color at (" + x + ", " + y + ") is: " + color);

        int alpha = (color >> 24) & 0xff;
        int red = (color >> 16) & 0xff;
        int green = (color >> 8) & 0xff;
        int blue = color & 0xff;
        System.out.printf("argb:%x,%x,%x,%x,", alpha, red, green, blue);

        Color color2 = new Color(red, green, blue);

        return color2;
    }

    BufferedImage getBufferedImage(String filePath) {
        // 读取图片文件
        try {
            BufferedImage image = ImageIO.read(new File(filePath));
            return image;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 图片只包含期望的颜色,用来判断小说、视频、分享
     * @param image
     * @param expColor
     * @return
     */
    boolean hasOnlyExpectedColor(BufferedImage image, Color expColor) {
        int expectedColor = expColor.getRGB();
        int c = 0;
        for (int i = 0; i < image.getWidth(); i++) {
            for (int j = 0; j < image.getHeight(); j++) {
                int actualColor = image.getRGB(i, j);
                if (actualColor != expectedColor) {
                    System.out.println("The image contains other colors.");
                    System.out.println("but the found expected color numbers have:" + c);
                    return false;
                } else {
                    c++;
                }
            }
        }
        System.out.println("The image contains only the expected color.");
        return true;
    }
}

你可能感兴趣的:(java,小程序,游戏)