From 272dd7942650d7fb45510e502f6858a3a628b8d1 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Wed, 30 Jan 2019 15:49:37 -0800 Subject: [PATCH] much needed pathing overhaul --- .../pathing/calc/AStarPathFinder.java | 58 +++++-------------- .../pathing/calc/AbstractNodeCostSearch.java | 56 ++++++++++++++---- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index b32fc99f..6f44fac2 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -25,7 +25,6 @@ import baritone.api.utils.BetterBlockPos; import baritone.pathing.calc.openset.BinaryHeapOpenSet; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Moves; -import baritone.utils.Helper; import baritone.utils.pathing.BetterWorldBorder; import baritone.utils.pathing.Favoring; import baritone.utils.pathing.MutableMoveResult; @@ -37,7 +36,7 @@ import java.util.Optional; * * @author leijurv */ -public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper { +public final class AStarPathFinder extends AbstractNodeCostSearch { private final Favoring favoring; private final CalculationContext calcContext; @@ -55,14 +54,12 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel startNode.combinedCost = startNode.estimatedCostToGoal; BinaryHeapOpenSet openSet = new BinaryHeapOpenSet(); openSet.insert(startNode); - bestSoFar = new PathNode[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i]) - double[] bestHeuristicSoFar = new double[COEFFICIENTS.length]; + double[] bestHeuristicSoFar = new double[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i]) for (int i = 0; i < bestHeuristicSoFar.length; i++) { bestHeuristicSoFar[i] = startNode.estimatedCostToGoal; bestSoFar[i] = startNode; } MutableMoveResult res = new MutableMoveResult(); - Favoring favored = favoring; BetterWorldBorder worldBorder = new BetterWorldBorder(calcContext.world.getWorldBorder()); long startTime = System.currentTimeMillis(); boolean slowPath = Baritone.settings().slowPath.get(); @@ -75,10 +72,10 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel int numNodes = 0; int numMovementsConsidered = 0; int numEmptyChunk = 0; - boolean favoring = !favored.isEmpty(); + boolean isFavoring = !favoring.isEmpty(); int timeCheckInterval = 1 << 6; int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.get(); // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior - boolean minimumImprovementRepropagation = Baritone.settings().minimumImprovementRepropagation.get(); + double minimumImprovement = Baritone.settings().minimumImprovementRepropagation.get() ? MIN_IMPROVEMENT : 0; while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) { if ((numNodes & (timeCheckInterval - 1)) == 0) { // only call this once every 64 nodes (about half a millisecond) long now = System.currentTimeMillis(); // since nanoTime is slow on windows (takes many microseconds) @@ -136,21 +133,13 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel throw new IllegalStateException(moves + " " + res.y + " " + (currentNode.y + moves.yOffset)); } long hashCode = BetterBlockPos.longHash(res.x, res.y, res.z); - if (favoring) { + if (isFavoring) { // see issue #18 - actionCost *= favored.calculate(hashCode); + actionCost *= favoring.calculate(hashCode); } PathNode neighbor = getNodeAtPosition(res.x, res.y, res.z, hashCode); double tentativeCost = currentNode.cost + actionCost; - if (tentativeCost < neighbor.cost) { - double improvementBy = neighbor.cost - tentativeCost; - // there are floating point errors caused by random combinations of traverse and diagonal over a flat area - // that means that sometimes there's a cost improvement of like 10 ^ -16 - // it's not worth the time to update the costs, decrease-key the heap, potentially repropagate, etc - if (improvementBy < 0.01 && minimumImprovementRepropagation) { - // who cares about a hundredth of a tick? that's half a millisecond for crying out loud! - continue; - } + if (neighbor.cost - tentativeCost > minimumImprovement) { neighbor.previous = currentNode; neighbor.cost = tentativeCost; neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal; @@ -159,12 +148,9 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel } else { openSet.insert(neighbor);//dont double count, dont insert into open set if it's already there } - for (int i = 0; i < bestSoFar.length; i++) { + for (int i = 0; i < COEFFICIENTS.length; i++) { double heuristic = neighbor.estimatedCostToGoal + neighbor.cost / COEFFICIENTS[i]; - if (heuristic < bestHeuristicSoFar[i]) { - if (bestHeuristicSoFar[i] - heuristic < 0.01 && minimumImprovementRepropagation) { - continue; - } + if (bestHeuristicSoFar[i] - heuristic > minimumImprovement) { bestHeuristicSoFar[i] = heuristic; bestSoFar[i] = neighbor; if (getDistFromStartSq(neighbor) > MIN_DIST_PATH * MIN_DIST_PATH) { @@ -182,28 +168,10 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel System.out.println("Open set size: " + openSet.size()); System.out.println("PathNode map size: " + mapSize()); System.out.println((int) (numNodes * 1.0 / ((System.currentTimeMillis() - startTime) / 1000F)) + " nodes per second"); - double bestDist = 0; - for (int i = 0; i < bestSoFar.length; i++) { - if (bestSoFar[i] == null) { - continue; - } - double dist = getDistFromStartSq(bestSoFar[i]); - if (dist > bestDist) { - bestDist = dist; - } - if (dist > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared - logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, A* cost coefficient " + COEFFICIENTS[i] + ", " + numMovementsConsidered + " movements considered"); - if (COEFFICIENTS[i] >= 3) { - System.out.println("Warning: cost coefficient is greater than three! Probably means that"); - System.out.println("the path I found is pretty terrible (like sneak-bridging for dozens of blocks)"); - System.out.println("But I'm going to do it anyway, because yolo"); - } - System.out.println("Path goes for " + Math.sqrt(dist) + " blocks"); - return Optional.of(new Path(startNode, bestSoFar[i], numNodes, goal, calcContext)); - } + Optional result = bestSoFar(true, numNodes); + if (result.isPresent()) { + logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered"); } - logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); - logDebug("No path found =("); - return Optional.empty(); + return result; } } diff --git a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java index 625e8e59..a9974e8d 100644 --- a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java +++ b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java @@ -34,7 +34,7 @@ import java.util.Optional; * * @author leijurv */ -public abstract class AbstractNodeCostSearch implements IPathFinder { +public abstract class AbstractNodeCostSearch implements IPathFinder, Helper { protected final int startX; protected final int startY; @@ -53,7 +53,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { protected PathNode mostRecentConsidered; - protected PathNode[] bestSoFar; + protected final PathNode[] bestSoFar = new PathNode[COEFFICIENTS.length]; private volatile boolean isFinished; @@ -63,13 +63,23 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { * 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). * - * @see + * @see here */ - protected static final double[] COEFFICIENTS = {1.5, 2, 2.5, 3, 4, 5, 10}; // big TODO tune + protected static final double[] COEFFICIENTS = {1.5, 2, 2.5, 3, 4, 5, 10}; + /** * 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; + protected static final double MIN_DIST_PATH = 5; + + /** + * there are floating point errors caused by random combinations of traverse and diagonal over a flat area + * that means that sometimes there's a cost improvement of like 10 ^ -16 + * it's not worth the time to update the costs, decrease-key the heap, potentially repropagate, etc + *

