aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyeyoung <42869671+cyoung06@users.noreply.github.com>2023-10-29 01:19:24 +0900
committerGitHub <noreply@github.com>2023-10-29 01:19:24 +0900
commit9e3e93a3337989d7f669678a495866b38fdf3f89 (patch)
tree2194ffde48827fc9ab38659a0d566c2c796dd3c6
parentee521c8a3811d46a816cada7256bb006738b1a21 (diff)
downloadSkyblock-Dungeons-Guide-9e3e93a3337989d7f669678a495866b38fdf3f89.tar.gz
Skyblock-Dungeons-Guide-9e3e93a3337989d7f669678a495866b38fdf3f89.tar.bz2
Skyblock-Dungeons-Guide-9e3e93a3337989d7f669678a495866b38fdf3f89.zip
Waterboard simulation fix and one-flow support (#433)
* - New waterboard simulator Signed-off-by: syeyoung <cyoung06@naver.com> * - New waterboard simulator Signed-off-by: syeyoung <cyoung06@naver.com> * - Hyper accurate waterboard prediction Signed-off-by: syeyoung <cyoung06@naver.com> * - Better waterboard solver, with 1 flow support and timings Signed-off-by: syeyoung <cyoung06@naver.com> * - a bit mroe strict stableness detection Signed-off-by: syeyoung <cyoung06@naver.com> --------- Signed-off-by: syeyoung <cyoung06@naver.com>
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverState.java33
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverStateContradict.java25
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/RoomProcessorWaterPuzzle.java215
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Route.java60
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Simulator.java257
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/SwitchData.java46
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterBoard.java423
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterNode.java36
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterPathfinder.java204
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeAir.java59
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeEnd.java60
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeStart.java66
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeToggleable.java71
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeWall.java62
14 files changed, 645 insertions, 972 deletions
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverState.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverState.java
deleted file mode 100755
index e86fdd5c..00000000
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverState.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
- * Copyright (C) 2021 cyoung06
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
-@Data
-@AllArgsConstructor
-public class LeverState {
- private String blockId;
- private boolean requiredState;
-
- public LeverState invert() {
- return new LeverState(blockId, !requiredState);
- }
-}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverStateContradict.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverStateContradict.java
deleted file mode 100755
index a574d6bf..00000000
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/LeverStateContradict.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
- * Copyright (C) 2021 cyoung06
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle;
-
-public class LeverStateContradict extends LeverState {
- public LeverStateContradict() {
- super("contradict", true);
- }
-}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/RoomProcessorWaterPuzzle.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/RoomProcessorWaterPuzzle.java
index 98ac9cc9..fccfcaa1 100755
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/RoomProcessorWaterPuzzle.java
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/RoomProcessorWaterPuzzle.java
@@ -25,21 +25,43 @@ import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.GeneralRoomProcessor;
import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.RoomProcessorGenerator;
import kr.syeyoung.dungeonsguide.mod.features.FeatureRegistry;
import kr.syeyoung.dungeonsguide.mod.utils.RenderUtils;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockLever;
+import net.minecraft.block.BlockLiquid;
+import net.minecraft.init.Blocks;
import net.minecraft.util.BlockPos;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.world.World;
import java.awt.*;
+import java.util.*;
import java.util.List;
+import java.util.stream.Collectors;
public class RoomProcessorWaterPuzzle extends GeneralRoomProcessor {
private boolean argumentsFulfilled = false;
- private WaterBoard waterBoard;
private final OffsetPointSet doorsClosed;
private final OffsetPointSet levers;
private final OffsetPointSet frontBoard;
private final OffsetPointSet backBoard;
- private final OffsetPoint water_lever;
+
+
+
+ private Simulator.Node nodes[][];
+ private Simulator.Pt waterNodeStart;
+ private Map<String, Simulator.Pt> waterNodeEnds = new HashMap<>();
+ private Map<String, List<Simulator.Pt>> switchFlips = new HashMap<>();
+ private Map<String, BlockPos> switchLoc = new HashMap<>();
+ private List<String> targetDoors = new ArrayList<>();
+
+ private Map<Simulator.Pt, BlockPos> ptMapping = new HashMap<>();
+
+ private Thread t;
+ private List<WaterPathfinder.NodeNode> solutionList = new ArrayList<>();
+ private long lastStable;
+ private long lastUnstable;
public RoomProcessorWaterPuzzle(DungeonRoom dungeonRoom) {
super(dungeonRoom);
@@ -47,28 +69,162 @@ public class RoomProcessorWaterPuzzle extends GeneralRoomProcessor {
backBoard = (OffsetPointSet) dungeonRoom.getDungeonRoomInfo().getProperties().get("back");
levers = (OffsetPointSet) dungeonRoom.getDungeonRoomInfo().getProperties().get("levers");
doorsClosed = (OffsetPointSet) dungeonRoom.getDungeonRoomInfo().getProperties().get("doors");
- water_lever = (OffsetPoint) dungeonRoom.getDungeonRoomInfo().getProperties().get("water-lever");
+ OffsetPoint water_lever = (OffsetPoint) dungeonRoom.getDungeonRoomInfo().getProperties().get("water-lever");
if (frontBoard == null || backBoard == null || levers == null || doorsClosed == null ||water_lever == null) {
argumentsFulfilled = false;
} else {
argumentsFulfilled = true;
- try {
- waterBoard = new WaterBoard(this, frontBoard, backBoard, levers, doorsClosed, water_lever);
- } catch (Exception e) {
- e.printStackTrace();
+ buildLeverStates();
+ buildNodes(true);
+ targetDoors();
+
+ }
+
+
+
+ }
+
+
+ private void buildLeverStates(){
+ for (OffsetPoint offsetPoint : levers.getOffsetPointList()) {
+ if (offsetPoint.getBlock(getDungeonRoom()) == Blocks.lever){
+ BlockPos pos = offsetPoint.getBlockPos(getDungeonRoom());
+ World w= getDungeonRoom().getContext().getWorld();
+ BlockLever.EnumOrientation enumOrientation = w.getBlockState(pos).getValue(BlockLever.FACING);
+ EnumFacing enumFacing = enumOrientation.getFacing();
+ BlockPos newPos = pos.add(-enumFacing.getDirectionVec().getX(),0,-enumFacing.getDirectionVec().getZ());
+
+ int id = Block.getIdFromBlock(w.getChunkFromBlockCoords(newPos).getBlock(newPos));
+ int data = w.getChunkFromBlockCoords(newPos).getBlockMetadata(newPos);
+
+ switchFlips.put(id+":"+data, new ArrayList<>());
+ switchLoc.put(id+":"+data, pos);
}
}
+ switchLoc.put("mainStream", ((OffsetPoint) getDungeonRoom().getDungeonRoomInfo().getProperties().get("water-lever")).getBlockPos(getDungeonRoom()));
+ switchFlips.put("mainStream", new ArrayList<>());
}
+ private void buildNodes(boolean switchfips) {
+ List<OffsetPoint> frontPoints = frontBoard.getOffsetPointList();
+ List<OffsetPoint> backPoints = backBoard.getOffsetPointList();
+
+ nodes = new Simulator.Node[25][19];
+ waterNodeStart = new Simulator.Pt(9, 0);
+ for (int x = 0; x < 19; x++) {
+ for (int y = 0; y < 25; y++) {
+ OffsetPoint front = frontPoints.get(x *25 +y);
+ OffsetPoint back = backPoints.get(x * 25 +y);
+
+ ptMapping.put(new Simulator.Pt(x,y), front.getBlockPos(getDungeonRoom()));
+ int frontId = Block.getIdFromBlock(front.getBlock(getDungeonRoom()));
+ int backId = Block.getIdFromBlock(back.getBlock(getDungeonRoom()));
+ int frontData = front.getData(getDungeonRoom());
+ int backData = back.getData(getDungeonRoom());
+
+ if (switchfips) {
+ String switchD;
+
+ if (switchFlips.containsKey(switchD = (backId + ":" + backData)) || switchFlips.containsKey(switchD = (frontId + ":" + frontData))) {
+ switchFlips.get(switchD).add(new Simulator.Pt(x, y));
+ }
+ }
+
+ if (frontId == 0 || frontId == 8 /*flowing*/|| frontId == 9) {
+ if (y == 24) {
+ OffsetPoint pos;
+ if (x != 0) {
+ pos = frontPoints.get((x-1)*25+y);
+ } else {
+ pos = frontPoints.get((x+1) * 25 +y);
+ }
+
+ int id = Block.getIdFromBlock(pos.getBlock(getDungeonRoom()));
+ int data= pos.getData(getDungeonRoom());
+ waterNodeEnds.put(id+":"+data, new Simulator.Pt(x,y));
+ }
+
+ nodes[y][x] = new Simulator.Node(frontId != 0 ? frontData >= 8 ? 8 : 8-frontData : 0,
+ frontId == 0 ? Simulator.NodeType.AIR :
+ y == 0 ? Simulator.NodeType.SOURCE :
+ Simulator.NodeType.WATER, false);
+ } else {
+ nodes[y][x] = new Simulator.Node(0, Simulator.NodeType.BLOCK, false);
+ }
+ }
+ }
+ if (switchfips) {
+ switchFlips.get("mainStream").add(waterNodeStart);
+ }
+ }
+ private void targetDoors() {
+ targetDoors.clear();
+ for (OffsetPoint offsetPoint : doorsClosed.getOffsetPointList()) {
+ if (offsetPoint.getBlock(getDungeonRoom()) != Blocks.air) {
+ targetDoors.add(Block.getIdFromBlock(offsetPoint.getBlock(getDungeonRoom()))+":"+offsetPoint.getData(getDungeonRoom()));
+ }
+ }
+ }
+
+ Simulator.Node[][] lastCopy = null;
+
+ private int idx = 0;
@Override
public void tick() {
super.tick();
if (!FeatureRegistry.SOLVER_WATERPUZZLE.isEnabled()) return;
if (!argumentsFulfilled) return;
try {
- waterBoard.tick();
+ buildNodes(false);
+ targetDoors();
+
+ Simulator.Node[][] copy = Simulator.clone(nodes);
+ boolean changed = !Arrays.deepEquals(lastCopy, copy);
+ lastCopy = copy;
+ if (!changed) {
+ if ((System.currentTimeMillis() - lastUnstable) > 1000)
+ lastStable = System.currentTimeMillis();
+ } else {
+ lastUnstable = System.currentTimeMillis();
+ }
+ Simulator.simulateTicks(nodes);
+ if ((System.currentTimeMillis() - lastUnstable) > 1000) {
+ if (t == null || !t.isAlive()) {
+ t = new Thread(() -> {
+ try {
+ List<Simulator.Pt> targets = targetDoors.stream().map(waterNodeEnds::get).collect(Collectors.toList());
+ List<Simulator.Pt> nonTargets = waterNodeEnds.values().stream().filter(a -> !targets.contains(a)).collect(Collectors.toList());
+
+ WaterPathfinder waterPathfinder = new WaterPathfinder(copy, targets, nonTargets, switchFlips);
+ WaterPathfinder.NodeNode nodeNode = waterPathfinder.pathfind();
+ LinkedList<WaterPathfinder.NodeNode> solution = new LinkedList<>();
+ if (nodeNode.getParentToMeAction() != null)
+ solution.addFirst(nodeNode);
+ while (nodeNode.getParent() != null) {
+ nodeNode = nodeNode.getParent();
+ if (nodeNode.getParentToMeAction() != null)
+ solution.addFirst(nodeNode);
+ }
+ this.solutionList = solution;
+ idx = 0;
+ lastStable = System.currentTimeMillis();
+ } catch (Exception e) {
+ lastCopy = null;
+ }
+ });
+ t.start();
+ }
+ }
+
+
+
+ if (solutionList != null && solutionList.size() > 0) {
+ while (System.currentTimeMillis() - lastStable > (long) (idx) * 2500 + 250) { // water flows 5 ticks/s
+ idx ++;
+ }
+ }
} catch (Exception e) {
e.printStackTrace();
}
@@ -84,33 +240,30 @@ public class RoomProcessorWaterPuzzle extends GeneralRoomProcessor {
super.drawWorld(partialTicks);
if (!FeatureRegistry.SOLVER_WATERPUZZLE.isEnabled()) return;
if (!argumentsFulfilled) return;
- if (waterBoard == null) return;
-
- Route route = waterBoard.getCurrentRoute();
- if (route != null) {
- int j = 1;
- for (int i = 0; i < route.getConditionList().size(); i++) {
- LeverState condition = route.getConditionList().get(i);
- if (condition == null) continue;
- SwitchData switchData = waterBoard.getValidSwitches().get(condition.getBlockId());
- if (switchData.getCurrentState(getDungeonRoom().getContext().getWorld()) != condition.isRequiredState()) {
-
- RenderUtils.highlightBlock(switchData.getSwitchLoc(), new Color(0,255,0,50), partialTicks, true);
- RenderUtils.drawTextAtWorld("#"+j,switchData.getSwitchLoc().getX(), switchData.getSwitchLoc().getY()+1, switchData.getSwitchLoc().getZ(), 0xFF000000,0.1f, false, false, partialTicks);
- RenderUtils.drawTextAtWorld(condition.isRequiredState() ? "on":"off",switchData.getSwitchLoc().getX(), switchData.getSwitchLoc().getY(), switchData.getSwitchLoc().getZ(), 0xFF000000,0.1f, false, false, partialTicks);
- j++;
+ for (int y = 0; y < nodes.length; y++) {
+ for (int x = 0; x < nodes[y].length; x++) {
+ Simulator.Node n = nodes[y][x];
+ if (n.getNodeType().isWater()) {
+ RenderUtils.highlightBlock(ptMapping.get(new Simulator.Pt(x,y)), new Color(0, 255, 0, 50), partialTicks, true);
}
}
- for (WaterNode node : route.getNodes()) {
- RenderUtils.highlightBlock(node.getBlockPos(), new Color(0,255,255,50), partialTicks, true);
- }
}
- List<BlockPos> targets = waterBoard.getTarget();
- if (targets != null) {
- for (BlockPos target : targets) {
- RenderUtils.highlightBlock(target, new Color(0,255,255,100), partialTicks, true);
+
+ if (solutionList.size() > 0) {
+ for (int i = idx; i < solutionList.size(); i++) {
+
+ String key = solutionList.get(i).getParentToMeAction().getKey();
+ if (!key.equals("nothing")) {
+ BlockPos pos = switchLoc.get(key);
+ // target:
+ long target = lastStable + 2500L * i;
+
+ double time = (target-System.currentTimeMillis()) / 1000.0 + 0.051;
+ RenderUtils.drawTextAtWorld(String.format("%.1f", time)+"s", pos.getX()+0.5f, pos.getY()+(i-idx)*0.5f - 0.5f, pos.getZ()+0.5f,
+ time < 0.5 ? 0xFF00FF00 : 0xFFFF5500, 0.05f, false, false, partialTicks);
+ }
}
- RenderUtils.highlightBlock(waterBoard.getToggleableMap().get("mainStream").getBlockPos(), new Color(0,255,0,255), partialTicks, true);
+
}
}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Route.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Route.java
deleted file mode 100755
index a9f73ae2..00000000
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Route.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
- * Copyright (C) 2021 cyoung06
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle;
-
-import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.nodes.WaterNodeEnd;
-import lombok.Data;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.*;
-
-@Data
-public class Route implements Cloneable, Comparable {
- private Set<WaterNode> nodes = new LinkedHashSet<WaterNode>();
- private List<LeverState> conditionList = new ArrayList<LeverState>();
- private Set<WaterNodeEnd> endingNodes = new HashSet<WaterNodeEnd>();
-
-
- private int matches = 0;
- private int stateFlops = 0;
- private int notMatches = 0;
-
- public double calculateCost() {
- return (1.0/matches) * 50 + stateFlops * 20 + notMatches * 10000;
- }
-
- @Override
- protected Route clone() {
- Route r = new Route();
- r.getNodes().addAll(nodes);
- r.getConditionList().addAll(conditionList);
- r.getEndingNodes().addAll(endingNodes);
- return r;
- }
-
- @Override
- public int compareTo(@NotNull Object o) {
- if (o instanceof Route) {
- double var0 = calculateCost();
- double var1 = ((Route)o).calculateCost();
- return Double.compare(var0, var1);
- }
- return 0;
- }
-}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Simulator.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Simulator.java
new file mode 100644
index 00000000..085abf1c
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/Simulator.java
@@ -0,0 +1,257 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+import java.util.List;
+import java.util.Scanner;
+
+public class Simulator {
+
+ @AllArgsConstructor @Getter
+ public enum NodeType {
+ BLOCK(false), AIR(false), WATER(true), SOURCE(true);
+
+ private boolean water;
+ }
+
+ @Data @AllArgsConstructor
+ public static class Node {
+ private int waterLevel;
+ private NodeType nodeType;
+ private boolean update;
+ }
+ @Getter @AllArgsConstructor @Data
+ public static class Pt {
+ private final int x, y;
+
+ public Pt up() {return new Pt(x, y-1);}
+ public Pt down() {return new Pt(x, y+1);}
+ public Pt left() {return new Pt(x-1, y);}
+ public Pt right() {return new Pt(x+1, y);}
+
+ public boolean check(int w, int h) {
+ return x < 0 || y < 0 || x >= w || y >= h;
+ }
+
+ public Node get(Node[][] nodes) {
+ if (check(nodes[0].length, nodes.length)) {
+ return new Node(0, NodeType.BLOCK, false);
+ }
+ return nodes[y][x];
+ }
+
+ public void set(Node[][] nodes, NodeType nodeType) {
+ get(nodes).nodeType = nodeType;
+ get(nodes).waterLevel = 0;
+ get(nodes).update = true;
+ }
+
+ public int getFlowDirection(Node[][] nodes) {
+ int right = 0;
+ Pt rightPt = this.right();
+ while (true) {
+ right++;
+ if (right == 8) {
+ right = 999; break;
+ }
+ if (rightPt.get(nodes).nodeType == NodeType.BLOCK) {
+ right = 999; break;
+ }
+ if (rightPt.down().get(nodes).nodeType != NodeType.BLOCK) {
+ break;
+ }
+
+ rightPt = rightPt.right();
+ }
+ int left = 0;
+
+ Pt leftPt = this.left();
+ while (true) {
+ left++;
+ if (left == 8) {
+ left = 999; break;
+ }
+ if (leftPt.get(nodes).nodeType == NodeType.BLOCK) {
+ left = 999; break;
+ }
+ if (leftPt.down().get(nodes).nodeType != NodeType.BLOCK) {
+ break;
+ }
+
+ leftPt = leftPt.left();
+ }
+
+ if (left == right) return 0;
+ if (left > right) return 1;
+ return -1;
+ }
+
+ public boolean shouldUpdate(Node[][] nodes) {
+ return get(nodes).update || right().get(nodes).update
+ || left().get(nodes).update
+ || up().get(nodes).update
+ || down().get(nodes).update;
+ }
+ }
+ // if there is waterLevel bigger coming nearby, make water
+ // if there is waterLevel bigger coming nearby, delete water
+ //
+ public static void simulateTicks(Node[][] nodes) {
+ while(simulateSingleTick(nodes));
+ }
+
+ public static Node[][] clone(Node[][] nodes) {
+ Node[][] newNodes = new Node[nodes.length][nodes[0].length];
+
+ for (int y = 0; y < nodes.length; y++) {
+ for (int x = 0; x < nodes[y].length; x++) {
+ newNodes[y][x] = new Node(nodes[y][x].waterLevel, nodes[y][x].nodeType, nodes[y][x].update);
+ }
+ }
+ return newNodes;
+ }
+
+ public static boolean doTick(Node[][] nodes, Node[][] nodesNew, Pt pt) {
+ int y = pt.y, x = pt.x;
+ Node prev = pt.get(nodes);
+ int maxWaterLv = Math.max(0, prev.nodeType == NodeType.SOURCE ? 8 : prev.waterLevel - 1);
+ if (prev.nodeType == NodeType.AIR || prev.nodeType == NodeType.WATER) {
+ if (pt.up().get(nodes).nodeType.isWater()) {
+ maxWaterLv = 8;
+ }
+ if (pt.left().get(nodes).nodeType.isWater()) {
+ boolean isSource = pt.left().get(nodes).nodeType == NodeType.SOURCE;
+ NodeType bottomLeft = pt.left().down().get(nodes).nodeType;
+ if (prev.nodeType == NodeType.WATER // if was water
+ || (pt.left().shouldUpdate(nodes) && (bottomLeft == NodeType.BLOCK || (isSource && bottomLeft != NodeType.AIR)) && pt.left().getFlowDirection(nodes) >= 0)) {
+ maxWaterLv = Math.max(maxWaterLv, pt.left().get(nodes).waterLevel - 1);
+ }
+ }
+ if (pt.right().get(nodes).nodeType.isWater()) {
+ boolean isSource = pt.right().get(nodes).nodeType == NodeType.SOURCE;
+ NodeType bottomRight = pt.right().down().get(nodes).nodeType;
+ if (prev.nodeType == NodeType.WATER
+ || (pt.right().shouldUpdate(nodes) && (bottomRight == NodeType.BLOCK || (isSource && bottomRight != NodeType.AIR)) && pt.right().getFlowDirection(nodes) <= 0))
+ maxWaterLv = Math.max(maxWaterLv, pt.right().get(nodes).waterLevel - 1);
+ }
+ }
+
+ nodesNew[y][x] = new Node(prev.waterLevel, prev.nodeType, false);
+ nodesNew[y][x].setWaterLevel(maxWaterLv);
+ if (maxWaterLv == 0 && nodesNew[y][x].nodeType == NodeType.WATER)
+ nodesNew[y][x].setNodeType(NodeType.AIR);
+ else if (maxWaterLv > 0 && nodesNew[y][x].nodeType == NodeType.AIR)
+ nodesNew[y][x].setNodeType(NodeType.WATER);
+
+ if (prev.nodeType != nodesNew[y][x].nodeType || prev.waterLevel != nodesNew[y][x].waterLevel) {
+ nodesNew[y][x].update = true;
+ }
+ return nodesNew[y][x].update;
+ }
+ public static boolean simulateSingleTick(Node[][] nodes) {
+ Node[][] nodesNew = new Node[nodes.length][nodes[0].length];
+ boolean update = false;
+ for (int y = 0; y < nodes.length; y++) {
+ for (int x = 0; x < nodes[y].length; x++) {
+ Pt pt = new Pt(x,y);
+
+
+ if (doTick(nodes, nodesNew, pt)) update =true;
+
+ if ( pt.get(nodesNew).waterLevel - pt.get(nodes).waterLevel > 0 && pt.get(nodes).nodeType == NodeType.WATER && pt.get(nodesNew).nodeType == NodeType.WATER) {
+ Node prev = pt.get(nodes);
+
+ nodes[y][x] = nodesNew[y][x];
+
+ if (!pt.left().check(nodes[0].length, nodes.length))
+ doTick(nodes, nodesNew, pt.left());
+ if (!pt.right().check(nodes[0].length, nodes.length))
+ doTick(nodes, nodesNew, pt.right());
+
+ nodes[y][x] = prev;
+ }
+ }
+ }
+
+ for (int y = 0; y < nodesNew.length; y++) {
+ for (int x = 0; x < nodesNew[y].length; x++) {
+ nodes[y][x] = nodesNew[y][x];
+ }
+ }
+ return update;
+ }
+
+ public static void print(Node[][] nodes) {
+ for (int y = 0; y < nodes.length; y++) {
+ for (int x = 0; x < nodes[y].length; x++) {
+ NodeType type = nodes[y][x].nodeType;
+ int cnt = nodes[y][x].waterLevel;
+ if (type == NodeType.BLOCK) {
+ System.out.print("X");
+ } else if (type == NodeType.AIR) {
+ System.out.print(" ");
+ } else if (type == NodeType.WATER) {
+ System.out.print(cnt);
+ } else {
+ System.out.print("W");
+ }
+ }
+ System.out.println();
+ }
+ System.out.println("-----------------");
+ }
+
+ // New waterboard simulator pog
+ public static void main(String[] args) {
+ NodeType[][] nodeTypes = {
+ {NodeType.BLOCK, NodeType.BLOCK, NodeType.BLOCK, NodeType.BLOCK, NodeType.BLOCK, NodeType.BLOCK, NodeType.AIR, NodeType.SOURCE, NodeType.BLOCK},
+ {NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR , NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR },
+ {NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR , NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR },
+ {NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR , NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR },
+ {NodeType.BLOCK, NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR , NodeType.AIR, NodeType.AIR, NodeType.AIR, NodeType.AIR }
+ };
+
+ Node[][] nodes = new Node[nodeTypes.length][nodeTypes[0].length];
+ for (int y = 0; y < nodes.length; y++) {
+ for (int x = 0; x < nodes[y].length; x++) {
+ nodes[y][x] = new Node(0, nodeTypes[y][x], false);
+ if (nodeTypes[y][x] == NodeType.SOURCE) {
+ nodes[y][x].update = true;
+ nodes[y][x].waterLevel = 8;
+ }
+ }
+ }
+ Scanner scanner = new Scanner(System.in);
+ while(true) {
+ print(nodes);
+ while(simulateSingleTick(nodes))
+ print(nodes);
+
+ int x = scanner.nextInt();
+ int y= scanner.nextInt();
+ nodes[y][x] = new Node(0,
+ nodes[y][x].nodeType == NodeType.BLOCK ?
+ NodeType.AIR : NodeType.BLOCK, true);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/SwitchData.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/SwitchData.java
deleted file mode 100755
index 881716d8..00000000
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/SwitchData.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
- * Copyright (C) 2021 cyoung06
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle;
-
-import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.nodes.WaterNodeStart;
-import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.nodes.WaterNodeToggleable;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import net.minecraft.util.BlockPos;
-import net.minecraft.world.World;
-
-@Data
-@AllArgsConstructor
-public class SwitchData {
- private WaterBoard waterBoard;
-
- private BlockPos switchLoc;
- private BlockPos blockLoc;
-
- private String blockId;
-
- public boolean getCurrentState(World w) {
- WaterNode waterNode = waterBoard.getToggleableMap().get(blockId);
- if (waterNode instanceof WaterNodeStart)
- return ((WaterNodeStart) waterNode).isTriggered(w);
- else if (waterNode instanceof WaterNodeToggleable)
- return ((WaterNodeToggleable) waterNode).isTriggered(w);
- return false;
- }
-}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterBoard.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterBoard.java
deleted file mode 100755
index eece3057..00000000
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterBoard.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
- * Copyright (C) 2021 cyoung06
- *
- * This program is free software: you can