diff options
author | syeyoung <42869671+cyoung06@users.noreply.github.com> | 2023-10-29 01:19:24 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-29 01:19:24 +0900 |
commit | 9e3e93a3337989d7f669678a495866b38fdf3f89 (patch) | |
tree | 2194ffde48827fc9ab38659a0d566c2c796dd3c6 /mod/src/main/java/kr/syeyoung/dungeonsguide | |
parent | ee521c8a3811d46a816cada7256bb006738b1a21 (diff) | |
download | Skyblock-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>
Diffstat (limited to 'mod/src/main/java/kr/syeyoung/dungeonsguide')
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 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.dungeon.data.OffsetPoint; -import kr.syeyoung.dungeonsguide.dungeon.data.OffsetPointSet; -import kr.syeyoung.dungeonsguide.mod.DungeonsGuide; -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.nodes.*; -import lombok.Getter; -import net.minecraft.block.Block; -import net.minecraft.block.BlockLever; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.Tuple; -import net.minecraft.world.World; - -import java.util.*; - -public class WaterBoard { - @Getter - WaterNode[][] board; - RoomProcessorWaterPuzzle waterPuzzle; - - private final OffsetPointSet frontPlate; - private final OffsetPointSet backPlate; - private final OffsetPointSet levers; - private final OffsetPointSet doors; - private final OffsetPoint lever; - - @Getter - private final List<SwitchData> switchData = new ArrayList<SwitchData>(); - @Getter - private final Map<String, SwitchData> validSwitches = new HashMap<String, SwitchData>(); - - private WaterNodeStart waterNodeStart; - private final Map<String, WaterNodeEnd> waterNodeEndMap = new HashMap<String, WaterNodeEnd>(); - - @Getter - private final Map<String, WaterNode> toggleableMap = new HashMap<String, WaterNode>(); - - @Getter - private Set<String> reqOpen = new HashSet<String>(); - @Getter - private Route currentRoute; - @Getter - private List<BlockPos> target; - @Getter - private List<String> target2; - - public WaterBoard(RoomProcessorWaterPuzzle roomProcessorWaterPuzzle, OffsetPointSet frontPlate, OffsetPointSet backPlate, OffsetPointSet levers, OffsetPointSet doors, OffsetPoint leverMain) { - this.waterPuzzle = roomProcessorWaterPuzzle; - this.frontPlate = frontPlate; - this.backPlate = backPlate; - this.levers = levers; - this.doors = doors; - this.lever = leverMain; - - buildLeverStates(); - buildNodes(); - } - - private void buildLeverStates(){ - for (OffsetPoint offsetPoint : levers.getOffsetPointList()) { - if (offsetPoint.getBlock(waterPuzzle.getDungeonRoom()) == Blocks.lever){ - BlockPos pos = offsetPoint.getBlockPos(waterPuzzle.getDungeonRoom()); - World w= waterPuzzle.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); - - SwitchData sw; - switchData.add(sw = new SwitchData(this, pos,newPos,id+":"+data)); - validSwitches.put(id+":"+data, sw); - } - } - SwitchData sw; - switchData.add(sw = new SwitchData(this, lever.getBlockPos(waterPuzzle.getDungeonRoom()),lever.getBlockPos(waterPuzzle.getDungeonRoom()).add(0,-1,0),"mainStream")); - validSwitches.put("mainStream", sw); - } - - public void tick() { - Set<String> doorsToOpen = new HashSet<String>(); - for (OffsetPoint offsetPoint : doors.getOffsetPointList()) { - Block b =offsetPoint.getBlock(waterPuzzle.getDungeonRoom()); - if (b != Blocks.air) { - doorsToOpen.add(Block.getIdFromBlock(b)+":"+offsetPoint.getData(waterPuzzle.getDungeonRoom())); - } - } -// if (!(reqOpen.containsAll(doorsToOpen) && doorsToOpen.containsAll(reqOpen))) { - reqOpen = doorsToOpen; - if (doorsToOpen.size() != 0) { - Set<WaterNodeEnd> ends = new HashSet<WaterNodeEnd>(); - for (String s : doorsToOpen) { - ends.add(waterNodeEndMap.get(s)); - } - currentRoute = getBestRoute(ends); -// { -// -// Set<LeverState> currentState = new HashSet<LeverState>(); -// World w = waterPuzzle.getDungeonRoom().getContext().getWorld(); -// for (SwitchData switchDatum : this.switchData) { -// currentState.add(new LeverState(switchDatum.getBlockId(), switchDatum.getCurrentState(w))); -// } -// currentRoute = simulate(currentState); -// } - - target = new ArrayList<BlockPos>(); - target2 = new ArrayList<String>(); - if (currentRoute != null) { - for (WaterNodeEnd endingNode : currentRoute.getEndingNodes()) { - target.add(endingNode.getBlockPos()); - target2.add(endingNode.getResultId()); - } - } - } -// } - } - - public Route getBestRoute(Set<WaterNodeEnd> potentialEnds) { - int totalStates = (int) Math.pow(2, validSwitches.size() - 1); - List<SwitchData> switchData = new ArrayList<SwitchData>(); - Set<LeverState> currentState = new HashSet<LeverState>(); - World w = waterPuzzle.getDungeonRoom().getContext().getWorld(); - for (SwitchData switchDatum : this.switchData) { - if (!switchDatum.getBlockId().equals("mainStream")) { - switchData.add(switchDatum); - } - currentState.add(new LeverState(switchDatum.getBlockId(), switchDatum.getCurrentState(w))); - } - PriorityQueue<Route> routes = new PriorityQueue<Route>(); - - for (int i = 0; i < totalStates; i++) { - Set<LeverState> states = new HashSet<LeverState>(); - for (int i1 = 0; i1 < switchData.size(); i1++) { - states.add(new LeverState(switchData.get(i1).getBlockId(), ((i >> i1) & 0x1) > 0)); - } - states.add(new LeverState("mainStream", true)); - - Route r = simulate(states); - - for (LeverState leverState : currentState) { - if (!states.contains(leverState)) - r.setStateFlops(r.getStateFlops() + 1); - } - for (WaterNodeEnd potentialEnd : r.getEndingNodes()) { - if (potentialEnds.contains(potentialEnd)) { - r.setMatches(r.getMatches() + 1); - } else { - r.setNotMatches(r.getNotMatches() + 1); - } - } - if (r.getMatches() > 0) - routes.add(r); - } - - - return routes.peek(); - } - - public Route simulate(Set<LeverState> leverStates) { - leverStates.add(null); - Route r = new Route(); - final Queue<WaterNode> toGoDownTo = new LinkedList<WaterNode>(); - Set<WaterNode> searched = new HashSet<WaterNode>(); - Set<LeverState> waterBlockingStates = new HashSet<LeverState>(); - World w = waterPuzzle.getDungeonRoom().getContext().getWorld(); -// toGoDownTo.add(getNodeAt(waterNodeStart.getX(), waterNodeStart.getY() + 1)); - { - Queue<Tuple<WaterNode, Boolean>> toGo = new LinkedList<>(); - toGo.add(new Tuple<>(waterNodeStart, true)); - toGoDownTo.add(getNodeAt(waterNodeStart.getX(), waterNodeStart.getY() + 1)); - Set<WaterNode> visited = new HashSet<>(); - while (!toGo.isEmpty()) { - Tuple<WaterNode, Boolean> waterNode = toGo.poll(); - if (waterNode.getFirst() == null) continue; - if (visited.contains(waterNode.getFirst())) continue; - if (!waterNode.getFirst().canWaterGoThrough()) continue; - if (waterNode.getFirst() instanceof WaterNodeEnd) continue; - visited.add(waterNode.getFirst()); - - boolean water = waterNode.getFirst().isWaterFilled(w); - if (water && !waterNode.getSecond()) { - toGoDownTo.add(getNodeAt(waterNode.getFirst().getX(), waterNode.getFirst().getY())); - } - - int x = waterNode.getFirst().getX(), y = waterNode.getFirst().getY(); - toGo.add(new Tuple<>(getNodeAt(x+1, y), water)); - toGo.add(new Tuple<>(getNodeAt(x-1, y), water)); - toGo.add(new Tuple<>(getNodeAt(x, y+1), water)); - } - } - while (!toGoDownTo.isEmpty()) { - WaterNode asd = toGoDownTo.poll(); - if (asd == null) continue; - if (searched.contains(asd)) continue; - searched.add(asd); - - if (asd instanceof WaterNodeEnd) { - if (!asd.isWaterFilled(w)) - r.getEndingNodes().add((WaterNodeEnd) asd); - continue; - } - - r.getNodes().add(asd); - - if (asd.isWaterFilled(w) && ( - (getNodeAt(asd.getX() + 1, asd.getY()) != null && getNodeAt(asd.getX() + 1, asd.getY()).isWaterFilled(w)) - || (getNodeAt(asd.getX() - 1, asd.getY()) != null && getNodeAt(asd.getX() - 1, asd.getY()).isWaterFilled(w)))) { - boolean followWater = getNodeAt(asd.getX() - 1, asd.getY()) != null && leverStates.contains(getNodeAt(asd.getX() - 1, asd.getY()).getCondition()) - && getNodeAt(asd.getX() - 2, asd.getY()) != null && leverStates.contains(getNodeAt(asd.getX() - 2, asd.getY()).getCondition()); - for (int i = asd.getX(); i < asd.getX() + 8; i++) { - WaterNode nodeHere = getNodeAt(i, asd.getY()); - if (nodeHere == null) break; - if (followWater && !nodeHere.isWaterFilled(w)) break; - if (!nodeHere.canWaterGoThrough()) break; - if (!leverStates.contains(nodeHere.getCondition()) && !nodeHere.isWaterFilled(w)) break; - if (nodeHere.getCondition() != null && leverStates.contains(nodeHere.getCondition().invert()) && nodeHere.isWaterFilled(w)) waterBlockingStates.add(nodeHere.getCondition().invert()); - WaterNode down = getNodeAt(i, asd.getY() + 1); - if (i != asd.getX()) - followWater = nodeHere.isWaterFilled(w) && (down == null || (down.canWaterGoThrough() && leverStates.contains(down.getCondition()))); - r.getNodes().add(nodeHere); - if (down != null && down.canWaterGoThrough() && down.getCondition() != null && leverStates.contains(down.getCondition().invert())) { - waterBlockingStates.add(down.getCondition().invert()); - } - if (down != null && ((down.canWaterGoThrough() && leverStates.contains(down.getCondition())) || down.isWaterFilled(w))) { - toGoDownTo.add(down); - if (!followWater) break; - } - } - followWater = getNodeAt(asd.getX() +1, asd.getY()) != null && leverStates.contains(getNodeAt(asd.getX() + 1, asd.getY()).getCondition()) - && getNodeAt(asd.getX() +2, asd.getY()) != null && leverStates.contains(getNodeAt(asd.getX() + 2, asd.getY()).getCondition()); - for (int i = asd.getX(); i > asd.getX() - 8; i--) { - WaterNode nodeHere = getNodeAt(i, asd.getY()); - if (nodeHere == null) break; - if (followWater && !nodeHere.isWaterFilled(w)) break; - if (!nodeHere.canWaterGoThrough()) break; - if (!leverStates.contains(nodeHere.getCondition()) && !nodeHere.isWaterFilled(w)) break; - if (nodeHere.getCondition() != null && leverStates.contains(nodeHere.getCondition().invert()) && nodeHere.isWaterFilled(w)) waterBlockingStates.add(nodeHere.getCondition().invert()); - WaterNode down = getNodeAt(i, asd.getY() + 1); - if (i != asd.getX()) - followWater = nodeHere.isWaterFilled(w) && (down == null || (down.canWaterGoThrough() && leverStates.contains(down.getCondition()))); - r.getNodes().add(nodeHere); - if (down != null && down.canWaterGoThrough() && down.getCondition() != null && leverStates.contains(down.getCondition().invert())) { - waterBlockingStates.add(down.getCondition().invert()); - } - if (down != null && ((down.canWaterGoThrough() && leverStates.contains(down.getCondition())) || down.isWaterFilled(w))) { - toGoDownTo.add(down); - if (!followWater) break; - } - } - } else { - int minDistToDropRight = 9999; - for (int i = asd.getX(); i < asd.getX() + 8; i++) { - WaterNode nodeHere = getNodeAt(i, asd.getY()); - if (nodeHere == null) break; - if (!nodeHere.canWaterGoThrough()) break; - if (!leverStates.contains(nodeHere.getCondition()) && !nodeHere.isWaterFilled(w)) break; - WaterNode down = getNodeAt(i, asd.getY() + 1); - if (down != null && ((down.canWaterGoThrough() && leverStates.contains(down.getCondition())))) { - int dist = i - asd.getX(); - if (dist < minDistToDropRight) - minDistToDropRight = dist; - break; - } - } - int minDistToDropLeft = 9999; - for (int i = asd.getX(); i > asd.getX() - 8; i--) { - WaterNode nodeHere = getNodeAt(i, asd.getY()); - if (nodeHere == null) break; - if (!nodeHere.canWaterGoThrough()) break; - if (!leverStates.contains(nodeHere.getCondition()) && !nodeHere.isWaterFilled(w)) break; - WaterNode down = getNodeAt(i, asd.getY() + 1); - if (down != null && ((down.canWaterGoThrough() && leverStates.contains(down.getCondition())))) { - int dist = asd.getX() - i; - if (dist < minDistToDropLeft) - minDistToDropLeft = dist; - break; - } - } - - int min = Math.min(minDistToDropRight, minDistToDropLeft); - if (min == 9999) continue; - if (minDistToDropRight == min) { - for (int i = asd.getX(); i <= asd.getX() + minDistToDropRight; i++) { - WaterNode nodeHere = getNodeAt(i, asd.getY()); - if (nodeHere.getCondition() != null && leverStates.contains(nodeHere.getCondition().invert()) && nodeHere.isWaterFilled(w)) waterBlockingStates.add(nodeHere.getCondition().invert()); - r.getNodes().add(nodeHere); - WaterNode down = getNodeAt(i, asd.getY() + 1); - if (down != null && ((down.canWaterGoThrough() && leverStates.contains(down.getCondition())) || down.isWaterFilled(w))) { - toGoDownTo.add(down); - } - if (down != null && down.canWaterGoThrough() && down.getCondition() != null && leverStates.contains(down.getCondition().invert())) { - waterBlockingStates.add(down.getCondition().invert()); - } - } - } - if (minDistToDropLeft == min) { - for (int i = asd.getX(); i >= asd.getX() - minDistToDropLeft; i--) { - WaterNode nodeHere = getNodeAt(i, asd.getY()); - if (nodeHere.getCondition() != null && leverStates.contains(nodeHere.getCondition().invert()) && nodeHere.isWaterFilled(w)) waterBlockingStates.add(nodeHere.getCondition().invert()); - r.getNodes().add(nodeHere); - WaterNode down = getNodeAt(i, asd.getY() + 1); - if (down != null && ((down.canWaterGoThrough() && leverStates.contains(down.getCondition())) || down.isWaterFilled(w))) { - toGoDownTo.add(down); - } - if (down != null && down.canWaterGoThrough() && down.getCondition() != null && leverStates.contains(down.getCondition().invert())) { - waterBlockingStates.add(down.getCondition().invert()); - } - } - } - } - } - ArrayList<LeverState> state = new ArrayList<LeverState>(waterBlockingStates); - state.remove(null); - Collections.sort(state, new Comparator<LeverState>() { - @Override - public int compare(LeverState leverState, LeverState t1) { - int var0 = toggleableMap.get(leverState.getBlockId()).getY(); - int var1 = toggleableMap.get(t1.getBlockId()).getY(); - return var0 < var1 ? -1 : (var0 == var1 ? 0 : 1); - } - }); - LinkedList<LeverState> states = new LinkedList<LeverState>(state); - for (LeverState ls : leverStates) { - if (!states.contains(ls)) { - states.add(ls); - } - } - states.remove(null); - - - r.setConditionList(states); - return r; - } - - - public WaterNode getNodeAt(int x, int y) { - if (x < 0 || y < 0) return null; - if (x >= board[0].length || y >= board.length) return null; - return board[y][x]; - } - - private void buildNodes() { - List<OffsetPoint> frontPoints = frontPlate.getOffsetPointList(); - List<OffsetPoint> backPoints = backPlate.getOffsetPointList(); - - board = new WaterNode[25][19]; - 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); - int frontId = Block.getIdFromBlock(front.getBlock(waterPuzzle.getDungeonRoom())); - int backId = Block.getIdFromBlock(back.getBlock(waterPuzzle.getDungeonRoom())); - int frontData = front.getData(waterPuzzle.getDungeonRoom()); - int backData = back.getData(waterPuzzle.getDungeonRoom()); - WaterNode node; - if (validSwitches.containsKey(backId +":"+backData)) { - String resId = backId + ":"+backData; - node = new WaterNodeToggleable(resId, isSwitchActive(validSwitches.get(resId)), front.getBlockPos(waterPuzzle.getDungeonRoom()),x,y); - - toggleableMap.put(resId, node); - } else if (validSwitches.containsKey(frontId +":"+frontData)) { - String resId = frontId +":"+frontData; - node = new WaterNodeToggleable(resId, !isSwitchActive(validSwitches.get(resId)), front.getBlockPos(waterPuzzle.getDungeonRoom()),x,y); - - toggleableMap.put(resId, node); - } else if (frontId == 0 || frontId == 8 || 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(waterPuzzle.getDungeonRoom())); - int data= pos.getData(waterPuzzle.getDungeonRoom()); - node = new WaterNodeEnd(id+":"+data, front.getBlockPos(waterPuzzle.getDungeonRoom()),x,y); - waterNodeEndMap.put(id+":"+data, (WaterNodeEnd) node); - } else if (y == 2 && x == 9) { - waterNodeStart = (WaterNodeStart) (node = new WaterNodeStart(front.getBlockPos(waterPuzzle.getDungeonRoom()), - frontId != 0 ^ isSwitchActive(validSwitches.get("mainStream")),x,y)); - } else { - node = new WaterNodeAir(front.getBlockPos(waterPuzzle.getDungeonRoom()),x,y); - } - } else { - node = new WaterNodeWall(front.getBlockPos(waterPuzzle.getDungeonRoom()),x,y); - } - board[y][x] =node; - } - } - toggleableMap.put("mainStream", waterNodeStart); - } - - private boolean isSwitchActive(SwitchData switchData) { - BlockPos switch2 = switchData.getSwitchLoc(); - World w= waterPuzzle.getDungeonRoom().getContext().getWorld(); - boolean bool = DungeonsGuide.getDungeonsGuide().getBlockCache().getBlockState(switch2).getValue(BlockLever.POWERED); - return bool; - } - - -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterNode.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterNode.java deleted file mode 100755 index cbcb1cb0..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterNode.java +++ /dev/null @@ -1,36 +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 net.minecraft.util.BlockPos; -import net.minecraft.world.World; - -public interface WaterNode { - boolean canWaterGoThrough(); - - // condition for water go - LeverState getCondition(); - - boolean isWaterFilled(World w); - - BlockPos getBlockPos(); - - int getX(); - int getY(); -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterPathfinder.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterPathfinder.java new file mode 100644 index 00000000..92b08bd2 --- /dev/null +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/WaterPathfinder.java @@ -0,0 +1,204 @@ +/* + * 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.*; + +public class WaterPathfinder { + // A* on waterboard... lol graph search + Set<AdvanceAction> availableActions = new HashSet<>(); + + + private State begin; + private Map<State, NodeNode> mapping = new HashMap<>(); + + private List<Simulator.Pt> targets = new ArrayList<>(); + private List<Simulator.Pt> nonTargets = new ArrayList<>(); + private int maxMatch = 0; + + public WaterPathfinder(Simulator.Node[][] begin, List<Simulator.Pt> targets, List<Simulator.Pt> nonTargets, Map<String, List<Simulator.Pt>> switchFlips) { + this.targets = targets; + this.nonTargets = nonTargets; + + ArrayList<Simulator.Pt> total = new ArrayList(); + total.addAll(targets); + total.addAll(nonTargets); + maxMatch = total.size(); + for (Map.Entry<String, List<Simulator.Pt>> stringListEntry : switchFlips.entrySet()) { + availableActions.add(new AdvanceAction(stringListEntry.getValue(), stringListEntry.getKey(), 10, total, + 10)); + } + + availableActions.add(new AdvanceAction(new ArrayList<>(), "nothing", 1, total, 10)); + this.begin = new State(begin, new int[total.size()]); + + } + + private float fScore(NodeNode node) { + int cnt = 0; + for (int i = 0; i < node.state.flips.length; i++) { + if ((node.state.flips[i] % 2 == 1) != (i < targets.size())) { + cnt += 1; + } + } +// +// int smh = node.state.state.length; +// label: for (int i = node.state.state.length-1; i >= 0; i--) { +// for (int x = 0; x < node.state.state[i].length; x++) { +// if (node.state.state[i][x].getNodeType().isWater()) { +// smh = i; +// break label; +// } +// } +// } +// return (cnt) * 20; + return cnt * 200; // dijkstra lol + } + public NodeNode pathfind() { + PriorityQueue<NodeNode> nodes = new PriorityQueue<>(Comparator.comparingDouble((NodeNode a) -> a.f).thenComparing(a -> a.g).thenComparing(a->Arrays.deepHashCode(a.state.state)).thenComparing(NodeNode::hashCode)); + NodeNode start = openNode(begin); + start.f = fScore(start); + start.g = 0; + nodes.add(start); + while(!nodes.isEmpty()) { + NodeNode node = nodes.poll(); + if (fScore(node) == 0) return node; +// System.out.println(nodes.size()); + + + for (AdvanceAction availableAction : availableActions) { + State newState = availableAction.generateNew(node.state); + if (newState == null) continue; + NodeNode newNodeNode = openNode(newState); + + float newG = node.g + availableAction.cost; + if (newNodeNode.g > newG) { + newNodeNode.g = newG; + newNodeNode.f = newNodeNode.g + fScore(newNodeNode) ;// heuristic + newNodeNode.parent = node; + newNodeNode.parentToMeAction = availableAction; + + nodes.add(newNodeNode); + } + } + } + return null; + } + + public NodeNode openNode(State state) { + if (mapping.containsKey(state)) { + return mapping.get(state); + } + NodeNode nodeNode = new NodeNode(state, null, null, Float.MAX_VALUE, Float.MAX_VALUE); + mapping.put(state, nodeNode); + return nodeNode; + } + + @AllArgsConstructor @Getter + public static class State { + private final Simulator.Node[][] state; + private final int[] flips; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + State state1 = (State) o; + + return Arrays.deepEquals(state, state1.state) && Arrays.equals(flips, state1.flips); + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(state) << 31 | Arrays.hashCode(flips); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int y = 0; y < state.length; y++) { + for (int x = 0; x < state[y].length; x++) { + Simulator.NodeType type = state[y][x].getNodeType(); + int cnt = state[y][x].getWaterLevel(); + if (type == Simulator.NodeType.BLOCK) { + sb.append("X"); + } else if (type == Simulator.NodeType.AIR) { + sb.append(" "); + } else if (type == Simulator.NodeType.WATER) { + sb.append(cnt); + } else { + sb.append("W"); + } + } + sb.append("\n"); + } + return sb.toString(); + } + } + + @Data @AllArgsConstructor + public static class NodeNode { + private State state; + private NodeNode parent; + private AdvanceAction parentToMeAction; + + private float f, g; + // dist curr + } + + + @Getter @AllArgsConstructor + public static class AdvanceAction { + private final List<Simulator.Pt> flips; + private final String key; + private final float cost; + private final List<Simulator.Pt> targets; + private final int moves; + + public State generateNew(State from) { + Simulator.Node[][] nodes = Simulator.clone(from.state); + for (Simulator.Pt flip : flips) { + Simulator.Node node = flip.get(nodes); + if (node.getNodeType() == Simulator.NodeType.BLOCK) flip.set(nodes, Simulator.NodeType.AIR); + else flip.set(nodes, Simulator.NodeType.BLOCK); + } + Simulator.simulateSingleTick(nodes); + for (int i = 0; i < moves; i++) { + Simulator.simulateSingleTick(nodes); + } +// if (1==1) return null; + + int[] newFlips = Arrays.copyOf(from.flips, from.flips.length); + + for (int i = 0; i < targets.size(); i++) { + Simulator.Pt target = targets.get(i); + if (!target.get(from.state).getNodeType().isWater() && target.get(nodes).getNodeType().isWater()) { + newFlips[i]++; + } + } + + return new State(nodes, newFlips); + } + } +} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeAir.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeAir.java deleted file mode 100755 index ffb8b65a..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeAir.java +++ /dev/null @@ -1,59 +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.nodes; - -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.LeverState; -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.WaterNode; -import lombok.AllArgsConstructor; -import lombok.Data; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.world.World; - -@Data -@AllArgsConstructor -public class WaterNodeAir implements WaterNode { - BlockPos blockPos; - @Override - public boolean canWaterGoThrough() { - return true; - } - - @Override - public LeverState getCondition() { - return null; - } - - @Override - public boolean isWaterFilled(World w) { - Block b = w.getChunkFromBlockCoords(blockPos).getBlock(blockPos); - return b == Blocks.water || b == Blocks.flowing_water; - } - - public BlockPos getBlockPos() { - return blockPos; - } - - private int x,y; - - public String toString() { - return "A"; - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeEnd.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeEnd.java deleted file mode 100755 index 22a62a00..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeEnd.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.nodes; - -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.LeverState; -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.WaterNode; -import lombok.AllArgsConstructor; -import lombok.Data; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.world.World; - -@Data -@AllArgsConstructor -public class WaterNodeEnd implements WaterNode { - private String resultId; - BlockPos blockPos; - - @Override - public boolean canWaterGoThrough() { - return true; - } - - @Override - public LeverState getCondition() { - return null; - } - - @Override - public boolean isWaterFilled(World w) { - Block b = w.getChunkFromBlockCoords(blockPos).getBlock(blockPos); - return b == Blocks.water || b == Blocks.flowing_water; - } - - public BlockPos getBlockPos() { - return blockPos; - } - private int x,y; - - public String toString() { - return "E"; - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeStart.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeStart.java deleted file mode 100755 index e067cf9b..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeStart.java +++ /dev/null @@ -1,66 +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.nodes; - -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.LeverState; -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.WaterNode; -import lombok.AllArgsConstructor; -import lombok.Data; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.world.World; - -@Data -@AllArgsConstructor -public class WaterNodeStart implements WaterNode { - - private BlockPos blockPos; - private boolean isReversed; - - @Override - public boolean canWaterGoThrough() { - return true; - } - - @Override - public LeverState getCondition() { - return new LeverState("mainStream", !isReversed); - } - - @Override - public boolean isWaterFilled(World w) { - Block b = w.getChunkFromBlockCoords(blockPos).getBlock(blockPos); - return b == Blocks.water || b == Blocks.flowing_water; - } - - public boolean isTriggered(World w) { - return isWaterFilled(w); - } - - - public BlockPos getBlockPos() { - return blockPos; - } - private int x,y; - - public String toString() { - return "S"; - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeToggleable.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeToggleable.java deleted file mode 100755 index 7235e269..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeToggleable.java +++ /dev/null @@ -1,71 +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.nodes; - -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.LeverState; -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.WaterNode; -import lombok.AllArgsConstructor; -import lombok.Data; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.world.World; - -@Data -@AllArgsConstructor -public class WaterNodeToggleable implements WaterNode { - private String blockId; - private boolean invert; - - BlockPos blockPos; - - @Override - public boolean canWaterGoThrough() { - return true; - } - - @Override - public LeverState getCondition() { - return new LeverState(blockId, invert); - } - - - @Override - public boolean isWaterFilled(World w) { - Block b = w.getChunkFromBlockCoords(blockPos).getBlock(blockPos); - return b == Blocks.water || b == Blocks.flowing_water; - } - private int x,y; - - public boolean isTriggered(World w) { - Block b= w.getChunkFromBlockCoords(blockPos).getBlock(blockPos); - - return !(b == Blocks.air || b == Blocks.water || b == Blocks.flowing_water) ^ invert; - } - - - public BlockPos getBlockPos() { - return blockPos; - } - - - public String toString() { - return "T:"+blockId+(invert ? ":Y":":N"); - } -} diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeWall.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeWall.java deleted file mode 100755 index 0f2b9a44..00000000 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/dungeon/roomprocessor/waterpuzzle/nodes/WaterNodeWall.java +++ /dev/null @@ -1,62 +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.nodes; - -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.LeverState; -import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.waterpuzzle.WaterNode; -import lombok.AllArgsConstructor; -import lombok.Data; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.world.World; - -@Data -@AllArgsConstructor -public class WaterNodeWall implements WaterNode { - - BlockPos blockPos; - @Override - public boolean canWaterGoThrough() { - return false; - } - - @Override - public LeverState getCondition() { - return null; - } - - - @Override - public boolean isWaterFilled(World w) { - Block b = w.getChunkFromBlockCoords(blockPos).getBlock(blockPos); - return b == Blocks.water || b == Blocks.flowing_water; - } - - private int x,y; - - public BlockPos getBlockPos() { - return blockPos; - } - - - public String toString() { - return "W"; - } -} |