001/*
002 * This file is part of Baritone.
003 *
004 * Baritone is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Lesser General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * Baritone is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License
015 * along with Baritone.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package baritone.api.pathing.goals;
019
020import baritone.api.utils.SettingsUtil;
021import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
022import it.unimi.dsi.fastutil.doubles.DoubleIterator;
023import net.minecraft.util.math.BlockPos;
024
025import java.util.Arrays;
026
027/**
028 * Useful for automated combat (retreating specifically)
029 *
030 * @author leijurv
031 */
032public class GoalRunAway implements Goal {
033
034    private final BlockPos[] from;
035
036    private final int distanceSq;
037
038    private final Integer maintainY;
039
040    public GoalRunAway(double distance, BlockPos... from) {
041        this(distance, null, from);
042    }
043
044    public GoalRunAway(double distance, Integer maintainY, BlockPos... from) {
045        if (from.length == 0) {
046            throw new IllegalArgumentException();
047        }
048        this.from = from;
049        this.distanceSq = (int) (distance * distance);
050        this.maintainY = maintainY;
051    }
052
053    @Override
054    public boolean isInGoal(int x, int y, int z) {
055        if (maintainY != null && maintainY != y) {
056            return false;
057        }
058        for (BlockPos p : from) {
059            int diffX = x - p.getX();
060            int diffZ = z - p.getZ();
061            int distSq = diffX * diffX + diffZ * diffZ;
062            if (distSq < distanceSq) {
063                return false;
064            }
065        }
066        return true;
067    }
068
069    @Override
070    public double heuristic(int x, int y, int z) {// mostly copied from GoalBlock
071        double min = Double.MAX_VALUE;
072        for (BlockPos p : from) {
073            double h = GoalXZ.calculate(p.getX() - x, p.getZ() - z);
074            if (h < min) {
075                min = h;
076            }
077        }
078        min = -min;
079        if (maintainY != null) {
080            min = min * 0.6 + GoalYLevel.calculate(maintainY, y) * 1.5;
081        }
082        return min;
083    }
084
085    @Override
086    public double heuristic() {// TODO less hacky solution
087        int distance = (int) Math.ceil(Math.sqrt(distanceSq));
088        int minX = Integer.MAX_VALUE;
089        int minY = Integer.MAX_VALUE;
090        int minZ = Integer.MAX_VALUE;
091        int maxX = Integer.MIN_VALUE;
092        int maxY = Integer.MIN_VALUE;
093        int maxZ = Integer.MIN_VALUE;
094        for (BlockPos p : from) {
095            minX = Math.min(minX, p.getX() - distance);
096            minY = Math.min(minY, p.getY() - distance);
097            minZ = Math.min(minZ, p.getZ() - distance);
098            maxX = Math.max(minX, p.getX() + distance);
099            maxY = Math.max(minY, p.getY() + distance);
100            maxZ = Math.max(minZ, p.getZ() + distance);
101        }
102        DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978
103        double minOutside = Double.POSITIVE_INFINITY;
104        for (int x = minX; x <= maxX; x++) {
105            for (int y = minY; y <= maxY; y++) {
106                for (int z = minZ; z <= maxZ; z++) {
107                    double h = heuristic(x, y, z);
108                    if (h < minOutside && isInGoal(x, y, z)) {
109                        maybeAlwaysInside.add(h);
110                    } else {
111                        minOutside = Math.min(minOutside, h);
112                    }
113                }
114            }
115        }
116        double maxInside = Double.NEGATIVE_INFINITY;
117        DoubleIterator it = maybeAlwaysInside.iterator();
118        while (it.hasNext()) {
119            double inside = it.nextDouble();
120            if (inside < minOutside) {
121                maxInside = Math.max(maxInside, inside);
122            }
123        }
124        return maxInside;
125    }
126
127    @Override
128    public String toString() {
129        if (maintainY != null) {
130            return String.format(
131                    "GoalRunAwayFromMaintainY y=%s, %s",
132                    SettingsUtil.maybeCensor(maintainY),
133                    Arrays.asList(from)
134            );
135        } else {
136            return "GoalRunAwayFrom" + Arrays.asList(from);
137        }
138    }
139}