misc progress hooking everything up into solver engine harness

This commit is contained in:
Leijurv
2021-08-24 20:26:38 -07:00
parent bea31fe77d
commit 554c0de188
11 changed files with 382 additions and 163 deletions

View File

@@ -63,6 +63,7 @@ public interface Bounds {
int volume();
// this must be implemented EXTREMELY efficiently. no integer division allowed! even a hashmap lookup is borderline.
int toIndex(int x, int y, int z); // easy to implement for cuboid, harder for more complicated shapes
default int toIndex(long pos) {

View File

@@ -112,6 +112,10 @@ public class DependencyGraphScaffoldingOverlay {
}
}
public LongSets.UnmodifiableSet scaffolding() {
return (LongSets.UnmodifiableSet) LongSets.unmodifiable(scaffoldingAdded);
}
public BlockStateCachedData data(long pos) {
if (Main.DEBUG && !real(pos)) {
throw new IllegalStateException();

View File

@@ -23,27 +23,28 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
public class GreedySolver {
SolverEngineInput engineInput;
public GreedySolver(SolverEngineInput input) {
this.engineInput = input;
}
NodeBinaryHeap heap = new NodeBinaryHeap();
Long2ObjectOpenHashMap<Node> nodes = new Long2ObjectOpenHashMap<>();
Long2ObjectOpenHashMap<WorldState> zobristWorldStateCache = new Long2ObjectOpenHashMap<>();
synchronized SolverEngineOutput search() {
while (!heap.isEmpty()) {
Node node = heap.removeLowest();
public GreedySolver(SolverEngineInput input) {
this.engineInput = input;
}
synchronized SolverEngineOutput search() {
Node root = new Node(engineInput.player, 0L, -1L, this, 0);
nodes.put(root.nodeMapKey(), root);
heap.insert(root);
zobristWorldStateCache.put(0L, new WorldState.WorldStateWrappedSubstrate(engineInput));
while (!heap.isEmpty()) {
expandNode(heap.removeLowest());
}
throw new UnsupportedOperationException();
}
private void expandNode(Node node) {
WorldState worldState = node.coalesceState(this);
long pos = node.pos();
long pos = node.pos;
BlockStateCachedData above = at(BetterBlockPos.offsetBy(pos, 0, 2, 0), worldState);
BlockStateCachedData head = at(Face.UP.offset(pos), worldState);
if (Main.DEBUG && head.collidesWithPlayer) {
@@ -56,7 +57,7 @@ public class GreedySolver {
throw new IllegalStateException();
}
boolean stickingUpIntoThirdBlock = blipsWithinBlock > Blip.TWO_BLOCKS - Blip.PLAYER_HEIGHT_SLIGHT_OVERESTIMATE; // exactly equal means not sticking up, since overestimate means overestimate
int blips = node.y * Blip.PER_BLOCK + blipsWithinBlock;
int blips = BetterBlockPos.YfromLong(pos) * Blip.PER_BLOCK + blipsWithinBlock;
mid:
for (Face travel : Face.HORIZONTALS) {
@@ -141,25 +142,46 @@ public class GreedySolver {
}
}
int calculateHeuristicModifier(WorldState previous, long blockPlacedAt) {
private int calculateHeuristicModifier(WorldState previous, long blockPlacedAt) {
if (Main.DEBUG && previous.blockExists(blockPlacedAt)) {
throw new IllegalStateException();
}
if (engineInput.desiredToBePlaced(blockPlacedAt)) {
return -100;
if (true) {
throw new UnsupportedOperationException("tune the values first lol");
}
switch (engineInput.desiredToBePlaced(blockPlacedAt)) {
case PART_OF_CURRENT_GOAL:
case SCAFFOLDING_OF_CURRENT_GOAL:
return -100; // keep kitten on task
case PART_OF_FUTURE_GOAL:
return -10; // smaller kitten treat for working ahead
case SCAFFOLDING_OF_FUTURE_GOAL:
return -5; // smallest kitten treat for working ahead on scaffolding
case ANCILLARY:
return 0; // no kitten treat for placing a random extra block
default:
throw new IllegalStateException();
}
return 0;
}
Node getNode(long playerPosition, Node prev, WorldState prevWorld, long blockPlacement) {
long worldStateZobristHash = blockPlacement == -1 ? prev.worldStateZobristHash : WorldState.updateZobrist(prev.worldStateZobristHash, blockPlacement);
private Node getNode(long playerPosition, Node prev, WorldState prevWorld, long blockPlacement) {
if (Main.DEBUG && blockPlacement != -1 && prev.coalesceState(this).blockExists(blockPlacement)) {
throw new IllegalStateException();
}
long worldStateZobristHash = prev.worldStateZobristHash;
if (blockPlacement != -1) {
worldStateZobristHash = WorldState.updateZobrist(worldStateZobristHash, blockPlacement);
}
long code = playerPosition ^ worldStateZobristHash;
Node existing = nodes.get(code);
if (existing != null) {
return existing;
}
int newHeuristic = prev.heuristic + blockPlacement == -1 ? 0 : calculateHeuristicModifier(prevWorld, blockPlacement);
Node node = new Node(BetterBlockPos.XfromLong(playerPosition), BetterBlockPos.YfromLong(playerPosition), BetterBlockPos.ZfromLong(playerPosition), worldStateZobristHash, blockPlacement, this, newHeuristic);
int newHeuristic = prev.heuristic;
if (blockPlacement != -1) {
newHeuristic += calculateHeuristicModifier(prevWorld, blockPlacement);
}
Node node = new Node(playerPosition, worldStateZobristHash, blockPlacement, this, newHeuristic);
if (Main.DEBUG && node.nodeMapKey() != code) {
throw new IllegalStateException();
}

View File

@@ -17,31 +17,24 @@
package baritone.builder;
import baritone.api.utils.BetterBlockPos;
public class Node {
public int x;
public int y;
public int z;
public final long pos;
public final long worldStateZobristHash;
public long worldStateZobristHash;
public int heuristic;
public final int heuristic;
public int cost;
public int combinedCost;
public Node previous;
public int heapPosition;
// boolean unrealizedZobristBlockChange; // no longer needed since presence in the overall GreedySolver zobristWorldStateCache indicates if this is a yet-unrealized branch of the zobrist space
long packedUnrealizedCoordinate;
private long packedUnrealizedCoordinate;
// int unrealizedState; // no longer needed now that world state is binarized with scaffolding/build versus air
// long unrealizedZobristParentHash; // no longer needed since we can compute it backwards with XOR
public Node(int x, int y, int z, long zobristState, long unrealizedBlockPlacement, GreedySolver solver, int heuristic) {
this.x = x;
this.y = y;
this.z = z;
public Node(long pos, long zobristState, long unrealizedBlockPlacement, GreedySolver solver, int heuristic) {
this.pos = pos;
this.heapPosition = -1;
this.cost = Integer.MAX_VALUE;
this.heuristic = heuristic;
@@ -55,25 +48,27 @@ public class Node {
public WorldState coalesceState(GreedySolver solver) {
WorldState alr = solver.zobristWorldStateCache.get(worldStateZobristHash);
if (alr != null) {
// packedUnrealizedCoordinate can be -1 if the cache did not include our zobrist state on a previous call to coalesceState
if (Main.DEBUG && alr.zobristHash != worldStateZobristHash) {
throw new IllegalStateException();
}
// don't check packedUnrealizedCoordinate here because it could exist (not -1) if a different route was taken to a zobrist-equivalent node (such as at a different player position) which was then expanded and coalesced
return alr;
}
if (Main.DEBUG && packedUnrealizedCoordinate == -1) {
throw new IllegalStateException();
}
long parent = WorldState.updateZobrist(worldStateZobristHash, packedUnrealizedCoordinate); // updateZobrist is symmetric because XOR, so the same operation can do child->parent as parent->child
WorldState myState = solver.zobristWorldStateCache.get(parent).withChild(packedUnrealizedCoordinate);
if (Main.DEBUG && myState.zobristHash != worldStateZobristHash) {
throw new IllegalStateException();
}
solver.zobristWorldStateCache.put(worldStateZobristHash, myState);
// TODO set packedUnrealizedCoordinate to -1 here perhaps?
packedUnrealizedCoordinate = -1;
return myState;
}
public long pos() {
return BetterBlockPos.toLong(x, y, z);
}
public long nodeMapKey() {
return pos() ^ worldStateZobristHash;
return pos ^ worldStateZobristHash;
}
public boolean inHeap() {

View File

@@ -20,9 +20,7 @@ package baritone.builder;
import baritone.builder.DependencyGraphScaffoldingOverlay.CollapsedDependencyGraph;
import baritone.builder.DependencyGraphScaffoldingOverlay.CollapsedDependencyGraph.CollapsedDependencyGraphComponent;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.*;
@@ -55,6 +53,14 @@ public class Scaffolder {
this.rootComponents = calcRoots();
}
public static Scaffolder run(DependencyGraphScaffoldingOverlay overlayGraph) {
Scaffolder scaffolder = new Scaffolder(overlayGraph);
while (scaffolder.rootComponents.size() > 1) {
scaffolder.loop();
}
return 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
@@ -66,7 +72,9 @@ public class Scaffolder {
}
private void loop() {
int cid = collapsedGraph.lastComponentID().getAsInt();
if (rootComponents.size() <= 1) {
throw new IllegalStateException();
}
CollapsedDependencyGraphComponent root = rootComponents.remove(rootComponents.size() - 1);
if (!root.getIncoming().isEmpty()) {
throw new IllegalStateException();
@@ -83,15 +91,23 @@ public class Scaffolder {
if (!componentLocations.containsKey(path.get(0).pos)) {
throw new IllegalStateException();
}
for (int i = 1; i < path.size() - 1; i++) {
if (componentLocations.containsKey(path.get(i).pos)) {
LongList toEnable = path
.subList(1, path.size() - 1)
.stream()
.map(node -> node.pos)
.collect(Collectors.toCollection(LongArrayList::new));
enable(toEnable);
}
private void enable(LongList positions) {
positions.forEach(pos -> {
if (componentLocations.containsKey(pos)) {
throw new IllegalStateException();
}
}
});
int cid = collapsedGraph.lastComponentID().getAsInt();
for (int i = 1; i < path.size() - 1; i++) {
overlayGraph.enable(path.get(i).pos);
}
positions.forEach(overlayGraph::enable);
int newCID = collapsedGraph.lastComponentID().getAsInt();
for (int i = cid + 1; i <= newCID; i++) {
@@ -106,7 +122,23 @@ public class Scaffolder {
throw new IllegalStateException();
}
}
}
public void enableAncillaryScaffoldingAndRecomputeRoot(LongList positions) {
getRoot();
enable(positions);
getRoot();
}
public CollapsedDependencyGraphComponent getRoot() {
if (rootComponents.size() != 1) {
throw new IllegalStateException(); // this is okay because this can only possibly be called after Scaffolder.run is completed
}
CollapsedDependencyGraphComponent root = rootComponents.get(0);
if (!root.getIncoming().isEmpty()) {
throw new IllegalStateException();
}
return root;
}
private void walkAllDescendents(CollapsedDependencyGraphComponent root, Set<CollapsedDependencyGraphComponent> set) {
@@ -155,7 +187,7 @@ public class Scaffolder {
// 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) {
if (existingNode.costSoFar != newCost) {
throw new IllegalStateException();
}
continue; // nothing to do - we already have an equal-or-better path to this location
@@ -194,23 +226,4 @@ public class Scaffolder {
this.pos = pos;
}
}
private void sanityCheck() {
// we will trust DependencyGraphScaffoldingOverlay that there are no cycles of any kind in the components - they form a DAG
//
}
private void enableBlock(long pos) {
// first, before everything gets destroyed by updating the overlay graph, let's chill out our subgraphs
// i really would rather not write a whole new thing for incrementally recomputing an overlay graph!
}
public static void run(DependencyGraphScaffoldingOverlay overlay) {
CollapsedDependencyGraph collapsed = overlay.getCollapsedGraph();
Map<Integer, CollapsedDependencyGraphComponent> components = collapsed.getComponents();
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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.builder.DependencyGraphScaffoldingOverlay.CollapsedDependencyGraph.CollapsedDependencyGraphComponent;
import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalLong;
import java.util.Set;
import java.util.stream.Collectors;
public class SolverEngineHarness {
private final ISolverEngine engine;
private final PackedBlockStateCuboid blocks;
private final PlaceOrderDependencyGraph graph;
private final DependencyGraphScaffoldingOverlay overlay;
private final Scaffolder scaffolder;
public SolverEngineHarness(ISolverEngine engine, PackedBlockStateCuboid blocks) {
this.engine = engine;
this.blocks = blocks;
this.graph = new PlaceOrderDependencyGraph(blocks);
this.overlay = new DependencyGraphScaffoldingOverlay(graph);
this.scaffolder = Scaffolder.run(overlay);
}
public List<SolvedActionStep> solve(long playerStartPos) {
LongOpenHashSet alreadyPlacedSoFar = new LongOpenHashSet();
List<SolvedActionStep> steps = new ArrayList<>();
while (true) {
Set<CollapsedDependencyGraphComponent> frontier = calculateCurrentSolverFrontier(alreadyPlacedSoFar);
if (frontier.isEmpty()) {
// nothing on the table!
break;
}
List<LongOpenHashSet> goals = expandAndSubtract(frontier, alreadyPlacedSoFar);
long playerPos = steps.isEmpty() ? playerStartPos : steps.get(steps.size() - 1).playerMovesTo();
SolverEngineInput inp = new SolverEngineInput(graph, overlay.scaffolding(), alreadyPlacedSoFar, goals, playerPos);
SolverEngineOutput out = engine.solve(inp);
if (Main.DEBUG) {
out.sanityCheck(inp);
}
steps.addAll(out.getSteps());
LongList ancillaryScaffolding = new LongArrayList();
for (SolvedActionStep step : out.getSteps()) {
OptionalLong blockPlace = step.placeAt();
if (blockPlace.isPresent()) {
long pos = blockPlace.getAsLong();
if (!alreadyPlacedSoFar.add(pos)) {
throw new IllegalStateException();
}
if (overlay.air(pos)) { // not part of the schematic, nor intended scaffolding
ancillaryScaffolding.add(pos); // therefore it must be ancillary scaffolding, some throwaway block we needed to place in order to achieve something else, maybe to get a needed vantage point on some particularly tricky placement
}
}
}
scaffolder.enableAncillaryScaffoldingAndRecomputeRoot(ancillaryScaffolding);
}
if (Main.DEBUG) {
overlay.forEachReal(pos -> {
if (!alreadyPlacedSoFar.contains(pos)) {
throw new IllegalStateException();
}
});
alreadyPlacedSoFar.forEach(pos -> {
if (!overlay.real(pos)) {
throw new IllegalStateException();
}
});
}
return steps;
}
private List<LongOpenHashSet> expandAndSubtract(Set<CollapsedDependencyGraphComponent> frontier, LongSet already) {
return frontier.stream()
.map(component -> {
LongOpenHashSet remainingPositionsInComponent = new LongOpenHashSet(component.getPositions().size());
LongIterator it = component.getPositions().iterator();
while (it.hasNext()) {
long pos = it.nextLong();
if (!already.contains(pos)) {
remainingPositionsInComponent.add(pos);
}
}
return remainingPositionsInComponent;
}).collect(Collectors.toList());
}
private Set<CollapsedDependencyGraphComponent> calculateCurrentSolverFrontier(LongSet alreadyPlacedSoFar) {
Set<CollapsedDependencyGraphComponent> currentFrontier = new ObjectOpenHashSet<>();
Set<CollapsedDependencyGraphComponent> confirmedFullyCompleted = new ObjectOpenHashSet<>();
ObjectArrayFIFOQueue<CollapsedDependencyGraphComponent> toExplore = new ObjectArrayFIFOQueue<>();
toExplore.enqueue(scaffolder.getRoot());
outer:
while (!toExplore.isEmpty()) {
CollapsedDependencyGraphComponent component = toExplore.dequeue();
for (CollapsedDependencyGraphComponent parent : component.getIncoming()) {
if (!confirmedFullyCompleted.contains(parent)) {
// to be here, one parent must have been fully completed, but it's possible a different parent is not yet fully completed
// this is because while the collapsed block placement dependency graph is a directed acyclic graph, it can still have a diamond shape
// imagine the dependency is A->B, A->C, C->D, B->E, D->E (so, A is root, it splits in two, then converges at E)
// it might explore it in order A, B, C, E (because B, yet canceled as D is not completed yet), D (because C), E (now succeeds since B and D are confirmed)
continue outer;
}
}
LongIterator it = component.getPositions().iterator();
while (it.hasNext()) {
if (!alreadyPlacedSoFar.contains(it.nextLong())) {
currentFrontier.add(component);
continue outer;
}
}
if (confirmedFullyCompleted.add(component)) {
for (CollapsedDependencyGraphComponent child : component.getOutgoing()) {
toExplore.enqueue(child); // always reenqueue children because we added ourselves to confirmedFullyCompleted, meaning this time they may well have all parents completed
}
}
}
return currentFrontier;
}
}

View File

@@ -17,16 +17,21 @@
package baritone.builder;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.longs.LongSets;
import java.util.Collection;
import java.util.List;
public class SolverEngineInput {
public final PlaceOrderDependencyGraph graph;
public final LongSet intendedScaffolding;
public final LongSet alreadyPlaced;
private final LongOpenHashSet toPlaceNow;
public final LongSet allToPlaceNow;
private final List<LongOpenHashSet> toPlaceNow;
public final long player;
/**
@@ -36,12 +41,13 @@ public class SolverEngineInput {
* @param toPlaceNow Locations that are currently top of mind and must be placed. For example, to place the rest of the graph, this would be intendedScaffolding|(graph.allNonAir&~alreadyPlaced)
* @param player Last but not least, where is the player standing?
*/
public SolverEngineInput(PlaceOrderDependencyGraph graph, LongOpenHashSet intendedScaffolding, LongOpenHashSet alreadyPlaced, LongOpenHashSet toPlaceNow, long player) {
public SolverEngineInput(PlaceOrderDependencyGraph graph, LongSets.UnmodifiableSet intendedScaffolding, LongOpenHashSet alreadyPlaced, List<LongOpenHashSet> toPlaceNow, long player) {
this.graph = graph;
this.intendedScaffolding = LongSets.unmodifiable(intendedScaffolding);
this.intendedScaffolding = intendedScaffolding;
this.alreadyPlaced = LongSets.unmodifiable(alreadyPlaced);
this.toPlaceNow = toPlaceNow;
this.player = player;
this.allToPlaceNow = combine(toPlaceNow);
if (Main.DEBUG) {
sanityCheck();
}
@@ -51,24 +57,68 @@ public class SolverEngineInput {
if (!graph.bounds().inRangePos(player)) {
throw new IllegalStateException();
}
for (LongSet toVerify : new LongSet[]{intendedScaffolding, alreadyPlaced, toPlaceNow}) {
for (LongSet toVerify : new LongSet[]{intendedScaffolding, alreadyPlaced}) {
for (long pos : toVerify) {
if (!graph.bounds().inRangePos(pos)) {
throw new IllegalStateException();
}
}
}
for (long pos : toPlaceNow) {
if (alreadyPlaced.contains(pos)) {
throw new IllegalStateException();
for (LongSet toPlace : toPlaceNow) {
for (long pos : toPlace) {
if (alreadyPlaced.contains(pos)) {
throw new IllegalStateException();
}
if (!graph.bounds().inRangePos(pos)) {
throw new IllegalStateException();
}
if (intendedScaffolding.contains(pos) ^ graph.airTreatedAsScaffolding(pos)) {
throw new IllegalStateException();
}
}
}
}
public boolean desiredToBePlaced(long pos) {
public PlacementDesire desiredToBePlaced(long pos) {
if (Main.DEBUG && !graph.bounds().inRangePos(pos)) {
throw new IllegalStateException();
}
return !graph.data(pos).isAir || intendedScaffolding.contains(pos);
if (allToPlaceNow.contains(pos)) {
if (graph.airTreatedAsScaffolding(pos)) {
return PlacementDesire.SCAFFOLDING_OF_CURRENT_GOAL;
} else {
return PlacementDesire.PART_OF_CURRENT_GOAL;
}
} else {
// for positions NOT in allToPlaceNow, intendedScaffolding is not guaranteed to be equivalent to airTreatedAsScaffolding
if (graph.airTreatedAsScaffolding(pos)) {
if (intendedScaffolding.contains(pos)) {
return PlacementDesire.SCAFFOLDING_OF_FUTURE_GOAL;
} else {
return PlacementDesire.ANCILLARY;
}
} else {
return PlacementDesire.PART_OF_FUTURE_GOAL;
}
}
}
public enum PlacementDesire {
PART_OF_CURRENT_GOAL,
PART_OF_FUTURE_GOAL,
SCAFFOLDING_OF_CURRENT_GOAL,
SCAFFOLDING_OF_FUTURE_GOAL,
ANCILLARY
}
private static LongOpenHashSet combine(List<LongOpenHashSet> entries) {
LongOpenHashSet ret = new LongOpenHashSet(entries.stream().mapToInt(Collection::size).sum());
for (LongOpenHashSet set : entries) {
LongIterator it = set.iterator();
while (it.hasNext()) {
ret.add(it.nextLong());
}
}
return ret;
}
}

View File

@@ -19,6 +19,7 @@ package baritone.builder;
import baritone.api.utils.BetterBlockPos;
import java.util.Collections;
import java.util.List;
public class SolverEngineOutput {
@@ -48,6 +49,10 @@ public class SolverEngineOutput {
}
}
public List<SolvedActionStep> getSteps() {
return Collections.unmodifiableList(steps);
}
private static void sanityCheckMovement(long from, long to) {
int dx = BetterBlockPos.XfromLong(from) - BetterBlockPos.XfromLong(to);
int dz = BetterBlockPos.ZfromLong(from) - BetterBlockPos.ZfromLong(to);

View File

@@ -69,7 +69,7 @@ public class TarjansAlgorithm {
}
private void strongConnect(long vpos) {
if (!graph.real(vpos)) {
if (Main.DEBUG && !graph.real(vpos)) {
throw new IllegalStateException();
}
TarjanVertexInfo info = infoMap.get(vpos);

View File

@@ -18,9 +18,7 @@
package baritone.builder;
import baritone.api.utils.BetterBlockPos;
import baritone.pathing.calc.openset.BinaryHeapOpenSet;
import baritone.utils.BlockStateInterface;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@@ -39,76 +37,11 @@ public class Testing {
public static class AbstractNodeCostSearch {
Long2ObjectOpenHashMap<PathNode> nodeMap;
BinaryHeapOpenSet openSet;
Long2ObjectOpenHashMap<BlockStateInterfaceAbstractWrapper> zobristMap;
long[] KEYS_CACHE = new long[MAX_LEAF_LEVEL];
int[] VALS_CACHE = new int[MAX_LEAF_LEVEL];
protected void search() {
while (!openSet.isEmpty()) {
PathNode node = null;
BlockStateInterfaceAbstractWrapper bsi = node.get(this);
// consider actions:
// traverse
// pillar
// place blocks beneath feet
}
}
/*protected PathNode getNodeAtPosition(int x, int y, int z, long hashCode) {
baritone.pathing.calc.PathNode node = map.get(hashCode);
if (node == null) {
node = new baritone.pathing.calc.PathNode(x, y, z, goal);
map.put(hashCode, node);
}
return node;
}*/
}
public static final class PathNode {
public int x;
public int y;
public int z;
public long zobristBlocks;
public int heuristic;
public int cost;
public int combinedCost;
public PathNode previous;
public int heapPosition;
boolean unrealizedZobristBlockChange;
long packedUnrealizedCoordinate;
int unrealizedState;
long unrealizedZobristParentHash;
public long nodeMapKey() {
return BetterBlockPos.longHash(x, y, z) ^ zobristBlocks;
}
public BlockStateInterfaceAbstractWrapper get(AbstractNodeCostSearch ref) {
BlockStateInterfaceAbstractWrapper alr = ref.zobristMap.get(zobristBlocks);
if (alr != null) {
return alr;
}
if (!unrealizedZobristBlockChange || (BetterBlockPos.murmur64(BetterBlockPos.longHash(packedUnrealizedCoordinate) ^ unrealizedState) ^ zobristBlocks) != unrealizedZobristParentHash) {
throw new IllegalStateException();
}
alr = ref.zobristMap.get(unrealizedZobristParentHash).with(packedUnrealizedCoordinate, unrealizedState, ref);
if (alr.zobrist != zobristBlocks) {
throw new IllegalStateException();
}
ref.zobristMap.put(zobristBlocks, alr);
return alr;
}
}
public static abstract class BlockStateInterfaceAbstractWrapper {
protected long zobrist;
@@ -196,7 +129,7 @@ public class Testing {
}
}
BlockStateInterfaceMappedDiff coalesced = new BlockStateInterfaceMappedDiff(ancestor, keys, vals, startIdx, zobrist);
ref.zobristMap.put(zobrist, coalesced);
//ref.zobristMap.put(zobrist, coalesced);
return coalesced;
}
}
@@ -271,6 +204,8 @@ public class Testing {
public static class BlockStateLookupHelper {
// this falls back from System.identityHashCode to == so it's safe even in the case of a 32-bit collision
// but that would never have happened anyway: https://www.wolframalpha.com/input/?i=%28%282%5E32-1%29%2F%282%5E32%29%29%5E2000
private static Reference2IntOpenHashMap<IBlockState> states = new Reference2IntOpenHashMap<>();
static {
@@ -286,14 +221,5 @@ public class Testing {
states.put(state, realState);
return realState;
}
static {
IntOpenHashSet taken = new IntOpenHashSet();
for (IBlockState state : Block.BLOCK_STATE_IDS) {
if (!taken.add(System.identityHashCode(state))) {
throw new IllegalStateException("Duplicate hashcode among IBlockStates");
}
}
}
}
}

View File

@@ -18,22 +18,84 @@
package baritone.builder;
import baritone.api.utils.BetterBlockPos;
import it.unimi.dsi.fastutil.longs.LongIterator;
import java.util.BitSet;
public abstract class WorldState {
protected long zobristHash;
public final long zobristHash;
public abstract boolean blockExists(long coord);
public WorldState withChild(long coord) {
throw new UnsupportedOperationException();
protected WorldState(long zobristHash) {
this.zobristHash = zobristHash;
}
public static class WorldStateWrappedSubstrate {
// refers to GreedySolver.SolverEngineInput.PlaceOrderDependencyGraph.PackedBlockStateCuboid
public abstract boolean blockExists(long pos);
public WorldState withChild(long pos) {
return new WorldStateLeafDiff(this, pos);
}
public static long updateZobrist(long worldStateZobristHash, long changedPosition) {
return BetterBlockPos.zobrist(changedPosition) ^ worldStateZobristHash;
}
public static class WorldStateWrappedSubstrate extends WorldState {
private final Bounds bounds;
private final BitSet placed; // won't be copied, since alreadyPlaced is going to be **far** larger than toPlaceNow
private final int offset;
public WorldStateWrappedSubstrate(SolverEngineInput inp) {
super(0L);
this.bounds = inp.graph.bounds();
int min;
int max;
{
LongIterator positions = inp.alreadyPlaced.iterator();
if (!positions.hasNext()) {
throw new IllegalStateException();
}
min = bounds.toIndex(positions.nextLong());
max = min;
while (positions.hasNext()) {
int val = bounds.toIndex(positions.nextLong());
min = Math.min(min, val);
max = Math.max(max, val);
}
}
this.offset = min;
this.placed = new BitSet(max - min + 1);
LongIterator it = inp.alreadyPlaced.iterator();
while (it.hasNext()) {
placed.set(bounds.toIndex(it.nextLong()) - offset);
}
}
@Override
public boolean blockExists(long pos) {
int storedAt = bounds.toIndex(pos) - offset;
if (storedAt < 0) {
return false;
}
return placed.get(storedAt);
}
}
public static class WorldStateLeafDiff extends WorldState {
private WorldState delegate;
private final long pos;
private WorldStateLeafDiff(WorldState delegate, long pos) {
super(updateZobrist(delegate.zobristHash, pos));
this.delegate = delegate;
this.pos = pos;
}
@Override
public boolean blockExists(long pos) {
return this.pos == pos || delegate.blockExists(pos);
}
}
}