+ * who cares about a hundredth of a tick? that's half a millisecond for crying out loud! + */ + protected static final double MIN_IMPROVEMENT = 0.01; AbstractNodeCostSearch(int startX, int startY, int startZ, Goal goal, CalculationContext context) { this.startX = startX; @@ -170,25 +180,43 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { return Optional.ofNullable(mostRecentConsidered).map(node -> new Path(startNode, node, 0, goal, context)); } - protected int mapSize() { - return map.size(); + @Override + public Optional bestPathSoFar() { + return bestSoFar(false, 0); } - @Override - public Optional bestPathSoFar() { // TODO cleanup code duplication between here and AStarPathFinder + protected Optional bestSoFar(boolean logInfo, int numNodes) { if (startNode == null || bestSoFar == null) { return Optional.empty(); } - for (int i = 0; i < bestSoFar.length; i++) { + double bestDist = 0; + for (int i = 0; i < COEFFICIENTS.length; i++) { if (bestSoFar[i] == null) { continue; } - if (getDistFromStartSq(bestSoFar[i]) > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared - return Optional.of(new Path(startNode, bestSoFar[i], 0, goal, context)); + double dist = getDistFromStartSq(bestSoFar[i]); + if (dist > bestDist) { + bestDist = dist; + } + if (dist > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared + if (logInfo) { + if (COEFFICIENTS[i] >= 3) { + System.out.println("Warning: cost coefficient is greater than three! Probably means that"); + System.out.println("the path I found is pretty terrible (like sneak-bridging for dozens of blocks)"); + System.out.println("But I'm going to do it anyway, because yolo"); + } + System.out.println("Path goes for " + Math.sqrt(dist) + " blocks"); + logDebug("A* cost coefficient " + COEFFICIENTS[i]); + } + return Optional.of(new Path(startNode, bestSoFar[i], numNodes, goal, context)); } } // 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 + if (logInfo) { + logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); + logDebug("No path found =("); + } return Optional.empty(); } @@ -205,4 +233,8 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { public BetterBlockPos getStart() { return new BetterBlockPos(startX, startY, startZ); } + + protected int mapSize() { + return map.size(); + } }