2018-08-08 03:16:53 +00:00
|
|
|
/*
|
|
|
|
* This file is part of Baritone.
|
|
|
|
*
|
|
|
|
* Baritone is free software: you can redistribute it and/or modify
|
2018-09-17 22:11:40 +00:00
|
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
2018-08-08 03:16:53 +00:00
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
2018-08-08 04:15:22 +00:00
|
|
|
* Baritone is distributed in the hope that it will be useful,
|
2018-08-08 03:16:53 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2018-09-17 22:11:40 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2018-08-08 03:16:53 +00:00
|
|
|
*
|
2018-09-17 22:11:40 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
2018-08-08 03:16:53 +00:00
|
|
|
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-08-22 20:15:56 +00:00
|
|
|
package baritone.pathing.calc;
|
2018-08-03 04:50:45 +00:00
|
|
|
|
2018-09-26 22:01:07 +00:00
|
|
|
import baritone.Baritone;
|
2018-10-15 22:44:43 +00:00
|
|
|
import baritone.api.pathing.calc.IPath;
|
2018-10-09 01:37:52 +00:00
|
|
|
import baritone.api.pathing.calc.IPathFinder;
|
2018-09-23 23:29:03 +00:00
|
|
|
import baritone.api.pathing.goals.Goal;
|
2018-11-07 22:09:23 +00:00
|
|
|
import baritone.api.utils.PathCalculationResult;
|
2018-11-10 01:21:02 +00:00
|
|
|
import baritone.pathing.movement.CalculationContext;
|
2018-11-14 22:19:24 +00:00
|
|
|
import baritone.utils.Helper;
|
2018-08-29 20:21:54 +00:00
|
|
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
2018-08-03 04:50:45 +00:00
|
|
|
|
2018-08-06 07:21:47 +00:00
|
|
|
import java.util.Optional;
|
2018-08-03 04:50:45 +00:00
|
|
|
|
2018-08-03 13:55:17 +00:00
|
|
|
/**
|
|
|
|
* Any pathfinding algorithm that keeps track of nodes recursively by their cost (e.g. A*, dijkstra)
|
|
|
|
*
|
|
|
|
* @author leijurv
|
|
|
|
*/
|
2018-08-03 04:50:45 +00:00
|
|
|
public abstract class AbstractNodeCostSearch implements IPathFinder {
|
|
|
|
|
2018-10-03 14:57:24 +00:00
|
|
|
protected final int startX;
|
|
|
|
protected final int startY;
|
|
|
|
protected final int startZ;
|
2018-08-03 07:51:10 +00:00
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
protected final Goal goal;
|
2018-08-03 07:51:10 +00:00
|
|
|
|
2018-11-10 01:21:02 +00:00
|
|
|
private final CalculationContext context;
|
|
|
|
|
2018-09-16 20:28:23 +00:00
|
|
|
/**
|
|
|
|
* @see <a href="https://github.com/cabaletta/baritone/issues/107">Issue #107</a>
|
|
|
|
*/
|
|
|
|
private final Long2ObjectOpenHashMap<PathNode> map;
|
2018-08-03 07:51:10 +00:00
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
protected PathNode startNode;
|
2018-08-03 07:51:10 +00:00
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
protected PathNode mostRecentConsidered;
|
2018-08-03 07:51:10 +00:00
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
protected PathNode[] bestSoFar;
|
2018-08-03 07:51:10 +00:00
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
private volatile boolean isFinished;
|
|
|
|
|
2018-09-22 15:47:02 +00:00
|
|
|
protected boolean cancelRequested;
|
2018-08-28 18:17:11 +00:00
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
/**
|
|
|
|
* This is really complicated and hard to explain. I wrote a comment in the old version of MineBot but it was so
|
|
|
|
* long it was easier as a Google Doc (because I could insert charts).
|
2018-08-03 04:55:53 +00:00
|
|
|
*
|
2018-08-03 05:06:06 +00:00
|
|
|
* @see <a href="https://docs.google.com/document/d/1WVHHXKXFdCR1Oz__KtK8sFqyvSwJN_H4lftkHFgmzlc/edit"></a>
|
2018-08-03 04:50:45 +00:00
|
|
|
*/
|
2018-08-18 02:52:35 +00:00
|
|
|
protected static final double[] COEFFICIENTS = {1.5, 2, 2.5, 3, 4, 5, 10}; // big TODO tune
|
2018-08-03 05:06:49 +00:00
|
|
|
/**
|
|
|
|
* If a path goes less than 5 blocks and doesn't make it to its goal, it's not worth considering.
|
|
|
|
*/
|
|
|
|
protected final static double MIN_DIST_PATH = 5;
|
2018-08-03 04:50:45 +00:00
|
|
|
|
2018-11-10 01:21:02 +00:00
|
|
|
AbstractNodeCostSearch(int startX, int startY, int startZ, Goal goal, CalculationContext context) {
|
2018-10-03 14:57:24 +00:00
|
|
|
this.startX = startX;
|
|
|
|
this.startY = startY;
|
|
|
|
this.startZ = startZ;
|
2018-08-03 04:50:45 +00:00
|
|
|
this.goal = goal;
|
2018-11-10 01:21:02 +00:00
|
|
|
this.context = context;
|
2018-09-26 22:01:07 +00:00
|
|
|
this.map = new Long2ObjectOpenHashMap<>(Baritone.settings().pathingMapDefaultSize.value, Baritone.settings().pathingMapLoadFactor.get());
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
|
|
|
|
2018-08-28 18:17:11 +00:00
|
|
|
public void cancel() {
|
|
|
|
cancelRequested = true;
|
|
|
|
}
|
|
|
|
|
2018-11-07 22:09:23 +00:00
|
|
|
public synchronized PathCalculationResult calculate(long timeout) {
|
2018-08-03 04:50:45 +00:00
|
|
|
if (isFinished) {
|
2018-08-14 17:47:31 +00:00
|
|
|
throw new IllegalStateException("Path Finder is currently in use, and cannot be reused!");
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
2018-08-28 18:17:11 +00:00
|
|
|
this.cancelRequested = false;
|
2018-09-01 21:47:46 +00:00
|
|
|
try {
|
2018-11-17 17:18:55 +00:00
|
|
|
IPath path = calculate0(timeout).map(IPath::postProcess).orElse(null);
|
2018-09-01 21:47:46 +00:00
|
|
|
isFinished = true;
|
2018-11-07 22:09:23 +00:00
|
|
|
if (cancelRequested) {
|
|
|
|
return new PathCalculationResult(PathCalculationResult.Type.CANCELLATION, path);
|
|
|
|
}
|
2018-11-17 17:18:55 +00:00
|
|
|
if (path == null) {
|
|
|
|
return new PathCalculationResult(PathCalculationResult.Type.FAILURE);
|
2018-11-07 22:09:23 +00:00
|
|
|
}
|
2018-11-17 17:18:55 +00:00
|
|
|
if (goal.isInGoal(path.getDest())) {
|
2018-11-07 22:09:23 +00:00
|
|
|
return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_TO_GOAL, path);
|
|
|
|
} else {
|
|
|
|
return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_SEGMENT, path);
|
|
|
|
}
|
2018-11-14 22:19:24 +00:00
|
|
|
} catch (Exception e) {
|
|
|
|
Helper.HELPER.logDebug("Pathing exception: " + e);
|
|
|
|
e.printStackTrace();
|
2018-11-17 17:18:55 +00:00
|
|
|
return new PathCalculationResult(PathCalculationResult.Type.EXCEPTION);
|
2018-09-17 00:25:14 +00:00
|
|
|
} finally {
|
|
|
|
// this is run regardless of what exception may or may not be raised by calculate0
|
2018-09-01 21:47:46 +00:00
|
|
|
isFinished = true;
|
|
|
|
}
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
|
|
|
|
2018-09-02 20:51:38 +00:00
|
|
|
protected abstract Optional<IPath> calculate0(long timeout);
|
2018-08-03 04:50:45 +00:00
|
|
|
|
2018-08-03 07:51:10 +00:00
|
|
|
/**
|
|
|
|
* Determines the distance squared from the specified node to the start
|
|
|
|
* node. Intended for use in distance comparison, rather than anything that
|
|
|
|
* considers the real distance value, hence the "sq".
|
|
|
|
*
|
|
|
|
* @param n A node
|
|
|
|
* @return The distance, squared
|
|
|
|
*/
|
|
|
|
protected double getDistFromStartSq(PathNode n) {
|
2018-10-03 14:57:24 +00:00
|
|
|
int xDiff = n.x - startX;
|
|
|
|
int yDiff = n.y - startY;
|
|
|
|
int zDiff = n.z - startZ;
|
2018-08-03 07:51:10 +00:00
|
|
|
return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
|
|
|
|
2018-08-03 07:51:10 +00:00
|
|
|
/**
|
2018-10-03 14:57:24 +00:00
|
|
|
* Attempts to search the block position hashCode long to {@link PathNode} map
|
2018-08-03 07:51:10 +00:00
|
|
|
* for the node mapped to the specified pos. If no node is found,
|
|
|
|
* a new node is created.
|
|
|
|
*
|
|
|
|
* @return The associated node
|
2018-09-17 00:25:14 +00:00
|
|
|
* @see <a href="https://github.com/cabaletta/baritone/issues/107">Issue #107</a>
|
2018-08-03 07:51:10 +00:00
|
|
|
*/
|
2018-09-26 22:32:54 +00:00
|
|
|
protected PathNode getNodeAtPosition(int x, int y, int z, long hashCode) {
|
2018-08-29 20:21:54 +00:00
|
|
|
PathNode node = map.get(hashCode);
|
|
|
|
if (node == null) {
|
2018-09-23 15:52:03 +00:00
|
|
|
node = new PathNode(x, y, z, goal);
|
2018-08-29 20:21:54 +00:00
|
|
|
map.put(hashCode, node);
|
|
|
|
}
|
|
|
|
return node;
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-08-06 07:21:47 +00:00
|
|
|
public Optional<IPath> pathToMostRecentNodeConsidered() {
|
2018-11-17 00:28:20 +00:00
|
|
|
return Optional.ofNullable(mostRecentConsidered).map(node -> new Path(startNode, node, 0, goal, context));
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
|
|
|
|
2018-09-22 05:14:18 +00:00
|
|
|
protected int mapSize() {
|
|
|
|
return map.size();
|
|
|
|
}
|
|
|
|
|
2018-08-03 04:50:45 +00:00
|
|
|
@Override
|
2018-08-06 07:21:47 +00:00
|
|
|
public Optional<IPath> bestPathSoFar() {
|
2018-11-14 22:26:35 +00:00
|
|
|
if (startNode == null || bestSoFar == null || bestSoFar[0] == null) {
|
2018-08-06 07:21:47 +00:00
|
|
|
return Optional.empty();
|
2018-08-20 23:24:06 +00:00
|
|
|
}
|
|
|
|
for (int i = 0; i < bestSoFar.length; i++) {
|
|
|
|
if (bestSoFar[i] == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (getDistFromStartSq(bestSoFar[i]) > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared
|
2018-09-23 15:13:21 +00:00
|
|
|
try {
|
2018-11-10 01:21:02 +00:00
|
|
|
return Optional.of(new Path(startNode, bestSoFar[i], 0, goal, context));
|
2018-09-23 15:13:21 +00:00
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
System.out.println("Unable to construct path to render");
|
|
|
|
return Optional.empty();
|
|
|
|
}
|
2018-08-20 23:24:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// instead of returning bestSoFar[0], be less misleading
|
|
|
|
// if it actually won't find any path, don't make them think it will by rendering a dark blue that will never actually happen
|
|
|
|
return Optional.empty();
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|
2018-08-03 05:06:49 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public final boolean isFinished() {
|
|
|
|
return isFinished;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public final Goal getGoal() {
|
|
|
|
return goal;
|
|
|
|
}
|
2018-08-03 04:50:45 +00:00
|
|
|
}
|