diff --git a/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java new file mode 100644 index 00000000..049fa7fb --- /dev/null +++ b/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java @@ -0,0 +1,180 @@ +package baritone.bot.pathing.calc; + + +import baritone.Baritone; +import baritone.bot.pathing.goals.Goal; +import baritone.bot.pathing.movement.ActionCosts; +import baritone.bot.pathing.movement.Movement; +import baritone.bot.utils.ToolSet; +import net.minecraft.client.Minecraft; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.EmptyChunk; + +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The actual A* pathfinding + * + * @author leijurv + */ +public class AStarPathFinder extends AbstractNodeCostSearch { + public AStarPathFinder(BlockPos start, Goal goal) { + super(start, goal); + } + + @Override + protected IPath calculate0() { + startNode = getNodeAtPosition(start); + startNode.cost = 0; + IOpenSet openSet = new LinkedListOpenSet(); + startNode.isOpen = true; + 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]; + for (int i = 0; i < bestHeuristicSoFar.length; i++) { + bestHeuristicSoFar[i] = Double.MAX_VALUE; + } + currentlyRunning = this; + long startTime = System.currentTimeMillis(); + long timeoutTime = startTime + (Baritone.slowPath ? 40000 : 4000); + long lastPrintout = 0; + int numNodes = 0; + ToolSet ts = new ToolSet(); + int numEmptyChunk = 0; + while (!openSet.isEmpty() && numEmptyChunk < 50 && System.currentTimeMillis() < timeoutTime) { + if (Baritone.slowPath) { + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + Logger.getLogger(AStarPathFinder.class.getName()).log(Level.SEVERE, null, ex); + } + } + PathNode currentNode = openSet.removeLowest(); + mostRecentConsidered = currentNode; + currentNode.isOpen = false; + currentNode.nextOpen = null; + BlockPos currentNodePos = currentNode.pos; + numNodes++; + if (System.currentTimeMillis() > lastPrintout + 1000) {//print once a second + System.out.println("searching... at " + currentNodePos + ", considered " + numNodes + " nodes so far"); + lastPrintout = System.currentTimeMillis(); + } + if (goal.isInGoal(currentNodePos)) { + currentlyRunning = null; + return new Path(startNode, currentNode, goal); + } + //long constructStart = System.nanoTime(); + Movement[] possibleMovements = getConnectedPositions(currentNodePos);//movement that we could take that start at myPos, in random order + shuffle(possibleMovements); + //long constructEnd = System.nanoTime(); + //System.out.println(constructEnd - constructStart); + for (Movement movementToGetToNeighbor : possibleMovements) { + //long costStart = System.nanoTime(); + // TODO cache cost + double actionCost = movementToGetToNeighbor.calculateCost(ts); + //long costEnd = System.nanoTime(); + //System.out.println(movementToGetToNeighbor.getClass() + "" + (costEnd - costStart)); + if (actionCost >= ActionCosts.COST_INF) { + continue; + } + if (Minecraft.getMinecraft().world.getChunk(movementToGetToNeighbor.getDest()) instanceof EmptyChunk) { + numEmptyChunk++; + continue; + } + PathNode neighbor = getNodeAtPosition(movementToGetToNeighbor.getDest()); + double tentativeCost = currentNode.cost + actionCost; + if (tentativeCost < neighbor.cost) { + neighbor.previous = currentNode; + neighbor.previousMovement = movementToGetToNeighbor; + neighbor.cost = tentativeCost; + if (!neighbor.isOpen) { + openSet.insert(neighbor);//dont double count, dont insert into open set if it's already there + neighbor.isOpen = true; + } + for (int i = 0; i < bestSoFar.length; i++) { + double heuristic = neighbor.estimatedCostToGoal + neighbor.cost / COEFFICIENTS[i]; + if (heuristic < bestHeuristicSoFar[i]) { + bestHeuristicSoFar[i] = heuristic; + bestSoFar[i] = neighbor; + } + } + } + } + } + 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 + System.out.println("A* cost coefficient " + COEFFICIENTS[i]); + 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 " + dist + " blocks"); + currentlyRunning = null; + return new Path(startNode, bestSoFar[i], goal); + } + } + System.out.println("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + bestDist + " blocks =("); + System.out.println("No path found =("); + currentlyRunning = null; + return null; + } + + + private static Movement[] getConnectedPositions(BlockPos pos) { + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + /*Action[] actions = new Action[26]; + actions[0] = new ActionPillar(pos); + actions[1] = new ActionBridge(pos, new BlockPos(x + 1, y, z)); + actions[2] = new ActionBridge(pos, new BlockPos(x - 1, y, z)); + actions[3] = new ActionBridge(pos, new BlockPos(x, y, z + 1)); + actions[4] = new ActionBridge(pos, new BlockPos(x, y, z - 1)); + actions[5] = new ActionClimb(pos, new BlockPos(x + 1, y + 1, z)); + actions[6] = new ActionClimb(pos, new BlockPos(x - 1, y + 1, z)); + actions[7] = new ActionClimb(pos, new BlockPos(x, y + 1, z + 1)); + actions[8] = new ActionClimb(pos, new BlockPos(x, y + 1, z - 1)); + actions[9] = new ActionDescend(pos, new BlockPos(x, y - 1, z - 1)); + actions[10] = new ActionDescend(pos, new BlockPos(x, y - 1, z + 1)); + actions[11] = new ActionDescend(pos, new BlockPos(x - 1, y - 1, z)); + actions[12] = new ActionDescend(pos, new BlockPos(x + 1, y - 1, z)); + actions[13] = new ActionFall(pos); + actions[14] = new ActionDescendTwo(pos, new BlockPos(x, y - 2, z - 1)); + actions[15] = new ActionDescendTwo(pos, new BlockPos(x, y - 2, z + 1)); + actions[16] = new ActionDescendTwo(pos, new BlockPos(x - 1, y - 2, z)); + actions[17] = new ActionDescendTwo(pos, new BlockPos(x + 1, y - 2, z)); + actions[18] = new ActionDescendThree(pos, new BlockPos(x, y - 3, z - 1)); + actions[19] = new ActionDescendThree(pos, new BlockPos(x, y - 3, z + 1)); + actions[20] = new ActionDescendThree(pos, new BlockPos(x - 1, y - 3, z)); + actions[21] = new ActionDescendThree(pos, new BlockPos(x + 1, y - 3, z)); + actions[22] = new ActionWalkDiagonal(pos, EnumFacing.NORTH, EnumFacing.WEST); + actions[23] = new ActionWalkDiagonal(pos, EnumFacing.NORTH, EnumFacing.EAST); + actions[24] = new ActionWalkDiagonal(pos, EnumFacing.SOUTH, EnumFacing.WEST); + actions[25] = new ActionWalkDiagonal(pos, EnumFacing.SOUTH, EnumFacing.EAST); + return actions;*/ + return null; + } + + private final Random random = new Random(); + + private void shuffle(T[] list) { + int len = list.length; + for (int i = 0; i < len; i++) { + int j = random.nextInt(len); + T t = list[j]; + list[j] = list[i]; + list[i] = t; + } + } +} diff --git a/src/main/java/baritone/bot/pathing/calc/AbstractNodeCostSearch.java b/src/main/java/baritone/bot/pathing/calc/AbstractNodeCostSearch.java index 6abb36e0..718157da 100644 --- a/src/main/java/baritone/bot/pathing/calc/AbstractNodeCostSearch.java +++ b/src/main/java/baritone/bot/pathing/calc/AbstractNodeCostSearch.java @@ -6,11 +6,16 @@ import net.minecraft.util.math.BlockPos; import java.util.HashMap; import java.util.Map; +/** + * Any pathfinding algorithm that keeps track of nodes recursively by their cost (e.g. A*, dijkstra) + * + * @author leijurv + */ public abstract class AbstractNodeCostSearch implements IPathFinder { /** * The currently running search task - * + *

* TODO: This shouldn't be necessary, investigate old purpose of this field and determine necessity. */ public static AbstractNodeCostSearch currentlyRunning = null; @@ -63,10 +68,9 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { * node. Intended for use in distance comparison, rather than anything that * considers the real distance value, hence the "sq". * - * @see AbstractNodeCostSearch#getDistFromStart(PathNode) - * * @param n A node * @return The distance, squared + * @see AbstractNodeCostSearch#getDistFromStart(PathNode) */ protected double getDistFromStartSq(PathNode n) { int xDiff = n.pos.getX() - start.getX(); diff --git a/src/main/java/baritone/bot/pathing/calc/FibonacciHeap.java b/src/main/java/baritone/bot/pathing/calc/FibonacciHeap.java new file mode 100644 index 00000000..be61cbf7 --- /dev/null +++ b/src/main/java/baritone/bot/pathing/calc/FibonacciHeap.java @@ -0,0 +1,465 @@ +//Source: https://github.com/nlfiedler/graphmaker/blob/master/core/src/com/bluemarsh/graphmaker/core/util/FibonacciHeap.java +package baritone.bot.pathing.calc; +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is GraphMaker. The Initial Developer of the Original + * Software is Nathan L. Fiedler. Portions created by Nathan L. Fiedler + * are Copyright (C) 1999-2008. All Rights Reserved. + * + * Contributor(s): Nathan L. Fiedler. + * + * $Id$ + */ +//package com.bluemarsh.graphmaker.core.util; + +/** + * This class implements a Fibonacci heap data structure. Much of the + * code in this class is based on the algorithms in Chapter 21 of the + * "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein. + * The amortized running time of most of these methods is O(1), making + * it a very fast data structure. Several have an actual running time + * of O(1). removeMin() and delete() have O(log n) amortized running + * times because they do the heap consolidation. + * + *

Note that this implementation is not synchronized. + * If multiple threads access a set concurrently, and at least one of the + * threads modifies the set, it must be synchronized externally. + * This is typically accomplished by synchronizing on some object that + * naturally encapsulates the set.

+ * + * @author Nathan Fiedler + */ +public class FibonacciHeap { + /** + * Points to the minimum node in the heap. + */ + private Node min; + /** + * Number of nodes in the heap. If the type is ever widened, + * (e.g. changed to long) then recalcuate the maximum degree + * value used in the consolidate() method. + */ + private int n; + + /** + * Joins two Fibonacci heaps into a new one. No heap consolidation is + * performed at this time. The two root lists are simply joined together. + * + *

Running time: O(1)

+ * + * @param H1 first heap + * @param H2 second heap + * @return new heap containing H1 and H2 + */ + public static FibonacciHeap union(FibonacciHeap H1, FibonacciHeap H2) { + FibonacciHeap H = new FibonacciHeap(); + if (H1 != null && H2 != null) { + H.min = H1.min; + if (H.min != null) { + if (H2.min != null) { + H.min.right.left = H2.min.left; + H2.min.left.right = H.min.right; + H.min.right = H2.min; + H2.min.left = H.min; + if (H2.min.key < H1.min.key) { + H.min = H2.min; + } + } + } else { + H.min = H2.min; + } + H.n = H1.n + H2.n; + } + return H; + } + + /** + * Removes all elements from this heap. + * + *

Running time: O(1)

+ */ + public void clear() { + min = null; + n = 0; + } + + /** + * Consolidates the trees in the heap by joining trees of equal + * degree until there are no more trees of equal degree in the + * root list. + * + *

Running time: O(log n) amortized

+ */ + private void consolidate() { + // The magic 45 comes from log base phi of Integer.MAX_VALUE, + // which is the most elements we will ever hold, and log base + // phi represents the largest degree of any root list node. + Node[] A = new Node[45]; + + // For each root list node look for others of the same degree. + Node start = min; + Node w = min; + do { + Node x = w; + // Because x might be moved, save its sibling now. + Node nextW = w.right; + int d = x.degree; + while (A[d] != null) { + // Make one of the nodes a child of the other. + Node y = A[d]; + if (x.key > y.key) { + Node temp = y; + y = x; + x = temp; + } + if (y == start) { + // Because removeMin() arbitrarily assigned the min + // reference, we have to ensure we do not miss the + // end of the root node list. + start = start.right; + } + if (y == nextW) { + // If we wrapped around we need to check for this case. + nextW = nextW.right; + } + // Node y disappears from root list. + y.link(x); + // We've handled this degree, go to next one. + A[d] = null; + d++; + } + // Save this node for later when we might encounter another + // of the same degree. + A[d] = x; + // Move forward through list. + w = nextW; + } while (w != start); + + // The node considered to be min may have been changed above. + min = start; + // Find the minimum key again. + for (Node a : A) { + if (a != null && a.key < min.key) { + min = a; + } + } + } + + /** + * Decreases the key value for a heap node, given the new value + * to take on. The structure of the heap may be changed, but will + * not be consolidated. + * + *

Running time: O(1) amortized

+ * + * @param x node to decrease the key of + * @param k new key value for node x + * @throws IllegalArgumentException if k is larger than x.key value. + */ + public void decreaseKey(Node x, double k) { + decreaseKey(x, k, false); + } + + /** + * Decrease the key value of a node, or simply bubble it up to the + * top of the heap in preparation for a delete operation. + * + * @param x node to decrease the key of. + * @param k new key value for node x. + * @param delete true if deleting node (in which case, k is ignored). + */ + private void decreaseKey(Node x, double k, boolean delete) { + if (!delete && k > x.key) { + throw new IllegalArgumentException("cannot increase key value"); + } + x.key = k; + Node y = x.parent; + if (y != null && (delete || k < y.key)) { + y.cut(x, min); + y.cascadingCut(min); + } + if (delete || k < min.key) { + min = x; + } + } + + /** + * Deletes a node from the heap given the reference to the node. + * The trees in the heap will be consolidated, if necessary. + * + *

Running time: O(log n) amortized

+ * + * @param x node to remove from heap. + */ + public void delete(Node x) { + // make x as small as possible + decreaseKey(x, 0, true); + // remove the smallest, which decreases n also + removeMin(); + } + + /** + * Tests if the Fibonacci heap is empty or not. Returns true if + * the heap is empty, false otherwise. + * + *

Running time: O(1)

+ * + * @return true if the heap is empty, false otherwise. + */ + public boolean isEmpty() { + return min == null; + } + + /** + * Inserts a new data element into the heap. No heap consolidation + * is performed at this time, the new node is simply inserted into + * the root list of this heap. + * + *

Running time: O(1)

+ * + * @param x data object to insert into heap. + * @param key key value associated with data object. + * @return newly created heap node. + */ + public Node insert(Object x, double key) { + Node node = new Node(x, key); + // concatenate node into min list + if (min != null) { + node.right = min; + node.left = min.left; + min.left = node; + node.left.right = node; + if (key < min.key) { + min = node; + } + } else { + min = node; + } + n++; + return node; + } + + /** + * Returns the smallest element in the heap. This smallest element + * is the one with the minimum key value. + * + *

Running time: O(1)

+ * + * @return heap node with the smallest key, or null if empty. + */ + public Node min() { + return min; + } + + /** + * Removes the smallest element from the heap. This will cause + * the trees in the heap to be consolidated, if necessary. + * + *

Running time: O(log n) amortized

+ * + * @return data object with the smallest key. + */ + public Object removeMin() { + Node z = min; + if (z == null) { + return null; + } + if (z.child != null) { + z.child.parent = null; + // for each child of z do... + for (Node x = z.child.right; x != z.child; x = x.right) { + // set parent[x] to null + x.parent = null; + } + // merge the children into root list + Node minleft = min.left; + Node zchildleft = z.child.left; + min.left = zchildleft; + zchildleft.right = min; + z.child.left = minleft; + minleft.right = z.child; + } + // remove z from root list of heap + z.left.right = z.right; + z.right.left = z.left; + if (z == z.right) { + min = null; + } else { + min = z.right; + consolidate(); + } + // decrement size of heap + n--; + return z.data; + } + + /** + * Returns the size of the heap which is measured in the + * number of elements contained in the heap. + * + *

Running time: O(1)

+ * + * @return number of elements in the heap. + */ + public int size() { + return n; + } + + /** + * Implements a node of the Fibonacci heap. It holds the information + * necessary for maintaining the structure of the heap. It acts as + * an opaque handle for the data element, and serves as the key to + * retrieving the data from the heap. + * + * @author Nathan Fiedler + */ + public static class Node { + /** + * Data object for this node, holds the key value. + */ + private Object data; + /** + * Key value for this node. + */ + private double key; + /** + * Parent node. + */ + private Node parent; + /** + * First child node. + */ + private Node child; + /** + * Right sibling node. + */ + private Node right; + /** + * Left sibling node. + */ + private Node left; + /** + * Number of children of this node. + */ + private int degree; + /** + * True if this node has had a child removed since this node was + * added to its parent. + */ + private boolean mark; + + /** + * Two-arg constructor which sets the data and key fields to the + * passed arguments. It also initializes the right and left pointers, + * making this a circular doubly-linked list. + * + * @param data data object to associate with this node + * @param key key value for this data object + */ + public Node(Object data, double key) { + this.data = data; + this.key = key; + right = this; + left = this; + } + + /** + * Performs a cascading cut operation. Cuts this from its parent + * and then does the same for its parent, and so on up the tree. + * + *

Running time: O(log n)

+ * + * @param min the minimum heap node, to which nodes will be added. + */ + public void cascadingCut(Node min) { + Node z = parent; + // if there's a parent... + if (z != null) { + if (mark) { + // it's marked, cut it from parent + z.cut(this, min); + // cut its parent as well + z.cascadingCut(min); + } else { + // if y is unmarked, set it marked + mark = true; + } + } + } + + /** + * The reverse of the link operation: removes x from the child + * list of this node. + * + *

Running time: O(1)

+ * + * @param x child to be removed from this node's child list + * @param min the minimum heap node, to which x is added. + */ + public void cut(Node x, Node min) { + // remove x from childlist and decrement degree + x.left.right = x.right; + x.right.left = x.left; + degree--; + // reset child if necessary + if (degree == 0) { + child = null; + } else if (child == x) { + child = x.right; + } + // add x to root list of heap + x.right = min; + x.left = min.left; + min.left = x; + x.left.right = x; + // set parent[x] to nil + x.parent = null; + // set mark[x] to false + x.mark = false; + } + + /** + * Make this node a child of the given parent node. All linkages + * are updated, the degree of the parent is incremented, and + * mark is set to false. + * + * @param parent the new parent node. + */ + public void link(Node parent) { + // Note: putting this code here in Node makes it 7x faster + // because it doesn't have to use generated accessor methods, + // which add a lot of time when called millions of times. + // remove this from its circular list + left.right = right; + right.left = left; + // make this a child of x + this.parent = parent; + if (parent.child == null) { + parent.child = this; + right = this; + left = this; + } else { + left = parent.child; + right = parent.child.right; + parent.child.right = this; + right.left = this; + } + // increase degree[x] + parent.degree++; + // set mark false + mark = false; + } + } +} \ No newline at end of file diff --git a/src/main/java/baritone/bot/pathing/calc/FibonacciHeapOpenSet.java b/src/main/java/baritone/bot/pathing/calc/FibonacciHeapOpenSet.java new file mode 100644 index 00000000..a7771b50 --- /dev/null +++ b/src/main/java/baritone/bot/pathing/calc/FibonacciHeapOpenSet.java @@ -0,0 +1,19 @@ +package baritone.bot.pathing.calc; + +/** + * Wrapper adapter between FibonacciHeap and OpenSet + * + * @author leijurv + */ +public class FibonacciHeapOpenSet extends FibonacciHeap implements IOpenSet { + //isEmpty is already defined in FibonacciHeap + @Override + public void insert(PathNode node) { + super.insert(node, node.estimatedCostToGoal + node.cost); + } + + @Override + public PathNode removeLowest() { + return (PathNode) super.removeMin(); + } +} diff --git a/src/main/java/baritone/bot/pathing/calc/IOpenSet.java b/src/main/java/baritone/bot/pathing/calc/IOpenSet.java new file mode 100644 index 00000000..331aa480 --- /dev/null +++ b/src/main/java/baritone/bot/pathing/calc/IOpenSet.java @@ -0,0 +1,14 @@ +package baritone.bot.pathing.calc; + +/** + * An open set for A* or similar graph search algorithm + * + * @author leijurv + */ +public interface IOpenSet { + boolean isEmpty(); + + void insert(PathNode node); + + PathNode removeLowest(); +} diff --git a/src/main/java/baritone/bot/pathing/calc/IPathFinder.java b/src/main/java/baritone/bot/pathing/calc/IPathFinder.java index 63fa8a3c..ab5ac11d 100644 --- a/src/main/java/baritone/bot/pathing/calc/IPathFinder.java +++ b/src/main/java/baritone/bot/pathing/calc/IPathFinder.java @@ -3,6 +3,11 @@ package baritone.bot.pathing.calc; import baritone.bot.pathing.goals.Goal; import net.minecraft.util.math.BlockPos; +/** + * Generic path finder interface + * + * @author leijurv + */ public interface IPathFinder { BlockPos getStart(); diff --git a/src/main/java/baritone/bot/pathing/calc/LinkedListOpenSet.java b/src/main/java/baritone/bot/pathing/calc/LinkedListOpenSet.java new file mode 100644 index 00000000..b38c6378 --- /dev/null +++ b/src/main/java/baritone/bot/pathing/calc/LinkedListOpenSet.java @@ -0,0 +1,49 @@ +package baritone.bot.pathing.calc; + +/** + * + */ +public class LinkedListOpenSet implements IOpenSet { + private PathNode first = null; + + public boolean isEmpty() { + return first == null; + } + + public void insert(PathNode node) { + node.nextOpen = first; + first = node; + } + + public PathNode removeLowest() { + if (first == null) { + return null; + } + PathNode current = first.nextOpen; + if (current == null) { + PathNode n = first; + first = null; + return n; + } + PathNode previous = first; + double bestValue = first.estimatedCostToGoal + first.cost; + PathNode bestNode = first; + PathNode beforeBest = null; + while (current != null) { + double comp = current.estimatedCostToGoal + current.cost; + if (comp < bestValue) { + bestValue = comp; + bestNode = current; + beforeBest = previous; + } + previous = current; + current = current.nextOpen; + } + if (beforeBest == null) { + first = first.nextOpen; + return bestNode; + } + beforeBest.nextOpen = bestNode.nextOpen; + return bestNode; + } +} diff --git a/src/main/java/baritone/bot/pathing/calc/PathNode.java b/src/main/java/baritone/bot/pathing/calc/PathNode.java index 4074b2e7..2626f67b 100644 --- a/src/main/java/baritone/bot/pathing/calc/PathNode.java +++ b/src/main/java/baritone/bot/pathing/calc/PathNode.java @@ -1,7 +1,7 @@ package baritone.bot.pathing.calc; -import baritone.bot.pathing.movement.Movement; import baritone.bot.pathing.goals.Goal; +import baritone.bot.pathing.movement.Movement; import net.minecraft.util.math.BlockPos; import java.util.Objects;