friendship ended with linked list. now binary heap is my best friend.
This commit is contained in:
parent
b6d40785a1
commit
cd2b5d001e
3
.gitignore
vendored
3
.gitignore
vendored
@ -11,4 +11,5 @@ classes/
|
|||||||
|
|
||||||
# IntelliJ Files
|
# IntelliJ Files
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
|
/logs/
|
@ -55,4 +55,4 @@ public final class GameEventHandler implements IGameEventListener {
|
|||||||
private void dispatch(Consumer<Behavior> dispatchFunction) {
|
private void dispatch(Consumer<Behavior> dispatchFunction) {
|
||||||
Baritone.INSTANCE.getBehaviors().stream().filter(Behavior::isEnabled).forEach(dispatchFunction);
|
Baritone.INSTANCE.getBehaviors().stream().filter(Behavior::isEnabled).forEach(dispatchFunction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package baritone.bot.pathing.calc;
|
package baritone.bot.pathing.calc;
|
||||||
|
|
||||||
|
|
||||||
import baritone.Baritone;
|
//import baritone.Baritone;
|
||||||
|
|
||||||
import baritone.bot.pathing.goals.Goal;
|
import baritone.bot.pathing.goals.Goal;
|
||||||
import baritone.bot.pathing.movement.ActionCosts;
|
import baritone.bot.pathing.movement.ActionCosts;
|
||||||
import baritone.bot.pathing.movement.Movement;
|
import baritone.bot.pathing.movement.Movement;
|
||||||
@ -11,8 +12,6 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import net.minecraft.world.chunk.EmptyChunk;
|
import net.minecraft.world.chunk.EmptyChunk;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual A* pathfinding
|
* The actual A* pathfinding
|
||||||
@ -28,7 +27,8 @@ public class AStarPathFinder extends AbstractNodeCostSearch {
|
|||||||
protected IPath calculate0() {
|
protected IPath calculate0() {
|
||||||
startNode = getNodeAtPosition(start);
|
startNode = getNodeAtPosition(start);
|
||||||
startNode.cost = 0;
|
startNode.cost = 0;
|
||||||
IOpenSet openSet = new LinkedListOpenSet();
|
startNode.combinedCost = startNode.estimatedCostToGoal;
|
||||||
|
IOpenSet openSet = new BinaryHeapOpenSet();
|
||||||
startNode.isOpen = true;
|
startNode.isOpen = true;
|
||||||
openSet.insert(startNode);
|
openSet.insert(startNode);
|
||||||
bestSoFar = new PathNode[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i])
|
bestSoFar = new PathNode[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i])
|
||||||
@ -38,19 +38,19 @@ public class AStarPathFinder extends AbstractNodeCostSearch {
|
|||||||
}
|
}
|
||||||
currentlyRunning = this;
|
currentlyRunning = this;
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
long timeoutTime = startTime + (Baritone.slowPath ? 40000 : 4000);
|
long timeoutTime = startTime + /*(Baritone.slowPath ? 40000 : */4000/*)*/;
|
||||||
long lastPrintout = 0;
|
long lastPrintout = 0;
|
||||||
int numNodes = 0;
|
int numNodes = 0;
|
||||||
ToolSet ts = new ToolSet();
|
ToolSet ts = new ToolSet();
|
||||||
int numEmptyChunk = 0;
|
int numEmptyChunk = 0;
|
||||||
while (!openSet.isEmpty() && numEmptyChunk < 50 && System.currentTimeMillis() < timeoutTime) {
|
while (!openSet.isEmpty() && numEmptyChunk < 50 && System.currentTimeMillis() < timeoutTime) {
|
||||||
if (Baritone.slowPath) {
|
/*if (Baritone.slowPath) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
Logger.getLogger(AStarPathFinder.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(AStarPathFinder.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
PathNode currentNode = openSet.removeLowest();
|
PathNode currentNode = openSet.removeLowest();
|
||||||
mostRecentConsidered = currentNode;
|
mostRecentConsidered = currentNode;
|
||||||
currentNode.isOpen = false;
|
currentNode.isOpen = false;
|
||||||
@ -89,6 +89,7 @@ public class AStarPathFinder extends AbstractNodeCostSearch {
|
|||||||
neighbor.previous = currentNode;
|
neighbor.previous = currentNode;
|
||||||
neighbor.previousMovement = movementToGetToNeighbor;
|
neighbor.previousMovement = movementToGetToNeighbor;
|
||||||
neighbor.cost = tentativeCost;
|
neighbor.cost = tentativeCost;
|
||||||
|
neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal;
|
||||||
if (!neighbor.isOpen) {
|
if (!neighbor.isOpen) {
|
||||||
openSet.insert(neighbor);//dont double count, dont insert into open set if it's already there
|
openSet.insert(neighbor);//dont double count, dont insert into open set if it's already there
|
||||||
neighbor.isOpen = true;
|
neighbor.isOpen = true;
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package baritone.bot.pathing.calc;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class BinaryHeapOpenSet implements IOpenSet {
|
||||||
|
private static final int INITIAL_CAPACITY = 1024;
|
||||||
|
private PathNode[] array;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public BinaryHeapOpenSet() {
|
||||||
|
this(INITIAL_CAPACITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryHeapOpenSet(int size) {
|
||||||
|
this.size = 0;
|
||||||
|
this.array = new PathNode[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(PathNode value) {
|
||||||
|
if (size >= array.length - 1) {
|
||||||
|
array = Arrays.copyOf(array, array.length * 2);
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
int index = size;
|
||||||
|
array[index] = value;
|
||||||
|
int parent = index >>> 1;
|
||||||
|
while (index > 1 && array[parent].combinedCost > array[index].combinedCost) {
|
||||||
|
swap(index, parent);
|
||||||
|
index = parent;
|
||||||
|
parent = index >>> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the heap has no elements; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes and returns the minimum element in the heap.
|
||||||
|
*/
|
||||||
|
public PathNode removeLowest() {
|
||||||
|
if (size == 0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
PathNode result = array[1];
|
||||||
|
array[1] = array[size];
|
||||||
|
array[size] = null;
|
||||||
|
size--;
|
||||||
|
int index = 1;
|
||||||
|
int smallerChild = 2;
|
||||||
|
while (smallerChild <= size) {
|
||||||
|
int right = smallerChild + 1;
|
||||||
|
if (right <= size && array[smallerChild].combinedCost > array[right].combinedCost) {
|
||||||
|
smallerChild = right;
|
||||||
|
}
|
||||||
|
if (array[index].combinedCost > array[smallerChild].combinedCost) {
|
||||||
|
swap(index, smallerChild);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index = smallerChild;
|
||||||
|
smallerChild = index << 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void swap(int index1, int index2) {
|
||||||
|
PathNode tmp = array[index1];
|
||||||
|
array[index1] = array[index2];
|
||||||
|
array[index2] = tmp;
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ public class FibonacciHeapOpenSet extends FibonacciHeap implements IOpenSet {
|
|||||||
//isEmpty is already defined in FibonacciHeap
|
//isEmpty is already defined in FibonacciHeap
|
||||||
@Override
|
@Override
|
||||||
public void insert(PathNode node) {
|
public void insert(PathNode node) {
|
||||||
super.insert(node, node.estimatedCostToGoal + node.cost);
|
super.insert(node, node.combinedCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package baritone.bot.pathing.calc;
|
package baritone.bot.pathing.calc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* A linked list implementation of an open set. This is the original implementation from MineBot.
|
||||||
|
* It has incredbly fast insert performance, at the cost of O(n) removeLowest.
|
||||||
*/
|
*/
|
||||||
public class LinkedListOpenSet implements IOpenSet {
|
public class LinkedListOpenSet implements IOpenSet {
|
||||||
private PathNode first = null;
|
private PathNode first = null;
|
||||||
@ -26,11 +27,11 @@ public class LinkedListOpenSet implements IOpenSet {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
PathNode previous = first;
|
PathNode previous = first;
|
||||||
double bestValue = first.estimatedCostToGoal + first.cost;
|
double bestValue = first.combinedCost;
|
||||||
PathNode bestNode = first;
|
PathNode bestNode = first;
|
||||||
PathNode beforeBest = null;
|
PathNode beforeBest = null;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
double comp = current.estimatedCostToGoal + current.cost;
|
double comp = current.combinedCost;
|
||||||
if (comp < bestValue) {
|
if (comp < bestValue) {
|
||||||
bestValue = comp;
|
bestValue = comp;
|
||||||
bestNode = current;
|
bestNode = current;
|
||||||
|
@ -22,6 +22,8 @@ class PathNode {
|
|||||||
// These three fields are mutable and are changed by PathFinder
|
// These three fields are mutable and are changed by PathFinder
|
||||||
double cost;
|
double cost;
|
||||||
|
|
||||||
|
public double combinedCost;
|
||||||
|
|
||||||
PathNode previous;
|
PathNode previous;
|
||||||
|
|
||||||
Movement previousMovement;
|
Movement previousMovement;
|
||||||
|
@ -17,6 +17,11 @@ public class MovementAscend extends Movement {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinish() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MovementState updateState() {
|
public MovementState updateState() {
|
||||||
MovementState latestState = currentState.setInput(InputOverrideHandler.Input.JUMP, true).setInput(InputOverrideHandler.Input.MOVE_FORWARD, true);
|
MovementState latestState = currentState.setInput(InputOverrideHandler.Input.JUMP, true).setInput(InputOverrideHandler.Input.MOVE_FORWARD, true);
|
||||||
|
66
src/test/java/baritone/bot/pathing/calc/OpenSetsTest.java
Normal file
66
src/test/java/baritone/bot/pathing/calc/OpenSetsTest.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package baritone.bot.pathing.calc;
|
||||||
|
|
||||||
|
import baritone.bot.pathing.goals.GoalBlock;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class OpenSetsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOpenSets() {
|
||||||
|
for (int size = 1; size < 100; size++) {
|
||||||
|
testSize(size);
|
||||||
|
}
|
||||||
|
for (int size = 100; size < 10000; size += 100) {
|
||||||
|
testSize(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSize(int size) {
|
||||||
|
System.out.println("Testing size " + size);
|
||||||
|
// Include LinkedListOpenSet even though it's not performant because I absolutely trust that it behaves properly
|
||||||
|
// I'm really testing the heap implementations against it as the ground truth
|
||||||
|
IOpenSet[] test = new IOpenSet[]{new BinaryHeapOpenSet(), new LinkedListOpenSet(), new FibonacciHeapOpenSet()};
|
||||||
|
for (IOpenSet set : test) {
|
||||||
|
assertTrue(set.isEmpty());
|
||||||
|
}
|
||||||
|
PathNode[] toInsert = new PathNode[size];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
PathNode pn = new PathNode(new BlockPos(0, 0, 0), new GoalBlock(new BlockPos(0, 0, 0)));
|
||||||
|
pn.combinedCost = Math.random();
|
||||||
|
toInsert[i] = pn;
|
||||||
|
|
||||||
|
}
|
||||||
|
System.out.println("Insertion");
|
||||||
|
for (IOpenSet set : test) {
|
||||||
|
long before = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
set.insert(toInsert[i]);
|
||||||
|
System.out.println(set.getClass() + " " + (System.currentTimeMillis() - before));
|
||||||
|
//all three take either 0 or 1ms to insert up to 10,000 nodes
|
||||||
|
//linkedlist takes 0ms most often (because there's no array resizing or allocation there, just pointer shuffling)
|
||||||
|
}
|
||||||
|
for (IOpenSet set : test) {
|
||||||
|
assertFalse(set.isEmpty());
|
||||||
|
}
|
||||||
|
System.out.println("Removal");
|
||||||
|
double[][] results = new double[test.length][size];
|
||||||
|
for (int i = 0; i < test.length; i++) {
|
||||||
|
long before = System.currentTimeMillis();
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
results[i][j] = test[i].removeLowest().combinedCost;
|
||||||
|
}
|
||||||
|
System.out.println(test[i].getClass() + " " + (System.currentTimeMillis() - before));
|
||||||
|
}
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
for (int i = 1; i < test.length; i++) {
|
||||||
|
assertEquals(results[i][j], results[0][j], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (IOpenSet set : test) {
|
||||||
|
assertTrue(set.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user