diff --git a/src/main/java/baritone/builder/DependencyGraphScaffoldingOverlay.java b/src/main/java/baritone/builder/DependencyGraphScaffoldingOverlay.java index 62ec87306..1c357aea1 100644 --- a/src/main/java/baritone/builder/DependencyGraphScaffoldingOverlay.java +++ b/src/main/java/baritone/builder/DependencyGraphScaffoldingOverlay.java @@ -165,7 +165,7 @@ public class DependencyGraphScaffoldingOverlay { } } }); - if (Main.DEBUG) { + if (Main.SLOW_DEBUG) { sanityCheck(); } } @@ -254,7 +254,7 @@ public class DependencyGraphScaffoldingOverlay { } } // can't run sanityCheck after each mergeInto because it could leave a 2-way connection between components as an intermediary state while collapsing - if (Main.DEBUG) { + if (Main.SLOW_DEBUG) { sanityCheck(); } return; @@ -294,7 +294,7 @@ public class DependencyGraphScaffoldingOverlay { CollapsedDependencyGraphComponent component = addComponent(); component.positions.add(pos); posToComponent.put(pos, component); - if (Main.DEBUG) { + if (Main.SLOW_DEBUG) { sanityCheck(); } //System.out.println("Incremental " + pos); @@ -309,7 +309,7 @@ public class DependencyGraphScaffoldingOverlay { incrementalEdgeAddition(face.offset(pos), pos); } } - if (Main.DEBUG) { + if (Main.SLOW_DEBUG) { sanityCheck(); } } diff --git a/src/main/java/baritone/builder/Face.java b/src/main/java/baritone/builder/Face.java index 1866ca7da..f7eb7c585 100644 --- a/src/main/java/baritone/builder/Face.java +++ b/src/main/java/baritone/builder/Face.java @@ -37,7 +37,7 @@ public enum Face { public final int x = toMC().getXOffset(); public final int y = toMC().getYOffset(); public final int z = toMC().getZOffset(); - public final long offset = BetterBlockPos.toLong(x, y, z); + public final long offset = BetterBlockPos.toLong(x, y, z); // both the previous three lines (avoidably) and this one (unavoidably due to BlockPos superclass of BetterBlockPos) mess up test timing / profiling because it calls the of both EnumFacing and BlockPos which does some Log4j bs lol public final int[] vec = new int[]{x, y, z}; public final boolean vertical = y != 0; public final int horizontalIndex = x & 1 | (x | z) & 2; diff --git a/src/main/java/baritone/builder/FakeStates.java b/src/main/java/baritone/builder/FakeStates.java index 4324c4321..f210d8537 100644 --- a/src/main/java/baritone/builder/FakeStates.java +++ b/src/main/java/baritone/builder/FakeStates.java @@ -17,6 +17,12 @@ package baritone.builder; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + /** * There will be a BlockStateCachedData for every IBlockState in the game, yes. *

