diff --git a/src/main/java/baritone/bot/Settings.java b/src/main/java/baritone/bot/Settings.java index d226763c..bb12fcba 100644 --- a/src/main/java/baritone/bot/Settings.java +++ b/src/main/java/baritone/bot/Settings.java @@ -51,7 +51,7 @@ public class Settings { )); public Setting renderGoal = new Setting<>(true); public Setting pathingMaxChunkBorderFetch = new Setting<>(50); - public Setting backtrackCostFavoringCoefficient = new Setting<>(0.9); + public Setting backtrackCostFavoringCoefficient = new Setting<>(0.9); // see issue #18 public Setting pathRenderLineWidth = new Setting<>(5F); public Setting goalRenderLineWidth = new Setting<>(3F); diff --git a/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java index 9d308162..2fab0358 100644 --- a/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java @@ -151,6 +151,7 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " calculated implausible cost " + actionCost); } if (favoring && favored.contains(dest)) { + // see issue #18 actionCost *= favorCoeff; } PathNode neighbor = getNodeAtPosition(dest); diff --git a/src/main/java/baritone/bot/pathing/movement/ActionCosts.java b/src/main/java/baritone/bot/pathing/movement/ActionCosts.java index d21b9384..ee0eb907 100644 --- a/src/main/java/baritone/bot/pathing/movement/ActionCosts.java +++ b/src/main/java/baritone/bot/pathing/movement/ActionCosts.java @@ -24,7 +24,7 @@ public interface ActionCosts extends ActionCostsButOnlyTheOnesThatMakeMickeyDieI */ double WALK_ONE_BLOCK_COST = 20 / 4.317; // 4.633 double WALK_ONE_IN_WATER_COST = 20 / 2.2; - double JUMP_ONE_BLOCK_COST = 5.72854;//see below calculation for fall. 1.25 blocks + double WALK_ONE_OVER_SOUL_SAND_COST = WALK_ONE_IN_WATER_COST; // TODO issue #7 double LADDER_UP_ONE_COST = 20 / 2.35; double LADDER_DOWN_ONE_COST = 20 / 3.0; double SNEAK_ONE_BLOCK_COST = 20 / 1.3; diff --git a/src/main/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.java b/src/main/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.java index 2241bf8c..e69dce85 100644 --- a/src/main/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.java +++ b/src/main/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.java @@ -20,6 +20,23 @@ package baritone.bot.pathing.movement; public interface ActionCostsButOnlyTheOnesThatMakeMickeyDieInside { double[] FALL_N_BLOCKS_COST = generateFallNBlocksCost(); + double FALL_1_25_BLOCKS_COST = distanceToTicks(1.25); + double FALL_0_25_BLOCKS_COST = distanceToTicks(0.25); + /** + * When you hit space, you get enough upward velocity to go 1.25 blocks + * Then, you fall the remaining 0.25 to get on the surface, on block higher. + * Since parabolas are symmetric, the amount of time it takes to ascend up from 1 to 1.25 + * will be the same amount of time that it takes to fall back down from 1.25 to 1. + * And the same applies to the overall shape, if it takes X ticks to fall back down 1.25 blocks, + * it will take X ticks to reach the peak of your 1.25 block leap. + * Therefore, the part of your jump from y=0 to y=1.25 takes distanceToTicks(1.25) ticks, + * and the sub-part from y=1 to y=1.25 takes distanceToTicks(0.25) ticks. + * Therefore, the other sub-part, from y=0 to y-1, takes distanceToTicks(1.25)-distanceToTicks(0.25) ticks. + * That's why JUMP_ONE_BLOCK_COST = FALL_1_25_BLOCKS_COST - FALL_0_25_BLOCKS_COST + */ + double JUMP_ONE_BLOCK_COST = FALL_1_25_BLOCKS_COST - FALL_0_25_BLOCKS_COST; + + static double[] generateFallNBlocksCost() { double[] costs = new double[257]; for (int i = 0; i < 257; i++) { diff --git a/src/main/java/baritone/bot/pathing/movement/MovementHelper.java b/src/main/java/baritone/bot/pathing/movement/MovementHelper.java index 39fedd1a..65a645ff 100644 --- a/src/main/java/baritone/bot/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/bot/pathing/movement/MovementHelper.java @@ -43,8 +43,8 @@ import java.util.Optional; */ public interface MovementHelper extends ActionCosts, Helper { - static boolean avoidBreaking(BlockPos pos) { - Block b = BlockStateInterface.getBlock(pos); + static boolean avoidBreaking(BlockPos pos, IBlockState state) { + Block b = state.getBlock(); BlockPos below = new BlockPos(pos.getX(), pos.getY() - 1, pos.getZ()); return Blocks.ICE.equals(b) // ice becomes water, and water can mess up the path || b instanceof BlockSilverfish @@ -63,8 +63,7 @@ public interface MovementHelper extends ActionCosts, Helper { * @return */ static boolean canWalkThrough(BlockPos pos) { - IBlockState state = BlockStateInterface.get(pos); - return canWalkThrough(pos, state); + return canWalkThrough(pos, BlockStateInterface.get(pos)); } static boolean canWalkThrough(BlockPos pos, IBlockState state) { @@ -126,21 +125,20 @@ public interface MovementHelper extends ActionCosts, Helper { || block instanceof BlockCactus || block instanceof BlockFire || block instanceof BlockEndPortal - || block instanceof BlockWeb - || block instanceof BlockMagma; + || block instanceof BlockWeb; } /** * Can I walk on this block without anything weird happening like me falling * through? Includes water because we know that we automatically jump on - * lava + * water * * @return */ static boolean canWalkOn(BlockPos pos, IBlockState state) { Block block = state.getBlock(); - if (block instanceof BlockLadder || block instanceof BlockVine) { + if (block instanceof BlockLadder || block instanceof BlockVine) { // TODO reconsider this return true; } if (block instanceof BlockAir) { @@ -149,15 +147,14 @@ public interface MovementHelper extends ActionCosts, Helper { if (BlockStateInterface.isWater(block)) { return BlockStateInterface.isWater(pos.up()); // You can only walk on water if there is water above it } - if (block.equals(Blocks.MAGMA)) { + if (Blocks.MAGMA.equals(block)) { return false; } return state.isBlockNormalCube() && !BlockStateInterface.isLava(block); } static boolean canWalkOn(BlockPos pos) { - IBlockState state = BlockStateInterface.get(pos); - return canWalkOn(pos, state); + return canWalkOn(pos, BlockStateInterface.get(pos)); } static boolean canFall(BlockPos pos) { @@ -166,12 +163,16 @@ public interface MovementHelper extends ActionCosts, Helper { static double getMiningDurationTicks(ToolSet ts, BlockPos position) { IBlockState state = BlockStateInterface.get(position); + return getMiningDurationTicks(ts, position, state); + } + + static double getMiningDurationTicks(ToolSet ts, BlockPos position, IBlockState state) { Block block = state.getBlock(); - if (!block.equals(Blocks.AIR) && !canWalkThrough(position)) { + if (!block.equals(Blocks.AIR) && !canWalkThrough(position, state)) { if (!Baritone.settings().allowBreak.get()) { return COST_INF; } - if (avoidBreaking(position)) { + if (avoidBreaking(position, state)) { return COST_INF; } double m = Blocks.CRAFTING_TABLE.equals(block) ? 10 : 1; diff --git a/src/main/java/baritone/bot/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/bot/pathing/movement/movements/MovementAscend.java index 7a8d5176..51e9db3c 100644 --- a/src/main/java/baritone/bot/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/bot/pathing/movement/movements/MovementAscend.java @@ -26,6 +26,7 @@ import baritone.bot.pathing.movement.MovementState.MovementStatus; import baritone.bot.utils.BlockStateInterface; import baritone.bot.utils.InputOverrideHandler; import baritone.bot.utils.Utils; +import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -82,14 +83,29 @@ public class MovementAscend extends Movement { return COST_INF; } if (BlockStateInterface.get(src.up(3)).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us - return COST_INF; + // HOWEVER, we assume that we're standing in the start position + // that means that src and src.up(1) are both air + // maybe they aren't now, but they will be by the time this starts + Block srcUp = BlockStateInterface.get(src.up(1)).getBlock(); + Block srcUp2 = BlockStateInterface.get(src.up(2)).getBlock(); + if (!(srcUp instanceof BlockFalling) || !(srcUp2 instanceof BlockFalling)) { + // if both of those are BlockFalling, that means that by standing on src + // (the presupposition of this Movement) + // we have necessarily already cleared the entire BlockFalling stack + // on top of our head + + // but if either of them aren't BlockFalling, that means we're still in suffocation danger + // so don't do it + return COST_INF; + } } - double halfWalk = WALK_ONE_BLOCK_COST / 2; + // TODO maybe change behavior if src.down() is soul sand? + double walk = WALK_ONE_BLOCK_COST; if (toPlace.getBlock().equals(Blocks.SOUL_SAND)) { - halfWalk *= WALK_ONE_IN_WATER_COST / WALK_ONE_BLOCK_COST; + walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST; } - // we walk half the block to get to the edge, then we walk the other half while simultaneously jumping (math.max because of how it's in parallel) - return halfWalk + Math.max(JUMP_ONE_BLOCK_COST, halfWalk) + getTotalHardnessOfBlocksToBreak(context.getToolSet()); + // we hit space immediately on entering this action + return Math.max(JUMP_ONE_BLOCK_COST, walk) + getTotalHardnessOfBlocksToBreak(context.getToolSet()); } @Override diff --git a/src/main/java/baritone/bot/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/bot/pathing/movement/movements/MovementDescend.java index 7be59858..202e9f6f 100644 --- a/src/main/java/baritone/bot/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/bot/pathing/movement/movements/MovementDescend.java @@ -48,7 +48,8 @@ public class MovementDescend extends Movement { // we walk half the block plus 0.3 to get to the edge, then we walk the other 0.2 while simultaneously falling (math.max because of how it's in parallel) double walk = WALK_OFF_BLOCK_COST; if (BlockStateInterface.get(src.down()).getBlock().equals(Blocks.SOUL_SAND)) { - walk *= WALK_ONE_IN_WATER_COST / WALK_ONE_BLOCK_COST; + // use this ratio to apply the soul sand speed penalty to our 0.8 block distance + walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST; } return walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST) + getTotalHardnessOfBlocksToBreak(context.getToolSet()); } diff --git a/src/main/java/baritone/bot/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/bot/pathing/movement/movements/MovementDiagonal.java index c7a045f2..c0d69b2e 100644 --- a/src/main/java/baritone/bot/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/bot/pathing/movement/movements/MovementDiagonal.java @@ -78,11 +78,15 @@ public class MovementDiagonal extends Movement { return COST_INF; } double multiplier = WALK_ONE_BLOCK_COST; + + // for either possible soul sand, that affects half of our walking if (destWalkOn.getBlock().equals(Blocks.SOUL_SAND)) { - multiplier *= WALK_ONE_IN_WATER_COST / WALK_ONE_BLOCK_COST; - } else if (BlockStateInterface.get(src.down()).getBlock().equals(Blocks.SOUL_SAND)) { - multiplier *= WALK_ONE_IN_WATER_COST / WALK_ONE_BLOCK_COST; + multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } + if (BlockStateInterface.get(src.down()).getBlock().equals(Blocks.SOUL_SAND)) { + multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; + } + if (BlockStateInterface.get(positionsToBreak[2].down()).getBlock() instanceof BlockMagma) { return COST_INF; } @@ -110,12 +114,15 @@ public class MovementDiagonal extends Movement { return COST_INF; } } + if (BlockStateInterface.isWater(src) || BlockStateInterface.isWater(dest)) { + // ignore previous multiplier + // whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water + // not even touching the blocks below + multiplier = WALK_ONE_IN_WATER_COST; + } if (optionA != 0 || optionB != 0) { multiplier *= SQRT_2 - 0.001; // TODO tune } - if (BlockStateInterface.isWater(src) || BlockStateInterface.isWater(dest)) { - multiplier *= WALK_ONE_IN_WATER_COST / WALK_ONE_BLOCK_COST; - } if (multiplier == WALK_ONE_BLOCK_COST && context.canSprint()) { // if we aren't edging around anything, and we aren't in water or soul sand // we can sprint =D diff --git a/src/main/java/baritone/bot/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/bot/pathing/movement/movements/MovementDownward.java index 20aad6df..b8145e94 100644 --- a/src/main/java/baritone/bot/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/bot/pathing/movement/movements/MovementDownward.java @@ -25,6 +25,7 @@ import baritone.bot.utils.BlockStateInterface; import net.minecraft.block.Block; import net.minecraft.block.BlockLadder; import net.minecraft.block.BlockVine; +import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; public class MovementDownward extends Movement { @@ -40,12 +41,13 @@ public class MovementDownward extends Movement { if (!MovementHelper.canWalkOn(dest.down())) { return COST_INF; } - Block td = BlockStateInterface.get(dest).getBlock(); + IBlockState d = BlockStateInterface.get(dest); + Block td = d.getBlock(); boolean ladder = td instanceof BlockLadder || td instanceof BlockVine; if (ladder) { return LADDER_DOWN_ONE_COST; } else { - return FALL_N_BLOCKS_COST[1] + getTotalHardnessOfBlocksToBreak(context.getToolSet()); + return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context.getToolSet(), dest, d); } } diff --git a/src/main/java/baritone/bot/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/bot/pathing/movement/movements/MovementFall.java index a82d874d..4b733284 100644 --- a/src/main/java/baritone/bot/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/bot/pathing/movement/movements/MovementFall.java @@ -49,6 +49,9 @@ public class MovementFall extends Movement { } double placeBucketCost = 0.0; if (!BlockStateInterface.isWater(dest) && src.getY() - dest.getY() > 3) { + if (!context.hasWaterBucket()) { + return COST_INF; + } placeBucketCost = ActionCosts.PLACE_ONE_BLOCK_COST; } double frontTwo = MovementHelper.getMiningDurationTicks(context.getToolSet(), positionsToBreak[0]) + MovementHelper.getMiningDurationTicks(context.getToolSet(), positionsToBreak[1]); @@ -56,6 +59,11 @@ public class MovementFall extends Movement { return COST_INF; } for (int i = 2; i < positionsToBreak.length; i++) { + // TODO is this the right check here? + // miningDurationTicks is all right, but shouldn't it be canWalkThrough instead? + // lilypads (i think?) are 0 ticks to mine, but they definitely cause fall damage + // same thing for falling through water... we can't actually do that + // and falling through signs is possible, but they do have a mining duration, right? if (MovementHelper.getMiningDurationTicks(context.getToolSet(), positionsToBreak[i]) > 0) { //can't break while falling return COST_INF; diff --git a/src/main/java/baritone/bot/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/bot/pathing/movement/movements/MovementTraverse.java index cd8d56d5..b92a021c 100644 --- a/src/main/java/baritone/bot/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/bot/pathing/movement/movements/MovementTraverse.java @@ -72,9 +72,16 @@ public class MovementTraverse extends Movement { IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); IBlockState destOn = BlockStateInterface.get(positionsToPlace[0]); if (MovementHelper.canWalkOn(positionsToPlace[0], destOn)) {//this is a walk, not a bridge - double WC = BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock()) ? WALK_ONE_IN_WATER_COST : WALK_ONE_BLOCK_COST; - if (destOn.getBlock().equals(Blocks.SOUL_SAND)) { - WC *= WALK_ONE_IN_WATER_COST / WALK_ONE_BLOCK_COST; + double WC = WALK_ONE_BLOCK_COST; + if (BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock())) { + WC = WALK_ONE_IN_WATER_COST; + } else { + if (Blocks.SOUL_SAND.equals(destOn.getBlock())) { + WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; + } + if (Blocks.SOUL_SAND.equals(BlockStateInterface.get(src.down()).getBlock())) { + WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; + } } if (MovementHelper.canWalkThrough(positionsToBreak[0]) && MovementHelper.canWalkThrough(positionsToBreak[1])) { if (WC == WALK_ONE_BLOCK_COST && context.canSprint()) { @@ -89,9 +96,8 @@ public class MovementTraverse extends Movement { //Out.log("Can't walk through " + blocksToBreak[0] + " (hardness" + hardness1 + ") or " + blocksToBreak[1] + " (hardness " + hardness2 + ")"); return WC + getTotalHardnessOfBlocksToBreak(context.getToolSet()); } else {//this is a bridge, so we need to place a block - //return 1000000; - Block f = BlockStateInterface.get(src.down()).getBlock(); - if (f instanceof BlockLadder || f instanceof BlockVine) { + Block srcDown = BlockStateInterface.get(src.down()).getBlock(); + if (srcDown instanceof BlockLadder || srcDown instanceof BlockVine) { return COST_INF; } IBlockState pp0 = BlockStateInterface.get(positionsToPlace[0]); diff --git a/src/main/java/baritone/bot/utils/InputOverrideHandler.java b/src/main/java/baritone/bot/utils/InputOverrideHandler.java index 99e59e3f..2e1f15a9 100755 --- a/src/main/java/baritone/bot/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/bot/utils/InputOverrideHandler.java @@ -151,7 +151,12 @@ public final class InputOverrideHandler implements Helper { /** * The sneak input */ - SNEAK(mc.gameSettings.keyBindSneak); + SNEAK(mc.gameSettings.keyBindSneak), + + /** + * The sprint input + */ + SPRINT(mc.gameSettings.keyBindSprint); /** * The actual game {@link KeyBinding} being forced. diff --git a/src/test/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInsideTest.java b/src/test/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInsideTest.java index 3033bb69..817e5a58 100644 --- a/src/test/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInsideTest.java +++ b/src/test/java/baritone/bot/pathing/movement/ActionCostsButOnlyTheOnesThatMakeMickeyDieInsideTest.java @@ -19,8 +19,7 @@ package baritone.bot.pathing.movement; import org.junit.Test; -import static baritone.bot.pathing.movement.ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.FALL_N_BLOCKS_COST; -import static baritone.bot.pathing.movement.ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.velocity; +import static baritone.bot.pathing.movement.ActionCostsButOnlyTheOnesThatMakeMickeyDieInside.*; import static org.junit.Assert.assertEquals; public class ActionCostsButOnlyTheOnesThatMakeMickeyDieInsideTest { @@ -31,6 +30,9 @@ public class ActionCostsButOnlyTheOnesThatMakeMickeyDieInsideTest { double blocks = ticksToBlocks(FALL_N_BLOCKS_COST[i]); assertEquals(blocks, i, 0.000000000001); // If you add another 0 the test fails at i=217 LOL } + assertEquals(FALL_1_25_BLOCKS_COST, 6.2344, 0.00001); + assertEquals(FALL_0_25_BLOCKS_COST, 3.0710, 0.00001); + assertEquals(JUMP_ONE_BLOCK_COST, 3.1634, 0.00001); } public double ticksToBlocks(double ticks) {