path finding
This commit is contained in:
180
src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java
Normal file
180
src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java
Normal file
@ -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 <T> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,11 +6,16 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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 {
|
public abstract class AbstractNodeCostSearch implements IPathFinder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently running search task
|
* The currently running search task
|
||||||
*
|
* <p>
|
||||||
* TODO: This shouldn't be necessary, investigate old purpose of this field and determine necessity.
|
* TODO: This shouldn't be necessary, investigate old purpose of this field and determine necessity.
|
||||||
*/
|
*/
|
||||||
public static AbstractNodeCostSearch currentlyRunning = null;
|
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
|
* node. Intended for use in distance comparison, rather than anything that
|
||||||
* considers the real distance value, hence the "sq".
|
* considers the real distance value, hence the "sq".
|
||||||
*
|
*
|
||||||
* @see AbstractNodeCostSearch#getDistFromStart(PathNode)
|
|
||||||
*
|
|
||||||
* @param n A node
|
* @param n A node
|
||||||
* @return The distance, squared
|
* @return The distance, squared
|
||||||
|
* @see AbstractNodeCostSearch#getDistFromStart(PathNode)
|
||||||
*/
|
*/
|
||||||
protected double getDistFromStartSq(PathNode n) {
|
protected double getDistFromStartSq(PathNode n) {
|
||||||
int xDiff = n.pos.getX() - start.getX();
|
int xDiff = n.pos.getX() - start.getX();
|
||||||
|
465
src/main/java/baritone/bot/pathing/calc/FibonacciHeap.java
Normal file
465
src/main/java/baritone/bot/pathing/calc/FibonacciHeap.java
Normal file
@ -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.
|
||||||
|
*
|
||||||
|
* <p><strong>Note that this implementation is not synchronized.</strong>
|
||||||
|
* If multiple threads access a set concurrently, and at least one of the
|
||||||
|
* threads modifies the set, it <em>must</em> be synchronized externally.
|
||||||
|
* This is typically accomplished by synchronizing on some object that
|
||||||
|
* naturally encapsulates the set.</p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(log n) amortized</em></p>
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1) amortized</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(log n) amortized</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(log n) amortized</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(log n)</em></p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* <p><em>Running time: O(1)</em></p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/baritone/bot/pathing/calc/IOpenSet.java
Normal file
14
src/main/java/baritone/bot/pathing/calc/IOpenSet.java
Normal file
@ -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();
|
||||||
|
}
|
@ -3,6 +3,11 @@ package baritone.bot.pathing.calc;
|
|||||||
import baritone.bot.pathing.goals.Goal;
|
import baritone.bot.pathing.goals.Goal;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic path finder interface
|
||||||
|
*
|
||||||
|
* @author leijurv
|
||||||
|
*/
|
||||||
public interface IPathFinder {
|
public interface IPathFinder {
|
||||||
|
|
||||||
BlockPos getStart();
|
BlockPos getStart();
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package baritone.bot.pathing.calc;
|
package baritone.bot.pathing.calc;
|
||||||
|
|
||||||
import baritone.bot.pathing.movement.Movement;
|
|
||||||
import baritone.bot.pathing.goals.Goal;
|
import baritone.bot.pathing.goals.Goal;
|
||||||
|
import baritone.bot.pathing.movement.Movement;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
Reference in New Issue
Block a user