started on simultaneous dijkstra scaffolder, saving progress here for now

This commit is contained in:
Leijurv 2023-04-10 00:14:13 -07:00
parent f039a9cff8
commit 4bea9dfb42
No known key found for this signature in database
GPG Key ID: 44A3EA646EADAC6A
7 changed files with 312 additions and 86 deletions

View File

@ -196,7 +196,7 @@ public class DependencyGraphScaffoldingOverlay {
if (child == parent) {
throw new IllegalStateException();
}
if (child.positions.size() > parent.positions.size()) {
if (child.positions.size() > parent.positions.size() || (child.positions.size() == parent.positions.size() && child.id < parent.id)) {
return mergeInto(parent, child);
}
if (Main.DEBUG) {
@ -358,7 +358,7 @@ public class DependencyGraphScaffoldingOverlay {
if (!deleted()) {
return this;
}
return deletedInto.deletedIntoRecursive();
return deletedInto = deletedInto.deletedIntoRecursive();
}
public LongSet getPositions() {

View File

@ -76,7 +76,7 @@ public enum DijkstraScaffolder implements IScaffolderStrategy {
// any position in the initial frontier is clearly in the node map, but also any node that has already been considered
// this prevents useless cycling of equivalent paths
// this is okay because all paths are equivalent, so there is no possible way to find a better path (because currently it's a fixed value for horizontal / vertical movements)
if (existingNode.costSoFar > newCost && !root.getPositions().contains(node.pos)) { // initialization nodes will have costSoFar = 0 as a base case
if (existingNode.costSoFar > newCost) { // initialization nodes will have costSoFar = 0 as a base case
// note that obviously there is a loopback possibility: search one block north then one block south, you'll run into the same node again. that's fine - "costSoFar < newCost" doesn't mean anything
// same for diagonals: one block north then one block down, versus one block down then one block north. that's also fine - "costSoFar == newCost" doesn't mean anything
System.out.println(BetterBlockPos.fromLong(node.pos) + " to " + BetterBlockPos.fromLong(neighborPos) + " " + existingNode.costSoFar + " " + newCost + " " + root.getPositions().contains(node.pos) + " " + root.getPositions().contains(neighborPos) + " " + reconstructPathTo(node).stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()) + " " + reconstructPathTo(existingNode).stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
@ -112,7 +112,7 @@ public enum DijkstraScaffolder implements IScaffolderStrategy {
return path;
}
private static int edgeCost(Face face) {
public static int edgeCost(Face face) {
if (Main.STRICT_Y && face == Face.UP) {
throw new IllegalStateException();
}
@ -122,7 +122,7 @@ public enum DijkstraScaffolder implements IScaffolderStrategy {
if (face.y == 0) {
return 1;
}
return 1;
return 2;
}
private static class ScaffoldingSearchNode {

View File

@ -23,11 +23,8 @@ import baritone.builder.mc.DebugStates;
import baritone.builder.mc.VanillaBlockStateDataProvider;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import java.util.Random;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@ -344,56 +341,6 @@ Branchy took 124ms
System.out.println(SneakPosition.decode(SneakPosition.encode(BetterBlockPos.POST_ADDITION_MASK, sneak)));
}
}
{
int[][][] test = new int[8][8][8];
int dirt = Block.BLOCK_STATE_IDS.get(Blocks.DIRT.getDefaultState());
System.out.println("D " + dirt);
for (int x = 0; x < test.length; x++) {
for (int z = 0; z < test[0][0].length; z++) {
test[x][0][z] = dirt;
test[x][1][z] = dirt;
}
}
test[5][5][5] = dirt;
test[5][5][6] = dirt;
test[0][5][5] = dirt;
Consumer<DependencyGraphScaffoldingOverlay> debug = dgso -> {
for (int y = 0; y < test[0].length; y++) {
System.out.println("Layer " + y);
for (int x = 0; x < test.length; x++) {
for (int z = 0; z < test[0][0].length; z++) {
long pos = BetterBlockPos.toLong(x, y, z);
if (dgso.real(pos)) {
System.out.print(dgso.getCollapsedGraph().getComponentLocations().get(pos).deletedIntoRecursive());
} else {
System.out.print(" ");
}
}
System.out.println();
}
}
};
PackedBlockStateCuboid states = new PackedBlockStateCuboid(test, DATA);
PlaceOrderDependencyGraph graph = new PlaceOrderDependencyGraph(states);
System.out.println("N " + Face.NORTH.z);
System.out.println("S " + Face.SOUTH.z);
for (int z = 0; z < test[0][0].length; z++) {
//System.out.println(states.get(states.bounds.toIndex(0, 0, z)));
System.out.println(z + " " + graph.outgoingEdge(BetterBlockPos.toLong(0, 0, z), Face.NORTH) + " " + graph.outgoingEdge(BetterBlockPos.toLong(0, 0, z), Face.SOUTH));
}
DependencyGraphAnalyzer.prevalidate(graph);
DependencyGraphAnalyzer.prevalidateExternalToInteriorSearch(graph);
DependencyGraphScaffoldingOverlay scaffolding = new DependencyGraphScaffoldingOverlay(graph);
System.out.println("Hewwo");
scaffolding.getCollapsedGraph().getComponents().forEach((key, value) -> {
System.out.println(key);
System.out.println(value.getPositions().stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
});
System.out.println();
debug.accept(scaffolding);
Scaffolder.Output out = Scaffolder.run(graph, DijkstraScaffolder.INSTANCE);
debug.accept(out.secretInternalForTesting());
}
System.exit(0);
}
}

View File

@ -17,6 +17,8 @@
package baritone.builder;
import java.util.Arrays;
public class PackedBlockStateCuboid {
public final Bounds bounds;
@ -41,6 +43,14 @@ public class PackedBlockStateCuboid {
genScaffoldVariant();
}
public static void fillWithAir(BlockStateCachedData[][][] states) {
for (BlockStateCachedData[][] layer : states) {
for (BlockStateCachedData[] slice : layer) {
Arrays.fill(slice, FakeStates.AIR);
}
}
}
private void genScaffoldVariant() {
for (int i = 0; i < states.length; i++) {
if (PlaceOrderDependencyGraph.treatedAsScaffolding(states[i])) {

View File

@ -70,11 +70,7 @@ public class Scaffolder {
private List<CollapsedDependencyGraphComponent> calcRoots() {
// since the components form a DAG (because all strongly connected components, and therefore all cycles, have been collapsed)
// we can locate all root components by simply finding the ones with no incoming edges
return components
.values()
.stream()
.filter(component -> component.getIncoming().isEmpty())
.collect(Collectors.toCollection(ArrayList::new)); // ensure arraylist since we will be mutating the list
return components.values().stream().filter(component -> component.getIncoming().isEmpty()).collect(Collectors.toCollection(ArrayList::new)); // ensure arraylist since we will be mutating the list
}
private void loop() {
@ -96,31 +92,15 @@ public class Scaffolder {
if (root.getPositions().contains(path.get(0))) {
throw new IllegalStateException();
}
if (!componentLocations.containsKey(path.get(0))) {
throw new IllegalStateException();
}
for (int i = 1; i < path.size(); i++) {
if (!overlayGraph.hypotheticalScaffoldingIncomingEdge(path.get(i), Face.between(path.get(i), path.get(i - 1)))) {
throw new IllegalStateException();
}
}
enable(path.subList(1, path.size() - 1));
internalEnable(path);
return;
}
throw new IllegalStateException("unconnectable");
}
private void enable(LongList positions) {
positions.forEach(pos -> {
if (componentLocations.containsKey(pos)) {
throw new IllegalStateException();
}
});
System.out.println("Enabling " + positions.stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
private void internalEnable(LongList path) {
int cid = collapsedGraph.lastComponentID().getAsInt();
positions.forEach(overlayGraph::enable); // TODO more performant to enable in reverse order maybe?
applyScaffoldingConnection(overlayGraph, path);
int newCID = collapsedGraph.lastComponentID().getAsInt();
for (int i = cid + 1; i <= newCID; i++) {
if (components.get(i) != null && components.get(i).getIncoming().isEmpty()) {
@ -161,12 +141,40 @@ public class Scaffolder {
}
}
public static void applyScaffoldingConnection(DependencyGraphScaffoldingOverlay overlayGraph, LongList path) {
CollapsedDependencyGraph collapsedGraph = overlayGraph.getCollapsedGraph();
Long2ObjectMap<CollapsedDependencyGraphComponent> componentLocations = collapsedGraph.getComponentLocations();
if (!componentLocations.containsKey(path.getLong(0))) {
throw new IllegalStateException();
}
if (!componentLocations.containsKey(path.getLong(path.size() - 1))) {
throw new IllegalStateException();
}
if (componentLocations.get(path.getLong(0)) == componentLocations.get(path.getLong(path.size() - 1))) {
throw new IllegalStateException();
}
if (!componentLocations.get(path.getLong(path.size() - 1)).getIncoming().isEmpty()) {
throw new IllegalStateException();
}
// componentLocations.get(path.getLong(path.size() - 1)).getIncoming() can be either empty or nonempty
for (int i = 1; i < path.size(); i++) {
if (!overlayGraph.hypotheticalScaffoldingIncomingEdge(path.getLong(i), Face.between(path.getLong(i), path.getLong(i - 1)))) {
throw new IllegalStateException();
}
}
LongList positions = path.subList(1, path.size() - 1);
positions.forEach(pos -> {
if (componentLocations.containsKey(pos)) {
throw new IllegalStateException();
}
});
System.out.println("Enabling " + positions.stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
positions.forEach(overlayGraph::enable); // TODO more performant to enable in reverse order maybe?
}
public class Output {
public void enableAncillaryScaffoldingAndRecomputeRoot(LongList positions) {
getRoot();
enable(positions);
getRoot();
throw new UnsupportedOperationException("TODO: should ancillary scaffolding even recompute the components? that scaffolding doesn't NEED to part of any component, and having all components be mutable even after the scaffolder is done is sketchy");
throw new UnsupportedOperationException("mutable components after scaffolding is not worth it");
}
public CollapsedDependencyGraphComponent getRoot() { // TODO this should probably return a new class that is not mutable in-place

View File

@ -0,0 +1,184 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package baritone.builder;
import baritone.api.utils.BetterBlockPos;
import baritone.builder.DependencyGraphScaffoldingOverlay.CollapsedDependencyGraph.CollapsedDependencyGraphComponent;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
public class SimultaneousDijkstraScaffolder implements IScaffolderStrategy {
@Override
public LongList scaffoldTo(CollapsedDependencyGraphComponent $ignored$, DependencyGraphScaffoldingOverlay overlayGraph) {
// the use-case that i'm keeping mind from a performance pov is staircased mapart
// O(n^2) across all 128x128=16384 blocks would be cringe
// so let's have a combined priority queue from all cdg provenances without a reset on every scaffolding placement
List<CollapsedDependencyGraphComponent> roots = overlayGraph.getCollapsedGraph().getComponents().values().stream().filter(component -> component.getIncoming().isEmpty()).collect(Collectors.toList());
ObjectOpenHashSet<CollapsedDependencyGraphComponent> remainingRoots = new ObjectOpenHashSet<>();
remainingRoots.addAll(roots);
Object2IntOpenHashMap<CollapsedDependencyGraphComponent> componentToId = new Object2IntOpenHashMap<>();
for (int i = 0; i < roots.size(); i++) {
componentToId.put(roots.get(i), i);
}
PriorityQueue<ScaffoldingSearchNode> openSet = new PriorityQueue<>(Comparator.<ScaffoldingSearchNode>comparingInt(node -> node.costSoFar).thenComparingInt(node -> node.key.cdgid));
Object2ObjectOpenHashMap<ScaffoldingSearchKey, ScaffoldingSearchNode> nodeMap = new Object2ObjectOpenHashMap<>();
for (CollapsedDependencyGraphComponent component : roots) {
int cdgid = componentToId.getInt(component);
LongIterator it = component.getPositions().iterator();
while (it.hasNext()) {
long l = it.nextLong();
ScaffoldingSearchKey key = new ScaffoldingSearchKey(l, cdgid);
nodeMap.put(key, new ScaffoldingSearchNode(key));
}
}
openSet.addAll(nodeMap.values());
while (!openSet.isEmpty()) {
ScaffoldingSearchNode node = openSet.poll();
CollapsedDependencyGraphComponent provenance = roots.get(node.key.cdgid).deletedIntoRecursive();
// is the deletedIntoRecursive valid? i think so because the costSoFar is a shared key in the priority queue. sure you could get a suboptimal path from an old subsection of a new cdg, but, it would only be considered after the optimal path. so, no issue? i think?
if (!provenance.getIncoming().isEmpty()) {
continue; // node originated from a cdg that has been scaffolded making it no longer a root
}
CollapsedDependencyGraphComponent tentativeComponent = overlayGraph.getCollapsedGraph().getComponentLocations().get(node.key.pos);
if (tentativeComponent != provenance) {
// TODO eventually figure out the situation with exclusiveDescendents like in the sequential dijkstra scaffolder
LongList toActivate = reconstructPathTo(node);
// have to do this bs because the scaffolding route can touch a third component even if only one scaffolding block is added
long[] allNearby = toActivate.stream().flatMapToLong(pos -> LongStream.of(OFFS_INCL_ZERO).map(off -> (off + pos) & BetterBlockPos.POST_ADDITION_MASK)).toArray();
// have to check before applying scaffolding because getComponentLocations will return the new component and not the deleted root if we did it after
Set<CollapsedDependencyGraphComponent> toCheck = LongStream.of(allNearby).mapToObj(overlayGraph.getCollapsedGraph().getComponentLocations()::get).collect(Collectors.toCollection(ObjectOpenHashSet::new));
Scaffolder.applyScaffoldingConnection(overlayGraph, toActivate);
// have to check this again because new scaffolding can make its own collapsed node
// for example if there are two individual blocks separated by a knight's move in strict_y mode, meaning there are two new scaffolding blocks added at a certain y, connecting to one block at that y and another at a higher y, then the two new scaffolding can form a larger collapsed node, causing the previous block to be merged into it
// in short, it's possible for a new root to be created
toCheck.addAll(LongStream.of(allNearby).mapToObj(overlayGraph.getCollapsedGraph().getComponentLocations()::get).collect(Collectors.toSet()));
int sz = remainingRoots.size();
for (CollapsedDependencyGraphComponent component : toCheck) {
if (component.deleted()) {
remainingRoots.remove(component);
}
}
if (remainingRoots.size() >= sz) {
throw new IllegalStateException();
}
for (long pos : toActivate) { // im being lazy, this boxes to Long i think
CollapsedDependencyGraphComponent comp = overlayGraph.getCollapsedGraph().getComponentLocations().get(pos);
if (comp.getIncoming().isEmpty()) {
if (remainingRoots.add(comp)) {
int cdgid = roots.size();
roots.add(comp);
componentToId.put(comp, cdgid);
}
ScaffoldingSearchNode newNode = new ScaffoldingSearchNode(new ScaffoldingSearchKey(pos, componentToId.getInt(comp)));
nodeMap.put(newNode.key, newNode);
openSet.add(newNode);
}
}
if (remainingRoots.size() == 1) {
return null;
}
if (remainingRoots.isEmpty()) {
throw new IllegalStateException();
}
}
for (Face face : Face.VALUES) {
int newCost = node.costSoFar + DijkstraScaffolder.edgeCost(face);
if (overlayGraph.hypotheticalScaffoldingIncomingEdge(node.key.pos, face)) {
long neighborPos = face.offset(node.key.pos);
ScaffoldingSearchKey neighborKey = new ScaffoldingSearchKey(neighborPos, node.key.cdgid);
ScaffoldingSearchNode existingNode = nodeMap.get(neighborKey);
if (existingNode != null) {
if (existingNode.costSoFar > newCost) {
throw new IllegalStateException();
}
continue;
}
ScaffoldingSearchNode newNode = new ScaffoldingSearchNode(neighborKey);
newNode.costSoFar = newCost;
newNode.prev = node;
nodeMap.put(newNode.key, newNode);
openSet.add(newNode);
}
}
}
throw new UnsupportedOperationException();
}
private static class ScaffoldingSearchKey {
long pos;
int cdgid;
public ScaffoldingSearchKey(long pos, int cdgid) {
this.pos = pos;
this.cdgid = cdgid;
}
@Override
public boolean equals(Object o) {
//if (this == o) return true;
//if (!(o instanceof ScaffoldingSearchKey)) return false;
ScaffoldingSearchKey that = (ScaffoldingSearchKey) o;
return pos == that.pos && cdgid == that.cdgid;
}
@Override
public int hashCode() {
return HashCommon.murmurHash3(cdgid) + (int) HashCommon.murmurHash3(pos);
}
}
private static class ScaffoldingSearchNode {
private final ScaffoldingSearchKey key;
private int costSoFar;
private ScaffoldingSearchNode prev;
private ScaffoldingSearchNode(ScaffoldingSearchKey key) {
this.key = key;
}
}
private static LongList reconstructPathTo(ScaffoldingSearchNode end) {
LongList path = new LongArrayList();
while (end != null) {
path.add(end.key.pos);
end = end.prev;
}
return path;
}
private static final long[] OFFS_INCL_ZERO = new long[Face.NUM_FACES + 1];
static {
for (int i = 0; i < Face.NUM_FACES; i++) {
OFFS_INCL_ZERO[i] = Face.VALUES[i].offset;
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package baritone.builder;
import baritone.api.utils.BetterBlockPos;
import org.junit.Test;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class ScaffolderTest {
@Test
public void test() {
BlockStateCachedData[][][] test = new BlockStateCachedData[8][8][8];
PackedBlockStateCuboid.fillWithAir(test);
for (int x = 0; x < test.length; x++) {
for (int z = 0; z < test[0][0].length; z++) {
test[x][0][z] = FakeStates.SOLID;
test[x][1][z] = FakeStates.SOLID;
}
}
test[5][5][5] = FakeStates.SOLID;
test[5][5][6] = FakeStates.SOLID;
test[0][5][5] = FakeStates.SOLID;
Consumer<DependencyGraphScaffoldingOverlay> debug = dgso -> {
for (int y = 0; y < test[0].length; y++) {
System.out.println("Layer " + y);
for (int x = 0; x < test.length; x++) {
for (int z = 0; z < test[0][0].length; z++) {
long pos = BetterBlockPos.toLong(x, y, z);
if (dgso.real(pos)) {
System.out.print("A" + dgso.getCollapsedGraph().getComponentLocations().get(pos).deletedIntoRecursive());
} else {
System.out.print(" ");
}
}
System.out.println();
}
}
};
PackedBlockStateCuboid states = new PackedBlockStateCuboid(test);
PlaceOrderDependencyGraph graph = new PlaceOrderDependencyGraph(states);
System.out.println("N " + Face.NORTH.z);
System.out.println("S " + Face.SOUTH.z);
for (int z = 0; z < test[0][0].length; z++) {
//System.out.println(states.get(states.bounds.toIndex(0, 0, z)));
System.out.println(z + " " + graph.outgoingEdge(BetterBlockPos.toLong(0, 0, z), Face.NORTH) + " " + graph.outgoingEdge(BetterBlockPos.toLong(0, 0, z), Face.SOUTH));
}
DependencyGraphAnalyzer.prevalidate(graph);
DependencyGraphAnalyzer.prevalidateExternalToInteriorSearch(graph);
DependencyGraphScaffoldingOverlay scaffolding = new DependencyGraphScaffoldingOverlay(graph);
System.out.println("Hewwo");
scaffolding.getCollapsedGraph().getComponents().forEach((key, value) -> {
System.out.println(key);
System.out.println(value.getPositions().stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
});
System.out.println();
debug.accept(scaffolding);
Scaffolder.Output out = Scaffolder.run(graph, DijkstraScaffolder.INSTANCE);
debug.accept(out.secretInternalForTesting());
}
}