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.DoubleIterator;
022import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
023import net.minecraft.core.BlockPos;
024
025import java.util.Arrays;
026import java.util.Objects;
027
028/**
029 * Useful for automated combat (retreating specifically)
030 *
031 * @author leijurv
032 */
033public class GoalRunAway implements Goal {
034
035    private final BlockPos[] from;
036
037    private final int distanceSq;
038
039    private final Integer maintainY;
040
041    public GoalRunAway(double distance, BlockPos... from) {
042        this(distance, null, from);
043    }
044
045    public GoalRunAway(double distance, Integer maintainY, BlockPos... from) {
046        if (from.length == 0) {
047            throw new IllegalArgumentException("Positions to run away from must not be empty");
048        }
049        this.from = from;
050        this.distanceSq = (int) (distance * distance);
051        this.maintainY = maintainY;
052    }
053
054    @Override
055    public boolean isInGoal(int x, int y, int z) {
056        if (maintainY != null && maintainY != y) {
057            return false;
058        }
059        for (BlockPos p : from) {
060            int diffX = x - p.getX();
061            int diffZ = z - p.getZ();
062            int distSq = diffX * diffX + diffZ * diffZ;
063            if (distSq < distanceSq) {
064                return false;
065            }
066        }
067        return true;
068    }
069
070    @Override
071    public double heuristic(int x, int y, int z) {// mostly copied from GoalBlock
072        double min = Double.MAX_VALUE;
073        for (BlockPos p : from) {
074            double h = GoalXZ.calculate(p.getX() - x, p.getZ() - z);
075            if (h < min) {
076                min = h;
077            }
078        }
079        min = -min;
080        if (maintainY != null) {
081            min = min * 0.6 + GoalYLevel.calculate(maintainY, y) * 1.5;
082        }
083        return min;
084    }
085
086    @Override
087    public double heuristic() {// TODO less hacky solution
088        int distance = (int) Math.ceil(Math.sqrt(distanceSq));
089        int minX = Integer.MAX_VALUE;
090        int minY = Integer.MAX_VALUE;
091        int minZ = Integer.MAX_VALUE;
092        int maxX = Integer.MIN_VALUE;
093        int maxY = Integer.MIN_VALUE;
094        int maxZ = Integer.MIN_VALUE;
095        for (BlockPos p : from) {
096            minX = Math.min(minX, p.getX() - distance);
097            minY = Math.min(minY, p.getY() - distance);
098            minZ = Math.min(minZ, p.getZ() - distance);
099            maxX = Math.max(minX, p.getX() + distance);
100            maxY = Math.max(minY, p.getY() + distance);
101            maxZ = Math.max(minZ, p.getZ() + distance);
102        }
103        DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978
104        double minOutside = Double.POSITIVE_INFINITY;
105        for (int x = minX; x <= maxX; x++) {
106            for (int y = minY; y <= maxY; y++) {
107                for (int z = minZ; z <= maxZ; z++) {
108                    double h = heuristic(x, y, z);
109                    if (h < minOutside && isInGoal(x, y, z)) {
110                        maybeAlwaysInside.add(h);
111                    } else {
112                        minOutside = Math.min(minOutside, h);
113                    }
114                }
115            }
116        }
117        double maxInside = Double.NEGATIVE_INFINITY;
118        DoubleIterator it = maybeAlwaysInside.iterator();
119        while (it.hasNext()) {
120            double inside = it.nextDouble();
121            if (inside < minOutside) {
122                maxInside = Math.max(maxInside, inside);
123            }
124        }
125        return maxInside;
126    }
127
128    @Override
129    public boolean equals(Object o) {
130        if (this == o) {
131            return true;
132        }
133        if (o == null || getClass() != o.getClass()) {
134            return false;
135        }
136
137        GoalRunAway goal = (GoalRunAway) o;
138        return distanceSq == goal.distanceSq
139                && Arrays.equals(from, goal.from)
140                && Objects.equals(maintainY, goal.maintainY);
141    }
142
143    @Override
144    public int hashCode() {
145        int hash = Arrays.hashCode(from);
146        hash = hash * 1196803141 + distanceSq;
147        hash = hash * -2053788840 + maintainY;
148        return hash;
149    }
150
151    @Override
152    public String toString() {
153        if (maintainY != null) {
154            return String.format(
155                    "GoalRunAwayFromMaintainY y=%s, %s",
156                    SettingsUtil.maybeCensor(maintainY),
157                    Arrays.asList(from)
158            );
159        } else {
160            return "GoalRunAwayFrom" + Arrays.asList(from);
161        }
162    }
163}