misc progress hooking everything up into solver engine harness
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
141
src/main/java/baritone/builder/SolverEngineHarness.java
Normal file
141
src/main/java/baritone/builder/SolverEngineHarness.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user