better path splicing

This commit is contained in:
Leijurv 2018-08-16 15:10:15 -07:00
parent 399fa003dd
commit e0f2159276
No known key found for this signature in database
GPG Key ID: 44A3EA646EADAC6A
4 changed files with 53 additions and 28 deletions

View File

@ -35,17 +35,22 @@ public class Settings {
public Setting<Boolean> chuckCaching = new Setting<>(false); public Setting<Boolean> chuckCaching = new Setting<>(false);
public Setting<Boolean> allowWaterBucketFall = new Setting<>(true); public Setting<Boolean> allowWaterBucketFall = new Setting<>(true);
public Setting<Integer> planningTickLookAhead = new Setting<>(150); public Setting<Integer> planningTickLookAhead = new Setting<>(150);
public Setting<Boolean> renderPath = new Setting<>(true);
public Setting<Boolean> chatDebug = new Setting<>(true); public Setting<Boolean> chatDebug = new Setting<>(true);
public Setting<Boolean> chatControl = new Setting<>(true); // probably false in impact public Setting<Boolean> chatControl = new Setting<>(true); // probably false in impact
public Setting<Boolean> renderPath = new Setting<>(true);
public Setting<Boolean> fadePath = new Setting<>(false); // give this a better name in the UI, like "better path fps" idk public Setting<Boolean> fadePath = new Setting<>(false); // give this a better name in the UI, like "better path fps" idk
public Setting<Number> pathTimeoutMS = new Setting<>(4000L);
public Setting<Boolean> slowPath = new Setting<>(false); public Setting<Boolean> slowPath = new Setting<>(false);
public Setting<Number> slowPathTimeDelayMS = new Setting<>(100L);
public Setting<Number> slowPathTimeoutMS = new Setting<>(40000L);
public Setting<List<Item>> acceptableThrowAwayItems = new Setting<>(Arrays.asList( public Setting<List<Item>> acceptableThrowAwayItems = new Setting<>(Arrays.asList(
Item.getItemFromBlock(Blocks.DIRT), Item.getItemFromBlock(Blocks.DIRT),
Item.getItemFromBlock(Blocks.COBBLESTONE), Item.getItemFromBlock(Blocks.COBBLESTONE),
Item.getItemFromBlock(Blocks.NETHERRACK) Item.getItemFromBlock(Blocks.NETHERRACK)
)); ));
public Setting<Boolean> renderGoal = new Setting<>(true); public Setting<Boolean> renderGoal = new Setting<>(true);
public Setting<Integer> pathingMaxChunkBorderFetch = new Setting<>(50);
public Setting<Double> backtrackCostFavoringCoefficient = new Setting<>(0.9);
public final Map<String, Setting<?>> byName; public final Map<String, Setting<?>> byName;
public final List<Setting<?>> allSettings; public final List<Setting<?>> allSettings;
@ -63,8 +68,8 @@ public class Settings {
this.klass = (Class<T>) value.getClass(); this.klass = (Class<T>) value.getClass();
} }
public final T get() { public final <K extends T> K get() {
return value; return (K) value;
} }
public final String getName() { public final String getName() {

View File

@ -93,7 +93,7 @@ public class PathingBehavior extends Behavior {
// if we aren't calculating right now // if we aren't calculating right now
return; return;
} }
findPathInNewThread(playerFeet(), true); findPathInNewThread(playerFeet(), true, Optional.empty());
} }
return; return;
} }
@ -126,7 +126,7 @@ public class PathingBehavior extends Behavior {
if (ticksRemainingInSegment().get() < Baritone.settings().planningTickLookAhead.get()) { if (ticksRemainingInSegment().get() < Baritone.settings().planningTickLookAhead.get()) {
// and this path has 5 seconds or less left // and this path has 5 seconds or less left
displayChatMessageRaw("Path almost over. Planning ahead..."); displayChatMessageRaw("Path almost over. Planning ahead...");
findPathInNewThread(current.getPath().getDest(), false); findPathInNewThread(current.getPath().getDest(), false, Optional.of(current.getPath()));
} }
} }
} }
@ -160,11 +160,17 @@ public class PathingBehavior extends Behavior {
} }
public void path() { public void path() {
synchronized (pathCalcLock) { synchronized (pathPlanLock) {
if (isPathCalcInProgress) { if (current != null) {
displayChatMessageRaw("Currently executing a path. Please cancel it first.");
return; return;
} }
findPathInNewThread(playerFeet(), true); synchronized (pathCalcLock) {
if (isPathCalcInProgress) {
return;
}
findPathInNewThread(playerFeet(), true, Optional.empty());
}
} }
} }
@ -174,7 +180,7 @@ public class PathingBehavior extends Behavior {
* @param start * @param start
* @param talkAboutIt * @param talkAboutIt
*/ */
private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt) { private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt, final Optional<IPath> previous) {
synchronized (pathCalcLock) { synchronized (pathCalcLock) {
if (isPathCalcInProgress) { if (isPathCalcInProgress) {
throw new IllegalStateException("Already doing it"); throw new IllegalStateException("Already doing it");
@ -186,7 +192,7 @@ public class PathingBehavior extends Behavior {
displayChatMessageRaw("Starting to search for path from " + start + " to " + goal); displayChatMessageRaw("Starting to search for path from " + start + " to " + goal);
} }
findPath(start).map(IPath::cutoffAtLoadedChunks).map(PathExecutor::new).ifPresent(path -> { findPath(start, previous).map(IPath::cutoffAtLoadedChunks).map(PathExecutor::new).ifPresent(path -> {
synchronized (pathPlanLock) { synchronized (pathPlanLock) {
if (current == null) { if (current == null) {
current = path; current = path;
@ -219,13 +225,13 @@ public class PathingBehavior extends Behavior {
* @param start * @param start
* @return * @return
*/ */
private Optional<IPath> findPath(BlockPos start) { private Optional<IPath> findPath(BlockPos start, Optional<IPath> previous) {
if (goal == null) { if (goal == null) {
displayChatMessageRaw("no goal"); displayChatMessageRaw("no goal");
return Optional.empty(); return Optional.empty();
} }
try { try {
IPathFinder pf = new AStarPathFinder(start, goal); IPathFinder pf = new AStarPathFinder(start, goal, previous.map(IPath::positions));
return pf.calculate(); return pf.calculate();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -251,6 +257,7 @@ public class PathingBehavior extends Behavior {
PathExecutor current = this.current; // this should prevent most race conditions? PathExecutor current = this.current; // this should prevent most race conditions?
PathExecutor next = this.next; // like, now it's not possible for current!=null to be true, then suddenly false because of another thread PathExecutor next = this.next; // like, now it's not possible for current!=null to be true, then suddenly false because of another thread
// TODO is this enough, or do we need to acquire a lock here? // TODO is this enough, or do we need to acquire a lock here?
// TODO benchmark synchronized in render loop
// Render the current path, if there is one // Render the current path, if there is one
if (current != null && current.getPath() != null) { if (current != null && current.getPath() != null) {

View File

@ -52,6 +52,8 @@ import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.EmptyChunk; import net.minecraft.world.chunk.EmptyChunk;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
@ -62,8 +64,11 @@ import java.util.Random;
*/ */
public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
public AStarPathFinder(BlockPos start, Goal goal) { private final Optional<HashSet<BetterBlockPos>> favoredPositions;
public AStarPathFinder(BlockPos start, Goal goal, Optional<Collection<BetterBlockPos>> favoredPositions) {
super(start, goal); super(start, goal);
this.favoredPositions = favoredPositions.map(HashSet::new); // <-- okay this is epic
} }
@Override @Override
@ -72,26 +77,30 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
startNode.cost = 0; startNode.cost = 0;
startNode.combinedCost = startNode.estimatedCostToGoal; startNode.combinedCost = startNode.estimatedCostToGoal;
IOpenSet openSet = new BinaryHeapOpenSet(); IOpenSet openSet = new BinaryHeapOpenSet();
startNode.isOpen = true;
openSet.insert(startNode); openSet.insert(startNode);
startNode.isOpen = true;
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])
double[] bestHeuristicSoFar = new double[COEFFICIENTS.length]; double[] bestHeuristicSoFar = new double[COEFFICIENTS.length];
for (int i = 0; i < bestHeuristicSoFar.length; i++) { for (int i = 0; i < bestHeuristicSoFar.length; i++) {
bestHeuristicSoFar[i] = Double.MAX_VALUE; bestHeuristicSoFar[i] = Double.MAX_VALUE;
} }
CalculationContext calcContext = new CalculationContext();
HashSet<BetterBlockPos> favored = favoredPositions.orElse(null);
currentlyRunning = this; currentlyRunning = this;
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
boolean slowPath = Baritone.settings().slowPath.get(); boolean slowPath = Baritone.settings().slowPath.get();
long timeoutTime = startTime + (slowPath ? 40000 : 4000); long timeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS : Baritone.settings().pathTimeoutMS).<Long>get();
long lastPrintout = 0; long lastPrintout = 0;
int numNodes = 0; int numNodes = 0;
CalculationContext calcContext = new CalculationContext();
int numEmptyChunk = 0; int numEmptyChunk = 0;
boolean favoring = favoredPositions.isPresent(); // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior
boolean cache = Baritone.settings().chuckCaching.get(); boolean cache = Baritone.settings().chuckCaching.get();
while (!openSet.isEmpty() && numEmptyChunk < 50 && System.currentTimeMillis() < timeoutTime) { int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.get();
double favorCoeff = Baritone.settings().backtrackCostFavoringCoefficient.get();
while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && System.currentTimeMillis() < timeoutTime) {
if (slowPath) { if (slowPath) {
try { try {
Thread.sleep(100); Thread.sleep(Baritone.settings().slowPathTimeDelayMS.<Long>get());
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
} }
} }
@ -109,7 +118,7 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
return Optional.of(new Path(startNode, currentNode, numNodes)); return Optional.of(new Path(startNode, currentNode, numNodes));
} }
//long constructStart = System.nanoTime(); //long constructStart = System.nanoTime();
Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at myPos, in random order Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at currentNodePos, in random order
shuffle(possibleMovements); shuffle(possibleMovements);
//long constructEnd = System.nanoTime(); //long constructEnd = System.nanoTime();
//System.out.println(constructEnd - constructStart); //System.out.println(constructEnd - constructStart);
@ -117,16 +126,16 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
if (movementToGetToNeighbor == null) { if (movementToGetToNeighbor == null) {
continue; continue;
} }
BetterBlockPos dest = (BetterBlockPos) movementToGetToNeighbor.getDest();
boolean isPositionCached = false; boolean isPositionCached = false;
if (cache) { if (cache) {
if (CachedWorldProvider.INSTANCE.getCurrentWorld() != null) { if (CachedWorldProvider.INSTANCE.getCurrentWorld() != null) {
if (CachedWorldProvider.INSTANCE.getCurrentWorld().getBlockType(movementToGetToNeighbor.getDest()) != null) { if (CachedWorldProvider.INSTANCE.getCurrentWorld().getBlockType(dest) != null) {
isPositionCached = true; isPositionCached = true;
} }
} }
} }
if (!isPositionCached && Minecraft.getMinecraft().world.getChunk(movementToGetToNeighbor.getDest()) instanceof EmptyChunk) { if (!isPositionCached && Minecraft.getMinecraft().world.getChunk(dest) instanceof EmptyChunk) {
numEmptyChunk++; numEmptyChunk++;
continue; continue;
} }
@ -141,7 +150,10 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
if (actionCost <= 0) { if (actionCost <= 0) {
throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " calculated implausible cost " + actionCost); throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " calculated implausible cost " + actionCost);
} }
PathNode neighbor = getNodeAtPosition((BetterBlockPos) movementToGetToNeighbor.getDest()); if (favoring && favored.contains(dest)) {
actionCost *= favorCoeff;
}
PathNode neighbor = getNodeAtPosition(dest);
double tentativeCost = currentNode.cost + actionCost; double tentativeCost = currentNode.cost + actionCost;
if (tentativeCost < neighbor.cost) { if (tentativeCost < neighbor.cost) {
if (tentativeCost < 0) { if (tentativeCost < 0) {

View File

@ -20,6 +20,7 @@ package baritone.bot.pathing.path;
import baritone.bot.pathing.movement.Movement; import baritone.bot.pathing.movement.Movement;
import baritone.bot.utils.Helper; import baritone.bot.utils.Helper;
import baritone.bot.utils.Utils; import baritone.bot.utils.Utils;
import baritone.bot.utils.pathing.BetterBlockPos;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.util.Tuple; import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -44,7 +45,7 @@ public interface IPath extends Helper {
* All positions along the way. * All positions along the way.
* Should begin with the same as getSrc and end with the same as getDest * Should begin with the same as getSrc and end with the same as getDest
*/ */
List<BlockPos> positions(); List<BetterBlockPos> positions();
/** /**
* Number of positions in this path * Number of positions in this path
@ -62,7 +63,7 @@ public interface IPath extends Helper {
* @return * @return
*/ */
default Movement subsequentMovement(BlockPos currentPosition) { default Movement subsequentMovement(BlockPos currentPosition) {
List<BlockPos> pos = positions(); List<BetterBlockPos> pos = positions();
List<Movement> movements = movements(); List<Movement> movements = movements();
for (int i = 0; i < pos.size(); i++) { for (int i = 0; i < pos.size(); i++) {
if (currentPosition.equals(pos.get(i))) { if (currentPosition.equals(pos.get(i))) {
@ -98,15 +99,15 @@ public interface IPath extends Helper {
/** /**
* Where does this path start * Where does this path start
*/ */
default BlockPos getSrc() { default BetterBlockPos getSrc() {
return positions().get(0); return positions().get(0);
} }
/** /**
* Where does this path end * Where does this path end
*/ */
default BlockPos getDest() { default BetterBlockPos getDest() {
List<BlockPos> pos = positions(); List<BetterBlockPos> pos = positions();
return pos.get(pos.size() - 1); return pos.get(pos.size() - 1);
} }