diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index ab758f40..fdbaaf45 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -193,6 +193,18 @@ public class Settings { */ public Setting planningTickLookAhead = new Setting<>(100); + /** + * Default size of the Long2ObjectOpenHashMap used in pathing + */ + public Setting pathingMapDefaultSize = new Setting<>(1024); + + /** + * Load factor coefficient for the Long2ObjectOpenHashMap used in pathing + *

+ * Decrease for faster map operations, but higher memory usage + */ + public Setting pathingMapLoadFactor = new Setting<>(0.75f); + /** * How far are you allowed to fall onto solid ground (without a water bucket)? * 3 won't deal any damage. But if you just want to get down the mountain quickly and you have diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index 51e7598a..4a764bcc 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -109,7 +109,7 @@ public enum Baritone { PathingBehavior.INSTANCE ); } - if (BaritoneAutoTest.ENABLE_AUTO_TEST && "true".equals(System.getenv("BARITONE_AUTO_TEST"))) { + if (BaritoneAutoTest.ENABLE_AUTO_TEST) { registerEventListener(BaritoneAutoTest.INSTANCE); } this.dir = new File(Minecraft.getMinecraft().gameDir, "baritone"); diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index c6645a5c..fab4fae3 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -48,7 +48,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel @Override protected Optional calculate0(long timeout) { - startNode = getNodeAtPosition(start.x, start.y, start.z); + startNode = getNodeAtPosition(start.x, start.y, start.z, posHash(start.x, start.y, start.z)); startNode.cost = 0; startNode.combinedCost = startNode.estimatedCostToGoal; BinaryHeapOpenSet openSet = new BinaryHeapOpenSet(); @@ -166,12 +166,13 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel if (actionCost <= 0) { throw new IllegalStateException(moves + " calculated implausible cost " + actionCost); } - if (favoring && favored.contains(posHash(res.destX, res.destY, res.destZ))) { + long hashCode = posHash(res.destX, res.destY, res.destZ); + if (favoring && favored.contains(hashCode)) { // see issue #18 actionCost *= favorCoeff; } long st = System.nanoTime(); - PathNode neighbor = getNodeAtPosition(res.destX, res.destY, res.destZ); + PathNode neighbor = getNodeAtPosition(res.destX, res.destY, res.destZ, hashCode); getNode += System.nanoTime() - st; getNodeCount++; double tentativeCost = currentNode.cost + actionCost; diff --git a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java index 21f6d848..49e726b0 100644 --- a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java +++ b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java @@ -17,6 +17,7 @@ package baritone.pathing.calc; +import baritone.Baritone; import baritone.api.pathing.goals.Goal; import baritone.pathing.path.IPath; import baritone.utils.pathing.BetterBlockPos; @@ -71,7 +72,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { AbstractNodeCostSearch(BlockPos start, Goal goal) { this.start = new BetterBlockPos(start.getX(), start.getY(), start.getZ()); this.goal = goal; - this.map = new Long2ObjectOpenHashMap<>(); + this.map = new Long2ObjectOpenHashMap<>(Baritone.settings().pathingMapDefaultSize.value, Baritone.settings().pathingMapLoadFactor.get()); } public void cancel() { @@ -128,8 +129,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { * @return The associated node * @see Issue #107 */ - protected PathNode getNodeAtPosition(int x, int y, int z) { - long hashCode = posHash(x, y, z); + protected PathNode getNodeAtPosition(int x, int y, int z, long hashCode) { PathNode node = map.get(hashCode); if (node == null) { node = new PathNode(x, y, z, goal); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index d1fe89a4..d75515d3 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -88,17 +88,31 @@ public class MovementDiagonal extends Movement { IBlockState pb1 = BlockStateInterface.get(x, y + 1, destZ); IBlockState pb2 = BlockStateInterface.get(destX, y, z); IBlockState pb3 = BlockStateInterface.get(destX, y + 1, z); - double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false) + MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); - double optionB = MovementHelper.getMiningDurationTicks(context, destX, y, z, pb2, false) + MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); + double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false); + double optionB = MovementHelper.getMiningDurationTicks(context, destX, y, z, pb2, false); if (optionA != 0 && optionB != 0) { + // check these one at a time -- if pb0 and pb2 were nonzero, we already know that (optionA != 0 && optionB != 0) + // so no need to check pb1 as well, might as well return early here + return COST_INF; + } + optionA += MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); + if (optionA != 0 && optionB != 0) { + // same deal, if pb1 makes optionA nonzero and option B already was nonzero, pb3 can't affect the result return COST_INF; } if (optionA == 0) { + // at this point we're done calculating optionA, so we can check if it's actually possible to edge around in that direction if (MovementHelper.avoidWalkingInto(pb2.getBlock()) || MovementHelper.avoidWalkingInto(pb3.getBlock())) { return COST_INF; } } + optionB += MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); + if (optionA != 0 && optionB != 0) { + // and finally, if the cost is nonzero for both ways to approach this diagonal, it's not possible + return COST_INF; + } if (optionB == 0) { + // and now that option B is fully calculated, see if we can edge around that way if (MovementHelper.avoidWalkingInto(pb0.getBlock()) || MovementHelper.avoidWalkingInto(pb1.getBlock())) { return COST_INF; } diff --git a/src/main/java/baritone/utils/BaritoneAutoTest.java b/src/main/java/baritone/utils/BaritoneAutoTest.java index 071b1a90..faad6153 100644 --- a/src/main/java/baritone/utils/BaritoneAutoTest.java +++ b/src/main/java/baritone/utils/BaritoneAutoTest.java @@ -37,7 +37,7 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper { private BaritoneAutoTest() {} - public static final boolean ENABLE_AUTO_TEST = true; + public static final boolean ENABLE_AUTO_TEST = "true".equals(System.getenv("BARITONE_AUTO_TEST")); private static final long TEST_SEED = -928872506371745L; private static final BlockPos STARTING_POSITION = new BlockPos(0, 65, 0); private static final Goal GOAL = new GoalBlock(69, 121, 420); @@ -47,7 +47,7 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper { * Called right after the {@link GameSettings} object is created in the {@link Minecraft} instance. */ public void onPreInit() { - if (!BaritoneAutoTest.ENABLE_AUTO_TEST || !"true".equals(System.getenv("BARITONE_AUTO_TEST"))) { + if (!BaritoneAutoTest.ENABLE_AUTO_TEST) { return; } System.out.println("Optimizing Game Settings"); diff --git a/src/test/java/baritone/pathing/calc/AbstractNodeCostSearchTest.java b/src/test/java/baritone/pathing/calc/AbstractNodeCostSearchTest.java new file mode 100644 index 00000000..b3b22b54 --- /dev/null +++ b/src/test/java/baritone/pathing/calc/AbstractNodeCostSearchTest.java @@ -0,0 +1,49 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.pathing.calc; + +import org.junit.Test; + +//import static org.junit.Assert.*; + +public class AbstractNodeCostSearchTest { + @Test + public void testPosHash() { + + System.out.println(Long.toBinaryString(mix(0, 0))); + System.out.println(Long.toBinaryString(mix(0, 1))); + System.out.println(Long.toBinaryString(mix(1, 0))); + System.out.println(Long.toBinaryString(mix(0, -1))); + System.out.println(Long.toBinaryString(mix(-1, 0))); + System.out.println(Long.toBinaryString(mix(1, -1))); + System.out.println(Long.toBinaryString(mix(-1, 1))); + + System.out.println(Long.toBinaryString(mix(-1, - 1))); + System.out.println(Long.toBinaryString(mix(-30000000, -1))); + System.out.println(Long.toBinaryString(mix(-15000000, -1))); + System.out.println(Long.toBinaryString(mix(30000000, -1))); + System.out.println(Long.toBinaryString(mix(15000000, -1))); + System.out.println(Long.toBinaryString(mix(0, -1))); + + } + + public long mix(int x, int z) { + long xzmask = (1L << 26L) - 1; // 38 zero bits, 26 one bits + return (((long) x) & xzmask) | ((((long) z) & xzmask) << 26); + } +} \ No newline at end of file