“穿越福城”助手

更新 ( 2019.1.30 ) :

总算是研究出来它们之间的几何关系了。

设左上角为坐标原点,向右为x正方向,向下为y正方向。若企鹅的坐标为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),目标点的坐标为 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),此时最合适的板长是
∣ x 2 − x 1 ∣ 3 + ∣ y 1 − y 2 ∣ \frac{|x_2-x_1|}{\sqrt 3} + |y_1-y_2| 3 x2x1+y1y2

知道了这个关系之后,只要不厌,就可以一直刷下去…本来想刷到666的,结果错过了…

在这里插入图片描述

新的代码还有点小问题,等调试清楚了再挂上来吧。
旧的代码保留在下面。


以下原文:

献丑。

只能跑到223,提高分数的瓶颈在于我实在摸不透这款游戏中距离和尺寸之间的函数…

我发现这个东西还不是特别稳定。晚上跑出来的成绩比下午的差远了…这个代码就丢了吧,我也不想改进了。现在感觉这个有点意思:“灯笼高高挂”助手

主要思想:

先用RGB匹配出企鹅脚(它可能没有脚,那就企鹅屁股吧)的位置和目标点中央的菱形的位置。算出它们之间的欧几里得距离。计算菱形的尺寸(可以以对角线长,边长等菱形的属性作为标准)。用以上参数计算出企鹅前的白板需要延长到多长。控制鼠标按下,这时候企鹅前面的白板在不断延长。在这个过程中用RGB方法一直匹配白板,算出白板每一时刻的长度。当白板延长到需要的长度的时候控制鼠标松开。

下面的代码可能有一些注释。

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;

public class Assistant 
{
    final int LEFT_X = 6, LEFT_Y = 200, RIGHT_X = 374, RIGHT_Y = 820; // 游戏界面的位置,我习惯把它放在电脑屏幕的左侧
    
	final int PEN_R = 47, PEN_B = 44, PEN_G = 44; // 企鹅颜色
	
	final int DES_R = 253, DES_B = 68, DES_G = 66; // 目标点的中央的菱形的颜色	
	final int SUR_R = 255, SUR_B = 217, SUR_G = 116; //  目标点的中央的菱形的外面一圈空心菱形的颜色

	final int BOA_R = 189, BOA_B = 181, BOA_G = 182; // 白板暗时颜色,此时企鹅往右走
	final int BOA2_R = 239, BOA2_B = 233, BOA2_G = 233; // 白板亮时颜色,此时企鹅往左走
    
    double alpha; // 记录当前菱形尺寸
    
    BufferedImage screenCut; // 游戏截图
    int R[][], B[][], G[][]; 
    
    void getScreenCut() throws AWTException // 截图
    {
        Robot robot = new Robot();
        screenCut = robot.createScreenCapture(new Rectangle(LEFT_X, LEFT_Y, RIGHT_X-LEFT_X, RIGHT_Y-LEFT_Y));
        initRGB();
    }
    
    void initRGB() // 得到每个位置RGB
    {
        R = new int[RIGHT_X+5][RIGHT_Y+5];
        B = new int[RIGHT_X+5][RIGHT_Y+5];
        G = new int[RIGHT_X+5][RIGHT_Y+5];
        for(int x = LEFT_X; x < RIGHT_X; x++)
            for(int y = LEFT_Y; y < RIGHT_Y; y++)
            {
                int RGB = screenCut.getRGB(x-LEFT_X, y-LEFT_Y);
                B[x][y] = RGB & 0xFF;
                G[x][y] = RGB >> 8 & 0xFF;
                R[x][y] = RGB >> 16 & 0xFF;
            }
    }
    
    void alwaysGetRGB() throws AWTException, InterruptedException // 最终程序用不到。写程序时用来取颜色,是一个不断显示鼠标指向位置RGB值的死循环
    {
        for(;true;)
        {
            Thread.sleep(100);
            Robot robot = new Robot();
            BufferedImage screenCut = robot.createScreenCapture(new Rectangle(0, 0, 2000, 1000));
            Point point = MouseInfo.getPointerInfo().getLocation();
            int x = point.x, y = point.y;
            int RGB = screenCut.getRGB(x, y);
            int B = RGB & 0xFF, G = RGB >> 8 & 0xFF, R = RGB >> 16 & 0xFF;       
            System.out.println(x + ", " + y + " : " + R + " " + G + " " + B);
        }
    }
    
    boolean equalRGB(int R1, int B1, int G1, int R2, int B2, int G2) // 容许误差的RGB相等
    {
        return Math.abs(R1-R2) + Math.abs(B1-B2) + Math.abs(G1-G2) < 10;
    }
    
    Point findPenguin() throws AWTException // 返回企鹅坐标
    {
        getScreenCut();
        int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
        int maxX = 0, maxY = 0;
        
        boolean found = false; // 此次有没有找到
        
        for(int x = LEFT_X + 20; x < RIGHT_X - 20; x++)
            for(int y = LEFT_Y + 20; y < 650; y++)
            {
                if(equalRGB(PEN_R, PEN_G, PEN_B, R[x][y-4], B[x][y-4], G[x][y-4])
                && equalRGB(PEN_R, PEN_G, PEN_B, R[x-2][y-4], B[x-2][y-4], G[x-2][y-4])
                && equalRGB(PEN_R, PEN_G, PEN_B, R[x+2][y-4], B[x+2][y-4], G[x+2][y-4])) // 判断这个点周围是不是均为企鹅的颜色,要不然可能误判
                {
                    minX = Math.min(minX, x);
                    minY = Math.min(minY, y);
                    maxX = Math.max(maxX, x);
                    maxY = Math.max(maxY, y);
                    found = true;
                }
            }
        if(!found) return findPenguin();
        else return new Point((int)(minX * 0.5 + maxX * 0.5), (int)(minY * 0.2 + maxY * 0.8) + 3); // 加权位置
    }
    
