diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index b4d11c749c..ffded49e35 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -86,6 +86,7 @@ public class WidgetID public static final int MOTHERLODE_MINE_GROUP_ID = 382; public static final int EXPERIENCE_DROP_GROUP_ID = 122; public static final int PUZZLE_BOX_GROUP_ID = 306; + public static final int LIGHT_BOX_GROUP_ID = 322; public static final int NIGHTMARE_ZONE_GROUP_ID = 202; public static final int BLAST_FURNACE_GROUP_ID = 474; public static final int WORLD_MAP_GROUP_ID = 595; @@ -479,6 +480,21 @@ public class WidgetID static final int VISIBLE_BOX = 4; } + static class LightBox + { + static final int LIGHT_BOX = 1; + static final int LIGHT_BOX_WINDOW = 2; + static final int LIGHT_BULB_CONTAINER = 3; + static final int BUTTON_A = 8; + static final int BUTTON_B = 9; + static final int BUTTON_C = 10; + static final int BUTTON_D = 11; + static final int BUTTON_E = 12; + static final int BUTTON_F = 13; + static final int BUTTON_G = 14; + static final int BUTTON_H = 15; + } + static class DialogSprite { static final int SPRITE = 0; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 30a1e6b24d..2727d23b40 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -310,6 +310,19 @@ public enum WidgetInfo PUZZLE_BOX(WidgetID.PUZZLE_BOX_GROUP_ID, WidgetID.PuzzleBox.VISIBLE_BOX), + LIGHT_BOX(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BOX), + LIGHT_BOX_CONTENTS(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BULB_CONTAINER), + LIGHT_BOX_BUTTON_A(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_A), + LIGHT_BOX_BUTTON_B(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_B), + LIGHT_BOX_BUTTON_C(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_C), + LIGHT_BOX_BUTTON_D(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_D), + LIGHT_BOX_BUTTON_E(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_E), + LIGHT_BOX_BUTTON_F(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_F), + LIGHT_BOX_BUTTON_G(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_G), + LIGHT_BOX_BUTTON_H(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.BUTTON_H), + + LIGHT_BOX_WINDOW(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BOX_WINDOW), + NIGHTMARE_ZONE(WidgetID.NIGHTMARE_ZONE_GROUP_ID, 1), RAIDS_POINTS_INFOBOX(WidgetID.RAIDS_GROUP_ID, WidgetID.Raids.POINTS_INFOBOX), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/PuzzleSolverPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/PuzzleSolverPlugin.java index 12329700d3..0484ce6c37 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/PuzzleSolverPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/PuzzleSolverPlugin.java @@ -25,16 +25,40 @@ */ package net.runelite.client.plugins.puzzlesolver; +import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; +import java.util.Arrays; import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_A; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_B; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_C; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_D; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_E; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_F; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_G; +import static net.runelite.api.widgets.WidgetInfo.LIGHT_BOX_BUTTON_H; +import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import net.runelite.client.config.ConfigManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.puzzlesolver.lightbox.Combination; +import net.runelite.client.plugins.puzzlesolver.lightbox.LightBox; +import net.runelite.client.plugins.puzzlesolver.lightbox.LightboxSolution; +import net.runelite.client.plugins.puzzlesolver.lightbox.LightboxSolver; +import net.runelite.client.plugins.puzzlesolver.lightbox.LightboxState; import net.runelite.client.ui.overlay.OverlayManager; @PluginDescriptor( name = "Puzzle Solver" ) +@Slf4j public class PuzzleSolverPlugin extends Plugin { @Inject @@ -43,11 +67,13 @@ public class PuzzleSolverPlugin extends Plugin @Inject private PuzzleSolverOverlay overlay; - @Provides - PuzzleSolverConfig provideConfig(ConfigManager configManager) - { - return configManager.getConfig(PuzzleSolverConfig.class); - } + @Inject + private Client client; + + private LightboxState lightbox; + private LightboxState[] changes = new LightboxState[LightBox.COMBINATIONS_POWER]; + private Combination lastClick; + private boolean lastClickInvalid; @Override protected void startUp() throws Exception @@ -60,4 +86,156 @@ public class PuzzleSolverPlugin extends Plugin { overlayManager.remove(overlay); } + + @Provides + PuzzleSolverConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(PuzzleSolverConfig.class); + } + + @Subscribe + public void onWidgetClicked(MenuOptionClicked menuOptionClicked) + { + int widgetId = menuOptionClicked.getWidgetId(); + if (TO_GROUP(widgetId) != WidgetID.LIGHT_BOX_GROUP_ID) + { + return; + } + + Combination combination; + if (widgetId == LIGHT_BOX_BUTTON_A.getId()) + { + combination = Combination.A; + } + else if (widgetId == LIGHT_BOX_BUTTON_B.getId()) + { + combination = Combination.B; + } + else if (widgetId == LIGHT_BOX_BUTTON_C.getId()) + { + combination = Combination.C; + } + else if (widgetId == LIGHT_BOX_BUTTON_D.getId()) + { + combination = Combination.D; + } + else if (widgetId == LIGHT_BOX_BUTTON_E.getId()) + { + combination = Combination.E; + } + else if (widgetId == LIGHT_BOX_BUTTON_F.getId()) + { + combination = Combination.F; + } + else if (widgetId == LIGHT_BOX_BUTTON_G.getId()) + { + combination = Combination.G; + } + else if (widgetId == LIGHT_BOX_BUTTON_H.getId()) + { + combination = Combination.H; + } + else + { + return; + } + + if (lastClick != null) + { + lastClickInvalid = true; + } + else + { + lastClick = combination; + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + Widget lightboxWidget = client.getWidget(WidgetInfo.LIGHT_BOX_CONTENTS); + if (lightboxWidget == null) + { + if (lightbox != null) + { + lastClick = null; + lastClickInvalid = false; + lightbox = null; + Arrays.fill(changes, null); + } + return; + } + + // get current state from widget + LightboxState lightboxState = new LightboxState(); + int index = 0; + for (Widget light : lightboxWidget.getDynamicChildren()) + { + boolean lit = light.getItemId() == LightBox.LIGHT_BULB_ON; + lightboxState.setState(index / LightBox.WIDTH, index % LightBox.HEIGHT, lit); + index++; + } + + if (lightboxState.equals(lightbox)) + { + return; // no change + } + + log.debug("Lightbox changed!"); + + LightboxState prev = lightbox; + lightbox = lightboxState; + + if (lastClick == null || lastClickInvalid) + { + lastClick = null; + lastClickInvalid = false; + return; + } + + LightboxState diff = lightboxState.diff(prev); + changes[lastClick.ordinal()] = diff; + + log.debug("Recorded diff for {}", lastClick); + lastClick = null; + + // try to solve + LightboxSolver solver = new LightboxSolver(); + solver.setInitial(lightbox); + int idx = 0; + for (LightboxState state : changes) + { + if (state != null) + { + Combination combination = Combination.values()[idx]; + solver.setSwitchChange(combination, state); + } + ++idx; + } + + LightboxSolution solution = solver.solve(); + if (solution != null) + { + log.debug("Got solution: {}", solution); + } + + // Set solution to title + Widget lightbox = client.getWidget(WidgetInfo.LIGHT_BOX); + if (lightbox != null) + { + Widget title = lightbox.getChild(1); + if (solution != null && solution.numMoves() > 0) + { + title.setText("Light box - Solution: " + solution); + } + else if (solution != null) + { + title.setText("Light box - Solution: solved!"); + } + else + { + title.setText("Light box - Solution: unknown"); + } + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/Combination.java b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/Combination.java new file mode 100644 index 0000000000..5ed1afd0ea --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/Combination.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.puzzlesolver.lightbox; + +public enum Combination +{ + A, B, C, D, E, F, G, H; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightBox.java new file mode 100644 index 0000000000..8a17deff13 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightBox.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.puzzlesolver.lightbox; + +public class LightBox +{ + public static final int WIDTH = 5; + public static final int HEIGHT = 5; + public static final int COMBINATIONS_POWER = 8; + public static final int LIGHT_BULB_ON = 20357; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolution.java b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolution.java new file mode 100644 index 0000000000..bba03c6315 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolution.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.puzzlesolver.lightbox; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class LightboxSolution +{ + private int solution; + + public void flip(Combination c) + { + solution ^= (1 << c.ordinal()); + } + + public int numMoves() + { + int count = 0; + int cur = solution; + for (int i = 0; i < Combination.values().length; ++i) + { + count += cur & 1; + cur >>= 1; + } + return count; + } + + @Override + public String toString() + { + StringBuilder stringBuilder = new StringBuilder(); + for (Combination combination : Combination.values()) + { + if (((solution >>> combination.ordinal()) & 1) != 0) + { + stringBuilder.append(combination.name()); + } + } + return stringBuilder.toString(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolver.java b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolver.java new file mode 100644 index 0000000000..e7aae71dcc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolver.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.puzzlesolver.lightbox; + +public class LightboxSolver +{ + private LightboxState initial; + private final LightboxState[] switches = new LightboxState[LightBox.COMBINATIONS_POWER]; + + static boolean isBitSet(int num, int bit) + { + return ((num >>> bit) & 1) != 0; + } + + private static boolean isSolved(LightboxState s) + { + for (int i = 0; i < LightBox.WIDTH; ++i) + { + for (int j = 0; j < LightBox.HEIGHT; ++j) + { + if (!s.getState(i, j)) + { + return false; + } + } + } + return true; + } + + public LightboxSolution solve() + { + LightboxSolution solution = null; + outer: + for (int i = 0; i < Math.pow(2, LightBox.COMBINATIONS_POWER); ++i) + { + LightboxState s = initial; + + for (int bit = 0; bit < LightBox.COMBINATIONS_POWER; ++bit) + { + if (isBitSet(i, bit)) + { + // this switch is unknown, so this can't be a valid answer + if (switches[bit] == null) + { + continue outer; + } + + s = s.diff(switches[bit]); + } + } + + if (isSolved(s)) + { + LightboxSolution sol = new LightboxSolution(i); + if (solution == null || sol.numMoves() < solution.numMoves()) + { + solution = sol; + } + } + } + + return solution; + } + + public void setInitial(LightboxState initial) + { + this.initial = initial; + } + + public void setSwitchChange(Combination combination, LightboxState newState) + { + switches[combination.ordinal()] = newState; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxState.java b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxState.java new file mode 100644 index 0000000000..df690c1b74 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxState.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.puzzlesolver.lightbox; + +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode +public class LightboxState +{ + private final boolean[][] state = new boolean[LightBox.WIDTH][LightBox.HEIGHT]; + + public void setState(int x, int y, boolean s) + { + state[x][y] = s; + } + + public boolean getState(int x, int y) + { + return state[x][y]; + } + + public LightboxState diff(LightboxState other) + { + LightboxState newState = new LightboxState(); + + for (int i = 0; i < LightBox.WIDTH; ++i) + { + for (int j = 0; j < LightBox.HEIGHT; ++j) + { + newState.state[i][j] = state[i][j] ^ other.state[i][j]; + } + } + + return newState; + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolverTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolverTest.java new file mode 100644 index 0000000000..7b6fefc4f1 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/puzzlesolver/lightbox/LightboxSolverTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.puzzlesolver.lightbox; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class LightboxSolverTest +{ + private static final int[] INITIAL = new int[]{ + 1, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 0, 1, 1, 1, 0, + 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1 + }; + + private static final int[] A = new int[]{ + 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1 + }; + + private static final int[] B = new int[]{ + 0, 1, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, + 1, 1, 0, 1, 0, + 0, 0, 0, 1, 1, + }; + + private static final int[] C = new int[]{ + 0, 1, 0, 0, 0, + 1, 0, 0, 0, 1, + 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, + }; + + private static final int[] D = new int[]{ + 1, 1, 0, 0, 0, + 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, + 0, 1, 1, 0, 0, + 1, 0, 0, 1, 1, + }; + + private static final int[] E = new int[]{ + 1, 0, 0, 1, 0, + 1, 1, 1, 0, 1, + 1, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + 1, 0, 0, 1, 1, + }; + + private static final int[] F = new int[]{ + 1, 0, 0, 1, 0, + 1, 0, 0, 0, 1, + 1, 0, 1, 1, 0, + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, + }; + + private static final int[] G = new int[]{ + 1, 0, 0, 1, 1, + 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, + 1, 1, 0, 0, 0, + }; + + private static final int[] H = new int[]{ + 1, 0, 1, 1, 1, + 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, + 1, 1, 0, 0, 1, + 1, 0, 0, 1, 0 + }; + + private static LightboxState fromArray(int[] array) + { + LightboxState s = new LightboxState(); + + assert array.length == 25; + for (int i = 0; i < array.length; ++i) + { + s.setState(i / 5, i % 5, array[i] != 0); + } + + return s; + } + + @Test + public void test() + { + LightboxSolver solver = new LightboxSolver(); + + solver.setInitial(fromArray(INITIAL)); + solver.setSwitchChange(Combination.A, fromArray(A)); + solver.setSwitchChange(Combination.B, fromArray(B)); + solver.setSwitchChange(Combination.C, fromArray(C)); + solver.setSwitchChange(Combination.D, fromArray(D)); + solver.setSwitchChange(Combination.E, fromArray(E)); + solver.setSwitchChange(Combination.F, fromArray(F)); + solver.setSwitchChange(Combination.G, fromArray(G)); + solver.setSwitchChange(Combination.H, fromArray(H)); + + LightboxSolution solution = solver.solve(); + + LightboxSolution expected = new LightboxSolution(); + expected.flip(Combination.A); + expected.flip(Combination.B); + expected.flip(Combination.D); + expected.flip(Combination.E); + expected.flip(Combination.F); + expected.flip(Combination.G); + + assertEquals(expected, solution); + } +} \ No newline at end of file