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.BetterBlockPos;
021import baritone.api.utils.SettingsUtil;
022import baritone.api.utils.interfaces.IGoalRenderPos;
023import it.unimi.dsi.fastutil.doubles.DoubleIterator;
024import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
025import net.minecraft.core.BlockPos;
026
027public class GoalNear implements Goal, IGoalRenderPos {
028
029    private final int x;
030    private final int y;
031    private final int z;
032    private final int rangeSq;
033
034    public GoalNear(BlockPos pos, int range) {
035        this.x = pos.getX();
036        this.y = pos.getY();
037        this.z = pos.getZ();
038        this.rangeSq = range * range;
039    }
040
041    @Override
042    public boolean isInGoal(int x, int y, int z) {
043        int xDiff = x - this.x;
044        int yDiff = y - this.y;
045        int zDiff = z - this.z;
046        return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff <= rangeSq;
047    }
048
049    @Override
050    public double heuristic(int x, int y, int z) {
051        int xDiff = x - this.x;
052        int yDiff = y - this.y;
053        int zDiff = z - this.z;
054        return GoalBlock.calculate(xDiff, yDiff, zDiff);
055    }
056
057    @Override
058    public double heuristic() {// TODO less hacky solution
059        int range = (int) Math.ceil(Math.sqrt(rangeSq));
060        DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978
061        double minOutside = Double.POSITIVE_INFINITY;
062        for (int dx = -range; dx <= range; dx++) {
063            for (int dy = -range; dy <= range; dy++) {
064                for (int dz = -range; dz <= range; dz++) {
065                    double h = heuristic(x + dx, y + dy, z + dz);
066                    if (h < minOutside && isInGoal(x + dx, y + dy, z + dz)) {
067                        maybeAlwaysInside.add(h);
068                    } else {
069                        minOutside = Math.min(minOutside, h);
070                    }
071                }
072            }
073        }
074        double maxInside = Double.NEGATIVE_INFINITY;
075        DoubleIterator it = maybeAlwaysInside.iterator();
076        while (it.hasNext()) {
077            double inside = it.nextDouble();
078            if (inside < minOutside) {
079                maxInside = Math.max(maxInside, inside);
080            }
081        }
082        return maxInside;
083    }
084
085    @Override
086    public BlockPos getGoalPos() {
087        return new BlockPos(x, y, z);
088    }
089
090    @Override
091    public boolean equals(Object o) {
092        if (this == o) {
093            return true;
094        }
095        if (o == null || getClass() != o.getClass()) {
096            return false;
097        }
098
099        GoalNear goal = (GoalNear) o;
100        return x == goal.x
101                && y == goal.y
102                && z == goal.z
103                && rangeSq == goal.rangeSq;
104    }
105
106    @Override
107    public int hashCode() {
108        return (int) BetterBlockPos.longHash(x, y, z) + rangeSq;
109    }
110
111    @Override
112    public String toString() {
113        return String.format(
114                "GoalNear{x=%s, y=%s, z=%s, rangeSq=%d}",
115                SettingsUtil.maybeCensor(x),
116                SettingsUtil.maybeCensor(y),
117                SettingsUtil.maybeCensor(z),
118                rangeSq
119        );
120    }
121}