    Point findDestination(int limitY) throws AWTException // 返回菱形坐标,传参数加速并减少干扰
    {
        getScreenCut();
        int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
        int maxX = 0, maxY = 0;
        
        boolean found = false;

        for(int x = LEFT_X + 20; x < RIGHT_X - 20; x++)
            for(int y = LEFT_Y + 20; y < limitY - 20; y++)
            {
                if(equalRGB(DES_R, DES_G, DES_B, R[x][y], B[x][y], G[x][y]))
                {
                    if(!equalRGB(SUR_R, SUR_G, SUR_B, R[x-10][y], B[x-10][y], G[x-10][y])
                    && !equalRGB(SUR_R, SUR_G, SUR_B, R[x][y-10], B[x][y-10], G[x][y-10])
                    && !equalRGB(SUR_R, SUR_G, SUR_B, R[x+10][y], B[x+10][y], G[x+10][y])
                    && !equalRGB(SUR_R, SUR_G, SUR_B, R[x][y+10], B[x][y+10], G[x][y+10])) // 判断这个点周围是不是被另一种颜色的空心菱形包围着
                        continue;
                    
                    minX = Math.min(minX, x);
                    minY = Math.min(minY, y);
                    maxX = Math.max(maxX, x);
                    maxY = Math.max(maxY, y);
                    found = true;
                }
            }
        if(!found) return findDestination(limitY);
        else
        {
            alpha = maxX - minX;
            return new Point((minX + maxX) / 2, (minY + maxY) / 2);
        }
    }

    boolean findBoard(double dis, double limitY) throws AWTException // 寻找白板,当白板长度超过dis时返回假,limitY用于加速、减少干扰并作为白板下界
    {
        getScreenCut();
        int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
        int maxX = 0, maxY = 0;
        
        boolean found = false;
        
        for(int x = LEFT_X + 20; x < RIGHT_X - 20; x++)
            for(int y = LEFT_Y + 20; y < limitY - 30; y++)
            {
                // 白板应当是垂直的,与企鹅垂直连线上颜色都相同的一条带
                
                boolean flag1 = true;
                for(int k = y; k < limitY - 20; k++)  // 白板暗时
                    if(!equalRGB(BOA_R, BOA_G, BOA_B, R[x][k], B[x][k], G[x][k])
                    || !equalRGB(BOA_R, BOA_G, BOA_B, R[x-2][k], B[x-2][k], G[x-2][k])
                    || !equalRGB(BOA_R, BOA_G, BOA_B, R[x+2][k], B[x+2][k], G[x+2][k]))
                    {
                        flag1 = false;
                        break;
                    }
                
                boolean flag2 = true;
                    for(int k = y; k < limitY - 20; k++)  // 白板亮时
                        if(!equalRGB(BOA2_R, BOA2_G, BOA2_B, R[x][k], B[x][k], G[x][k])
                        || !equalRGB(BOA2_R, BOA2_G, BOA2_B, R[x-2][k], B[x-2][k], G[x-2][k])
                        || !equalRGB(BOA2_R, BOA2_G, BOA2_B, R[x+2][k], B[x+2][k], G[x+2][k]))
                        {
                            flag2 = false;
                            break;
                        }
                    
                if(!flag1 && !flag2) continue;
                
                minX = Math.min(minX, x);
                minY = Math.min(minY, y);
                maxX = Math.max(maxX, x);
                maxY = Math.max(maxY, y);
                found = true;
            }
        if(!found) return true;
        else
        {
            Robot robot = new Robot();
            robot.mouseMove(maxX, minY); // 实现鼠标跟踪白板延长的效果 
            return maxY - minY < dis;
        }
    }
    
    double sqr(double x) {return x * x;}
    
    double calcDistance(Point p1, Point p2)
    {
        return Math.sqrt(sqr(p1.x - p2.x) + sqr(p1.y - p2.y));
    }
    
    Assistant()
    {
        try
        {

            Robot robot = new Robot();
            Random random = new Random();
            Thread.sleep(3000); // 别急,先等等

            while(true)
            {
                
                Point penguin = findPenguin();
                System.out.println("penguin : " + penguin.x + ", " + penguin.y);
                Point dest = findDestination(penguin.y);
                System.out.println("dest : " + dest.x + ", " + dest.y);
                
                double distance = calcDistance(penguin,dest);

                System.out.println("distance : " + distance);
                
                double beta = 1.35 / Math.pow(alpha, 0.3); // 没有科学依据的函数
                if(distance > 165) // 没有科学依据的分类讨论
                {
                    if(alpha <= 1) beta *= 0.60;
                    else if(alpha <= 2) beta *= 0.78;
                    else if(alpha <= 3) beta *= 0.80;
                    else if(alpha <= 4) beta *= 0.89;
                    else if(alpha <= 5) beta *= 0.91;
                    else if(alpha <= 6) beta *= 0.95;
                    else if(alpha <= 7) beta *= 0.97;
                }
                
                System.out.println("alpha : " + alpha + " => " + beta);

                robot.mouseMove(dest.x, dest.y);
                robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                
                while(findBoard(distance * beta, penguin.y)); // 直到返回假,也就是够长了,才往下运行松开鼠标
                
                robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                
                System.out.println("=======");

                Thread.sleep(2823 + random.nextInt(300)); // 随机休息一会儿
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }

    public static void main(String[] args)
    {       
        new Assistant();
    }

}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 33
    评论
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值