@@ -29,6 +35,8 @@ package baritone.builder; public class FakeStates { // the three aformentioned placeholders for runtime public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(1).canPlaceAgainstMe()); + // need a second solid block so that "== FakeStates.SCAFFOLDING" doesn't get tricked + public static final BlockStateCachedData SOLID = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(1).canPlaceAgainstMe()); public static final BlockStateCachedData AIR = new BlockStateCachedData(new BlockStateCachedDataBuilder().setAir()); public static final BlockStateCachedData OUT_OF_BOUNDS = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).collisionHeight(1)); @@ -43,5 +51,38 @@ public class FakeStates { BY_HEIGHT[0] = AIR; } + private static List PROBABLE_BLOCKS = null; + + private static List getProbableBlocks() { + if (PROBABLE_BLOCKS == null) { + //long a = System.currentTimeMillis(); + Random rand = new Random(5021); + PROBABLE_BLOCKS = IntStream.range(0, 10000).mapToObj($ -> { + List ret = new ArrayList<>(SCAFFOLDING.placeMe); + ret.removeIf($$ -> rand.nextInt(10) < 2); + BlockStateCachedDataBuilder builder = new BlockStateCachedDataBuilder() { + @Override + public List howCanIBePlaced() { + return ret; + } + }; + if (ret.isEmpty()) { + builder.placementLogicNotImplementedYet(); + } + return new BlockStateCachedData(builder + .fullyWalkableTop() + .collisionHeight(1) + .canPlaceAgainstMe() + .collidesWithPlayer(true)); + }).collect(Collectors.toList()); + //System.out.println("Took " + (System.currentTimeMillis() - a)); + } + return PROBABLE_BLOCKS; + } + + public static BlockStateCachedData probablyCanBePlaced(Random rand) { + return getProbableBlocks().get(rand.nextInt(getProbableBlocks().size())); + } + // probably more will go here such as for making sure that slabs and stairs work right (like there'll be a fake slab and a fake stair i guess?) } diff --git a/src/main/java/baritone/builder/Main.java b/src/main/java/baritone/builder/Main.java index 385f1c651..43bdac16d 100644 --- a/src/main/java/baritone/builder/Main.java +++ b/src/main/java/baritone/builder/Main.java @@ -94,74 +94,6 @@ public class Main { { System.out.println(BetterBlockPos.fromLong(BetterBlockPos.toLong(150, 150, 150))); } - for (int i = 0; i < 0; i++) { - if (!fakePlacementForPerformanceTesting) { - // when facePlacement is off, then all the blocks in this test can be placed bidirectionally (since they're dirt) - // this causes the test to not actually be that interesting because there are no possible loops or diamond shapes in the graph - // for example: if you turn off fakePlacement, then this test will no longer catch if pathExists in DGSO only returned the first path (because, no more diamond shapes, so no more need to consider multiple paths from src to dst) - throw new IllegalStateException("should be true"); - } - long aaa = System.currentTimeMillis(); - int[][][] test = new int[64][64][64]; - int based = Block.BLOCK_STATE_IDS.get(Blocks.DIRT.getDefaultState()); - int numRealBlocks = 0; - for (int x = 0; x < test.length; x++) { - for (int y = 0; y < test[0].length; y++) { - for (int z = 0; z < test[0][0].length; z++) { - //if ((x + y + z) % 2 == 0) { - if (RAND.nextInt(10) < 2) { - test[x][y][z] = based; - numRealBlocks++; - } - } - - } - } - /*for (int[][] arr : test) { - for (int[] arr2 : arr) { - Arrays.fill(arr2, based); - } - }*/ - PackedBlockStateCuboid states = new PackedBlockStateCuboid(test, DATA); - PlaceOrderDependencyGraph graph = new PlaceOrderDependencyGraph(states); - long aa = System.currentTimeMillis(); - /*DependencyGraphAnalyzer.prevalidate(graph); - for (int i = 0; i < 1; i++) { - long a = System.currentTimeMillis(); - DependencyGraphAnalyzer.prevalidateExternalToInteriorSearch(graph); - long b = System.currentTimeMillis(); - System.out.println((b - a) + "ms"); - Thread.sleep(500); - System.gc(); - Thread.sleep(500); - }*/ - DependencyGraphScaffoldingOverlay scaffolding = new DependencyGraphScaffoldingOverlay(graph); - long a = System.currentTimeMillis(); - System.out.println(scaffolding.getCollapsedGraph().getComponents().size()); - int goal = numRealBlocks + 200000; - while (numRealBlocks < goal) { - //System.out.println(numRealBlocks + " " + scaffolding.getCollapsedGraph().getComponents().size()); - int x = RAND.nextInt(((CuboidBounds) scaffolding.bounds()).sizeX); - int y = RAND.nextInt(((CuboidBounds) scaffolding.bounds()).sizeY); - int z = RAND.nextInt(((CuboidBounds) scaffolding.bounds()).sizeZ); - long pos = BetterBlockPos.toLong(x, y, z); - if (scaffolding.air(pos)) { - //System.out.println("Setting to scaffolding " + BetterBlockPos.fromLong(pos) + " " + pos); - scaffolding.enable(pos); - numRealBlocks++; - if (numRealBlocks % 10000 == 0) { - System.out.println(numRealBlocks + " " + scaffolding.getCollapsedGraph().getComponents().size()); - scaffolding.recheckEntireCollapsedGraph(); - } - } - } - System.out.println(scaffolding.getCollapsedGraph().getComponents().size()); - System.out.println("Done " + (System.currentTimeMillis() - a) + "ms after " + (a - aa) + "ms after " + (aa - aaa) + "ms"); - Thread.sleep(500); - System.gc(); - Thread.sleep(500); - //scaffolding.enable(0); - } for (int i = 0; i < 0; i++) { Stream.of(new Object()) .flatMap(ignored -> IntStream.range(0, 100).boxed()) diff --git a/src/main/java/baritone/builder/PackedBlockStateCuboid.java b/src/main/java/baritone/builder/PackedBlockStateCuboid.java index 1eb5e0221..1e13e75db 100644 --- a/src/main/java/baritone/builder/PackedBlockStateCuboid.java +++ b/src/main/java/baritone/builder/PackedBlockStateCuboid.java @@ -35,8 +35,17 @@ public class PackedBlockStateCuboid { genScaffoldVariant(); } + public PackedBlockStateCuboid(BlockStateCachedData[][][] blockStates) { + this(blockStates.length, blockStates[0].length, blockStates[0][0].length); + bounds.forEach((x, y, z) -> states[bounds.toIndex(x, y, z)] = blockStates[x][y][z]); + genScaffoldVariant(); + } + private void genScaffoldVariant() { for (int i = 0; i < states.length; i++) { + if (PlaceOrderDependencyGraph.treatedAsScaffolding(states[i])) { + throw new IllegalStateException("including FakeStates.SCAFFOLDING will confuse the place order dependency graph. use an alternate block like FakeStates.SOLID"); + } statesWithScaffolding[i] = states[i].isAir ? FakeStates.SCAFFOLDING : states[i]; } } diff --git a/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java b/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java index 381b69e1f..22fc0a413 100644 --- a/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java +++ b/src/main/java/baritone/builder/PlaceOrderDependencyGraph.java @@ -85,7 +85,7 @@ public class PlaceOrderDependencyGraph { } public boolean airTreatedAsScaffolding(long pos) { - return data(pos) == FakeStates.SCAFFOLDING; + return treatedAsScaffolding(data(pos)); } private boolean inRange(long pos) { @@ -95,4 +95,8 @@ public class PlaceOrderDependencyGraph { public Bounds bounds() { return states.bounds; } + + public static boolean treatedAsScaffolding(BlockStateCachedData data) { + return data == FakeStates.SCAFFOLDING; + } } diff --git a/src/main/java/baritone/builder/TarjansAlgorithm.java b/src/main/java/baritone/builder/TarjansAlgorithm.java index 4acacfb2c..0353942e5 100644 --- a/src/main/java/baritone/builder/TarjansAlgorithm.java +++ b/src/main/java/baritone/builder/TarjansAlgorithm.java @@ -47,7 +47,7 @@ public class TarjansAlgorithm { this.tarjanCallStack = new ArrayDeque<>(); } - private LongSet sanityCheckResultFrom(long start) { + private static LongSet sanityCheckResultFrom(DependencyGraphScaffoldingOverlay graph, long start) { if (graph.air(start)) { throw new IllegalStateException(); } @@ -69,7 +69,7 @@ public class TarjansAlgorithm { return ret; } - private void sanityCheck() { + public static void sanityCheckResult(DependencyGraphScaffoldingOverlay graph, TarjansResult result) { // this is a much slower (O(n^2) at least instead of O(n)) implementation of finding strongly connected components Int2ObjectOpenHashMap checkedCids = new Int2ObjectOpenHashMap<>(); LongSet claimedAlready = new LongOpenHashSet(); @@ -77,7 +77,7 @@ public class TarjansAlgorithm { int cid = result.getComponent(pos); LongSet componentShouldBe = checkedCids.get(cid); if (componentShouldBe == null) { - componentShouldBe = sanityCheckResultFrom(pos); + componentShouldBe = sanityCheckResultFrom(graph, pos); checkedCids.put(cid, componentShouldBe); LongIterator it = componentShouldBe.iterator(); while (it.hasNext()) { @@ -96,7 +96,7 @@ public class TarjansAlgorithm { TarjansAlgorithm algo = new TarjansAlgorithm(overlayedGraph); algo.run(); if (Main.VERY_SLOW_DEBUG) { - algo.sanityCheck(); + sanityCheckResult(overlayedGraph, algo.result); } return algo.result; } diff --git a/src/main/java/baritone/builder/mc/DebugStates.java b/src/main/java/baritone/builder/mc/DebugStates.java index e18501be6..45de6fd2e 100644 --- a/src/main/java/baritone/builder/mc/DebugStates.java +++ b/src/main/java/baritone/builder/mc/DebugStates.java @@ -86,7 +86,7 @@ public class DebugStates { } } - private static String toString(BlockStateCachedData data) { + public static String toString(BlockStateCachedData data) { if (data == null) { return "UNKNOWN"; } diff --git a/src/test/java/baritone/builder/DependencyGraphScaffoldingOverlayTest.java b/src/test/java/baritone/builder/DependencyGraphScaffoldingOverlayTest.java new file mode 100644 index 000000000..33aadf299 --- /dev/null +++ b/src/test/java/baritone/builder/DependencyGraphScaffoldingOverlayTest.java @@ -0,0 +1,97 @@ +/* + * 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.builder; + +import baritone.api.utils.BetterBlockPos; +import org.junit.Test; + +import java.util.Random; + +import static org.junit.Assert.assertEquals; + +public class DependencyGraphScaffoldingOverlayTest { + @Test + public void testLarge() throws InterruptedException { + Random RAND = new Random(5021); + for (int i = 0; i < 1; i++) { + long aaa = System.currentTimeMillis(); + //int[][][] test = new int[64][64][64]; + BlockStateCachedData[][][] test = new BlockStateCachedData[64][64][64]; + int numRealBlocks = 0; + for (int x = 0; x < test.length; x++) { + for (int y = 0; y < test[0].length; y++) { + for (int z = 0; z < test[0][0].length; z++) { + //if ((x + y + z) % 2 == 0) { + if (RAND.nextInt(10) < 2) { + test[x][y][z] = FakeStates.probablyCanBePlaced(RAND); + numRealBlocks++; + } else { + test[x][y][z] = FakeStates.AIR; + } + } + + } + } + /*for (int[][] arr : test) { + for (int[] arr2 : arr) { + Arrays.fill(arr2, based); + } + }*/ + PackedBlockStateCuboid states = new PackedBlockStateCuboid(test); + PlaceOrderDependencyGraph graph = new PlaceOrderDependencyGraph(states); + long aa = System.currentTimeMillis(); + /*DependencyGraphAnalyzer.prevalidate(graph); + for (int i = 0; i < 1; i++) { + long a = System.currentTimeMillis(); + DependencyGraphAnalyzer.prevalidateExternalToInteriorSearch(graph); + long b = System.currentTimeMillis(); + System.out.println((b - a) + "ms"); + Thread.sleep(500); + System.gc(); + Thread.sleep(500); + }*/ + DependencyGraphScaffoldingOverlay scaffolding = new DependencyGraphScaffoldingOverlay(graph); + long a = System.currentTimeMillis(); + System.out.println(scaffolding.getCollapsedGraph().getComponents().size()); + int goal = numRealBlocks + 200000; + while (numRealBlocks < goal) { + //System.out.println(numRealBlocks + " " + scaffolding.getCollapsedGraph().getComponents().size()); + int x = RAND.nextInt(((CuboidBounds) scaffolding.bounds()).sizeX); + int y = RAND.nextInt(((CuboidBounds) scaffolding.bounds()).sizeY); + int z = RAND.nextInt(((CuboidBounds) scaffolding.bounds()).sizeZ); + long pos = BetterBlockPos.toLong(x, y, z); + if (scaffolding.air(pos)) { + //System.out.println("Setting to scaffolding " + BetterBlockPos.fromLong(pos) + " " + pos); + scaffolding.enable(pos); + numRealBlocks++; + if (numRealBlocks % 10000 == 0) { + System.out.println(numRealBlocks + " " + scaffolding.getCollapsedGraph().getComponents().size()); + scaffolding.recheckEntireCollapsedGraph(); + } + } + } + System.out.println(scaffolding.getCollapsedGraph().getComponents().size()); + System.out.println("Done " + (System.currentTimeMillis() - a) + "ms after " + (a - aa) + "ms after " + (aa - aaa) + "ms"); + assertEquals(238, scaffolding.getCollapsedGraph().getComponents().size()); + /*Thread.sleep(500); + System.gc(); + Thread.sleep(500);*/ + //scaffolding.enable(0); + } + } +} diff --git a/src/test/java/baritone/builder/FaceTest.java b/src/test/java/baritone/builder/FaceTest.java index 5b619311a..1b1f0b751 100644 --- a/src/test/java/baritone/builder/FaceTest.java +++ b/src/test/java/baritone/builder/FaceTest.java @@ -17,6 +17,7 @@ package baritone.builder; +import baritone.api.utils.BetterBlockPos; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -29,4 +30,19 @@ public class FaceTest { assertEquals(i, (int) Face.OPTS.get(i).map(face -> face.index).orElse(Face.NUM_FACES)); } } + + @Test + public void ensureValid() { + for (Face face : Face.VALUES) { + assertEquals(face.x, face.toMC().getXOffset()); + assertEquals(face.y, face.toMC().getYOffset()); + assertEquals(face.z, face.toMC().getZOffset()); + } + assertEquals(Face.UP.offset, BetterBlockPos.toLong(0, 1, 0)); + assertEquals(Face.DOWN.offset, BetterBlockPos.toLong(0, -1, 0)); + assertEquals(Face.NORTH.offset, BetterBlockPos.toLong(0, 0, -1)); + assertEquals(Face.SOUTH.offset, BetterBlockPos.toLong(0, 0, 1)); + assertEquals(Face.EAST.offset, BetterBlockPos.toLong(1, 0, 0)); + assertEquals(Face.WEST.offset, BetterBlockPos.toLong(-1, 0, 0)); + } } diff --git a/src/test/java/baritone/builder/TarjansAlgorithmTest.java b/src/test/java/baritone/builder/TarjansAlgorithmTest.java index fa82e1225..c51be98e3 100644 --- a/src/test/java/baritone/builder/TarjansAlgorithmTest.java +++ b/src/test/java/baritone/builder/TarjansAlgorithmTest.java @@ -19,9 +19,32 @@ package baritone.builder; import org.junit.Test; +import java.util.Random; + public class TarjansAlgorithmTest { @Test public void test() { // the correctness test is already in there just gotta ask for it + Random RAND = new Random(5021); + for (int i = 0; i < 100; i++) { + BlockStateCachedData[][][] test = new BlockStateCachedData[20][20][20]; + for (int x = 0; x < test.length; x++) { + for (int y = 0; y < test[0].length; y++) { + for (int z = 0; z < test[0][0].length; z++) { + if (RAND.nextInt(10) < 3) { + test[x][y][z] = FakeStates.probablyCanBePlaced(RAND); + } else { + test[x][y][z] = FakeStates.AIR; + } + } + + } + } + PackedBlockStateCuboid states = new PackedBlockStateCuboid(test); + PlaceOrderDependencyGraph graph = new PlaceOrderDependencyGraph(states); + DependencyGraphScaffoldingOverlay overlay = new DependencyGraphScaffoldingOverlay(graph); // this runs tarjan's twice, but the alternative ruins the abstraction layers too much :) + TarjansAlgorithm.TarjansResult result = TarjansAlgorithm.run(overlay); + TarjansAlgorithm.sanityCheckResult(overlay, result); + } } }