cutoff path up until movement failure, don't throw exception and fail entire path

This commit is contained in:
Leijurv 2018-11-07 14:37:23 -08:00
parent 4a1951b027
commit 96da078219
No known key found for this signature in database
GPG Key ID: 44A3EA646EADAC6A
3 changed files with 42 additions and 18 deletions

View File

@ -52,7 +52,9 @@ public interface IPath {
* This path is actually going to be executed in the world. Do whatever additional processing is required. * This path is actually going to be executed in the world. Do whatever additional processing is required.
* (as opposed to Path objects that are just constructed every frame for rendering) * (as opposed to Path objects that are just constructed every frame for rendering)
*/ */
default void postProcess() {} default IPath postProcess() {
return this;
}
/** /**
* Returns the number of positions in this path. Equivalent to {@code positions().size()}. * Returns the number of positions in this path. Equivalent to {@code positions().size()}.

View File

@ -25,6 +25,7 @@ import baritone.api.utils.BetterBlockPos;
import baritone.pathing.movement.Movement; import baritone.pathing.movement.Movement;
import baritone.pathing.movement.Moves; import baritone.pathing.movement.Moves;
import baritone.pathing.path.CutoffPath; import baritone.pathing.path.CutoffPath;
import baritone.utils.Helper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.EmptyChunk; import net.minecraft.world.chunk.EmptyChunk;
@ -59,6 +60,8 @@ class Path implements IPath {
private final List<Movement> movements; private final List<Movement> movements;
private final List<PathNode> nodes;
private final Goal goal; private final Goal goal;
private final int numNodes; private final int numNodes;
@ -71,8 +74,9 @@ class Path implements IPath {
this.numNodes = numNodes; this.numNodes = numNodes;
this.path = new ArrayList<>(); this.path = new ArrayList<>();
this.movements = new ArrayList<>(); this.movements = new ArrayList<>();
this.nodes = new ArrayList<>();
this.goal = goal; this.goal = goal;
assemblePath(start, end); assemblePath(end);
} }
@Override @Override
@ -81,62 +85,80 @@ class Path implements IPath {
} }
/** /**
* Assembles this path given the start and end nodes. * Assembles this path given the end node.
* *
* @param start The start node
* @param end The end node * @param end The end node
*/ */
private void assemblePath(PathNode start, PathNode end) { private void assemblePath(PathNode end) {
if (!path.isEmpty() || !movements.isEmpty()) { if (!path.isEmpty() || !movements.isEmpty()) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
PathNode current = end; PathNode current = end;
LinkedList<BetterBlockPos> tempPath = new LinkedList<>(); LinkedList<BetterBlockPos> tempPath = new LinkedList<>();
LinkedList<PathNode> tempNodes = new LinkedList();
// Repeatedly inserting to the beginning of an arraylist is O(n^2) // Repeatedly inserting to the beginning of an arraylist is O(n^2)
// Instead, do it into a linked list, then convert at the end // Instead, do it into a linked list, then convert at the end
while (!current.equals(start)) { while (current != null) {
tempNodes.addFirst(current);
tempPath.addFirst(new BetterBlockPos(current.x, current.y, current.z)); tempPath.addFirst(new BetterBlockPos(current.x, current.y, current.z));
current = current.previous; current = current.previous;
} }
tempPath.addFirst(this.start);
// Can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is // Can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is
// inserting into a LinkedList<E> keeps track of length, then when we addall (which calls .toArray) it's able // inserting into a LinkedList<E> keeps track of length, then when we addall (which calls .toArray) it's able
// to performantly do that conversion since it knows the length. // to performantly do that conversion since it knows the length.
path.addAll(tempPath); path.addAll(tempPath);
nodes.addAll(tempNodes);
} }
private void assembleMovements() { private boolean assembleMovements() {
if (path.isEmpty() || !movements.isEmpty()) { if (path.isEmpty() || !movements.isEmpty()) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
for (int i = 0; i < path.size() - 1; i++) { for (int i = 0; i < path.size() - 1; i++) {
movements.add(runBackwards(path.get(i), path.get(i + 1))); double cost = nodes.get(i + 1).cost - nodes.get(i).cost;
Movement move = runBackwards(path.get(i), path.get(i + 1), cost);
if (move == null) {
return true;
} else {
movements.add(move);
} }
} }
return false;
}
private static Movement runBackwards(BetterBlockPos src, BetterBlockPos dest) { // TODO this is horrifying private static Movement runBackwards(BetterBlockPos src, BetterBlockPos dest, double cost) {
for (Moves moves : Moves.values()) { for (Moves moves : Moves.values()) {
Movement move = moves.apply0(src); Movement move = moves.apply0(src);
if (move.getDest().equals(dest)) { if (move.getDest().equals(dest)) {
// TODO instead of recalculating here, could we take pathNode.cost - pathNode.prevNode.cost to get the cost as-calculated? // have to calculate the cost at calculation time so we can accurately judge whether a cost increase happened between cached calculation and real execution
move.recalculateCost(); // have to calculate the cost at calculation time so we can accurately judge whether a cost increase happened between cached calculation and real execution move.override(cost);
return move; return move;
} }
} }
// this is no longer called from bestPathSoFar, now it's in postprocessing // this is no longer called from bestPathSoFar, now it's in postprocessing
throw new IllegalStateException("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src)); Helper.HELPER.logDebug("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src));
return null;
} }
@Override @Override
public void postProcess() { public IPath postProcess() {
if (verified) { if (verified) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
verified = true; verified = true;
assembleMovements(); boolean failed = assembleMovements();
// more post processing here
movements.forEach(Movement::checkLoadedChunk); movements.forEach(Movement::checkLoadedChunk);
if (failed) { // at least one movement became impossible during calculation
CutoffPath res = new CutoffPath(this, movements().size());
if (res.movements().size() != movements.size()) {
throw new IllegalStateException();
}
return res;
}
// more post processing here
sanityCheck(); sanityCheck();
return this;
} }
@Override @Override

View File

@ -95,7 +95,7 @@ public abstract class Movement implements IMovement, Helper, MovementHelper {
return getCost(); return getCost();
} }
protected void override(double cost) { public void override(double cost) {
this.cost = cost; this.cost = cost;
} }