coolant logic refactor

This commit is contained in:
Curle 2023-06-30 07:10:56 +01:00
parent 8da40906e6
commit 7072a25ac4

View File

@ -28,135 +28,164 @@ public class CoolantBaseBlock extends Block {
if (level.isClientSide) return;
long startTime = System.currentTimeMillis();
boolean encounteredMetering = false;
boolean encounteredCompressor = false;
boolean encounteredHeatSpreader = false;
Set<BlockPos> visitedBlocks = new HashSet<>();
// If we happened to choose the path that encounters the metering device before a heat spreader, we need to swap our detection system.
boolean prematureMetering = false;
Direction lastTransitionDirection;
LOGGER.info("Beginning network discovery");
searchForValidNetwork(new BlockPos.MutableBlockPos(pos.getX(), pos.getY(), pos.getZ()), level);
// Get the valid directions we can go.
var dirs = getValidTransitionsFrom(pos, null, level, visitedBlocks);
LOGGER.info("First step: " + dirs.size() + " valid moves.");
// If we aren't connected to at least 2 other valid blocks, it is impossible to have a completed network.
if (dirs.size() < 2) {
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
return;
};
// Check the block we're at, first.
if (this == Blocks.COMPRESSOR_BLOCK.block().get()) encounteredCompressor = true;
else if (this == Blocks.COOLANT_METERING_BLOCK.block().get()) { encounteredMetering = true; prematureMetering = true; }
else if (this == Blocks.COOLANT_HEAT_SPREADER_BLOCK.block().get()) encounteredHeatSpreader = true;
visitedBlocks.add(pos);
while (dirs.size() > 1) {
// Traverse the new block
lastTransitionDirection = (Direction) dirs.keySet().toArray()[0];
BlockPos traversedPos = dirs.get(lastTransitionDirection);
Block reachedBlock = level.getBlockState(traversedPos).getBlock();
LOGGER.info("Traversing through " + lastTransitionDirection + " to " + traversedPos + ", which is a " + getBlockName(reachedBlock));
visitedBlocks.add(traversedPos);
// If we reached a metering block for the first time
if (reachedBlock == Blocks.COOLANT_METERING_BLOCK.block().get()) {
if (encounteredMetering) {
LOGGER.info("Two metering devices in the same network! Not valid.");
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
return;
}
// And we have already reached a heat spreader:
if (encounteredHeatSpreader) {
// Tell the system to check for another
encounteredHeatSpreader = false;
// Set the metering flag true
} else {
// We're searching prematurely!
// We'll search for it at the compressor instead.
prematureMetering = true;
}
encounteredMetering = true;
// The compressor has its own things to check..
} else if (reachedBlock == Blocks.COMPRESSOR_BLOCK.block().get()) {
if (encounteredCompressor) {
LOGGER.info("Two compressors in the same network! Not valid.");
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
return;
}
// Premature metering means we should check that a heat spreader is between the metering device and the compressor.
if (prematureMetering) {
if (!encounteredHeatSpreader) {
LOGGER.info("Metering Device and Compressor are too close together! Not valid.");
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
return;
} else {
encounteredCompressor = true;
}
} else {
encounteredCompressor = true;
}
// Heat spreaders may have many, so we just flip the flag to be true if we're just getting to one now.
} else if (reachedBlock == Blocks.COOLANT_HEAT_SPREADER_BLOCK.block().get()) {
if (!encounteredHeatSpreader) encounteredHeatSpreader = true;
}
dirs = getValidTransitionsFrom(traversedPos, lastTransitionDirection, level, visitedBlocks);
}
// Try to finalise stuff - we gotta have everything in our network.
// First, check that the last remaining direction takes us into a block we've already visited (the closed loop)
if (visitedBlocks.contains(dirs.values().iterator().next())) {
// Check for premature mode
if (prematureMetering) {
if (!(encounteredCompressor && encounteredHeatSpreader)) {
LOGGER.info("Two heat spreaders not found in valid configuration!");
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
return;
}
} else {
if (!(encounteredCompressor && encounteredMetering && encounteredHeatSpreader)) {
LOGGER.info("Not encountered all components of the coolant system!");
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
return;
}
}
}
LOGGER.info("Valid coolant loop!");
long endTime = System.currentTimeMillis();
LOGGER.info("Discovery took " + (endTime - startTime) + "ms");
}
private Map<Direction, BlockPos> getValidTransitionsFrom(BlockPos pos, Direction incoming, Level level, Set<BlockPos> visited) {
Map<Direction, BlockPos> dirs = new EnumMap<>(Direction.class);
private Set<Transition> getValidTransitionsFrom(NetworkState state, Level level) {
Set<Transition> dirs = new HashSet<>();
for (Direction dir : Direction.values()) {
if (incoming != null && dir == incoming) continue;
BlockPos newPos = pos.relative(dir);
if (visited.contains(newPos)) continue;
if (VALID_COOLANT_BLOCKS.contains(level.getBlockState(newPos).getBlock())) dirs.put(dir, newPos);
if (state.lastTransition != null && dir == state.lastTransition) continue;
BlockPos newPos = state.currentPos.relative(dir);
if (state.visited.contains(newPos)) continue;
if (VALID_COOLANT_BLOCKS.contains(level.getBlockState(newPos).getBlock())) dirs.add(new Transition(dir, newPos, level.getBlockState(newPos)));
}
return dirs;
}
// TODO: Revert the search to a known good state
private boolean searchForValidNetwork(BlockPos.MutableBlockPos pos, Level level) {
NetworkState state = new NetworkState(false, false, false, new HashSet<>(), false, null, pos);
Queue<StateTransitions> queue = new ArrayDeque<>();
LOGGER.info("Beginning network discovery");
// Get the valid directions we can go.
var dirs = getValidTransitionsFrom(state, level);
LOGGER.info("First step: " + dirs.size() + " valid moves.");
// If we aren't connected to at least 2 other valid blocks, it is impossible to have a completed network.
if (dirs.size() < 2) {
return false;
}
state.visited.add(pos);
queue.add(new StateTransitions(state.copy(), dirs));
// Check the block we're at, first.
if (this == Blocks.COMPRESSOR_BLOCK.block().get()) state.compressor = true;
else if (this == Blocks.COOLANT_METERING_BLOCK.block().get()) { state.metering = true; state.premature = true; }
else if (this == Blocks.COOLANT_HEAT_SPREADER_BLOCK.block().get()) state.spreader = true;
while(!queue.isEmpty()) {
// The search loop
search(queue.element(), level, queue);
// Try to finalise stuff - we gotta have everything in our network.
// First, check that the last remaining direction takes us into a block we've already visited (the closed loop)
if (state.visited.contains(dirs.iterator().next().pos())) {
// Check for premature mode
if (state.premature) {
if (!(state.compressor && state.spreader)) {
LOGGER.info("Two heat spreaders not found in valid configuration!");
queue.poll();
}
} else {
if (!(state.compressor && state.metering && state.spreader)) {
LOGGER.info("Not encountered all components of the coolant system!");
queue.poll();
}
}
}
}
LOGGER.info("Valid coolant loop!");
return true;
}
void search(StateTransitions st, Level level, Queue<StateTransitions> queue) {
while (st.transitions.size() > 1) {
// Traverse the new block
Transition transition = st.transitions.iterator().next();
st.state.lastTransition = transition.dir();
st.state.currentPos = transition.pos();
Block reachedBlock = transition.state().getBlock();
LOGGER.info("Traversing through " + st.state.lastTransition + " to " + st.state.currentPos + ", which is a " + getBlockName(reachedBlock));
st.state.visited.add(st.state.currentPos);
// If we reached a metering block for the first time
if (reachedBlock == Blocks.COOLANT_METERING_BLOCK.block().get()) {
st.state.metering();
// The compressor has its own things to check..
} else if (reachedBlock == Blocks.COMPRESSOR_BLOCK.block().get()) {
st.state.compressor();
// Heat spreaders may have many, so we just flip the flag to be true if we're just getting to one now.
} else if (reachedBlock == Blocks.COOLANT_HEAT_SPREADER_BLOCK.block().get()) {
if (!st.state.spreader) st.state.spreader = true;
}
Set<Transition> newTransitions = getValidTransitionsFrom(st.state, level);
queue.add(new StateTransitions(st.state.copy(), newTransitions));
}
}
private String getBlockName(Block block) {
return block.getName().getContents().toString();
}
record Transition(Direction dir, BlockPos pos, BlockState state) {}
static class NetworkState {
boolean metering, compressor, spreader, premature;
Set<BlockPos> visited;
Direction lastTransition;
BlockPos currentPos;
public NetworkState(boolean metering, boolean compressor, boolean spreader, HashSet<BlockPos> visited, boolean premature, Direction lastTransition, BlockPos pos) {
this.metering = metering;
this.compressor = compressor;
this.spreader = spreader;
this.premature = premature;
this.visited = visited;
this.lastTransition = lastTransition;
this.currentPos = pos;
}
void metering() {
if (metering)
LOGGER.info("Two metering devices in the same network! Not valid.");
// And we have already reached a heat spreader:
if (spreader) {
// Tell the system to check for another
spreader = false;
// Set the metering flag true
} else {
// We're searching prematurely!
// We'll search for it at the compressor instead.
premature = true;
}
metering = true;
}
void compressor() {
if (compressor)
LOGGER.info("Two compressors in the same network! Not valid.");
// Premature metering means we should check that a heat spreader is between the metering device and the compressor.
if (premature) {
if (!spreader) {
LOGGER.info("Metering Device and Compressor are too close together! Not valid.");
} else {
compressor = true;
}
} else {
compressor = true;
}
}
NetworkState copy() {
return new NetworkState(metering, compressor, spreader, new HashSet<>(visited), premature, lastTransition, currentPos);
}
}
record StateTransitions(NetworkState state, Set<Transition> transitions) {}
}