From 921ec3e0db6a829249f17dae3090cb736da01c06 Mon Sep 17 00:00:00 2001 From: vanni <43923017+gazivodag@users.noreply.github.com> Date: Thu, 2 May 2019 06:04:49 -0400 Subject: [PATCH] Runedoku plugin (check https://github.com/gazivodag/runedoku) (#167) --- .../plugins/runedoku/RunedokuConfig.java | 84 ++++++++++++ .../plugins/runedoku/RunedokuOverlay.java | 128 ++++++++++++++++++ .../plugins/runedoku/RunedokuPiece.java | 75 ++++++++++ .../plugins/runedoku/RunedokuPlugin.java | 79 +++++++++++ .../client/plugins/runedoku/RunedokuUtil.java | 126 +++++++++++++++++ .../client/plugins/runedoku/Sudoku.java | 92 +++++++++++++ 6 files changed, 584 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPiece.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuUtil.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runedoku/Sudoku.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuConfig.java new file mode 100644 index 0000000000..5d6cab6084 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuConfig.java @@ -0,0 +1,84 @@ +package net.runelite.client.plugins.runedoku; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +import java.awt.*; + +@ConfigGroup("runedoku") +public interface RunedokuConfig extends Config { + + @ConfigItem( + position = 0, + keyName = "mindRuneColor", + name = "Mind Rune Color", + description = "Color used to highlight Mind runes." + ) + default Color mindRuneColor() { return Color.PINK; } + + @ConfigItem( + position = 1, + keyName = "fireRuneColor", + name = "Fire Rune Color", + description = "Color used to highlight Fire runes." + ) + default Color fireRuneColor() { return Color.RED; } + + @ConfigItem( + position = 2, + keyName = "bodyRuneColor", + name = "Body Rune Color", + description = "Color used to highlight Body runes." + ) + default Color bodyRuneColor() { return Color.MAGENTA; } + + @ConfigItem( + position = 3, + keyName = "airRuneColor", + name = "Air Rune Color", + description = "Color used to highlight Air runes." + ) + default Color airRuneColor() { return Color.WHITE; } + + @ConfigItem( + position = 4, + keyName = "deathRuneColor", + name = "Death Rune Color", + description = "Color used to highlight Death runes." + ) + default Color deathRuneColor() { return Color.BLACK; } + + @ConfigItem( + position = 5, + keyName = "waterRuneColor", + name = "Water Rune Color", + description = "Color used to highlight Water runes." + ) + default Color waterRuneColor() { return Color.BLUE; } + + @ConfigItem( + position = 6, + keyName = "chaosRuneColor", + name = "Chaos Rune Color", + description = "Color used to highlight Chaos runes." + ) + default Color chaosRuneColor() { return Color.YELLOW; } + + @ConfigItem( + position = 7, + keyName = "earthRuneColor", + name = "Earth Rune Color", + description = "Color used to highlight Earth runes." + ) + default Color earthRuneColor() { return Color.GREEN; } + + @ConfigItem( + position = 8, + keyName = "lawRuneColor", + name = "Law Rune Color", + description = "Color used to highlight Law runes." + ) + default Color lawRuneColor() { return Color.CYAN; } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuOverlay.java new file mode 100644 index 0000000000..2f92172386 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuOverlay.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.runedoku; + +import net.runelite.api.Client; +import net.runelite.api.widgets.Widget; +import net.runelite.client.ui.overlay.*; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; + +import javax.inject.Inject; +import java.awt.*; +import java.util.ArrayList; + +import static java.awt.Color.RED; + +/** + * @author gazivodag + */ +class RunedokuOverlay extends Overlay { + + private final RunedokuPlugin plugin; + private final Client client; + private final RunedokuUtil util; + + + @Inject + private RunedokuOverlay(final RunedokuPlugin plugin, final Client client, final RunedokuUtil util) { + super(plugin); + this.plugin = plugin; + this.client = client; + this.util = util; + + setPosition(OverlayPosition.DETACHED); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) { + + final Widget sudokuScreen = client.getWidget(288,131); + + if (sudokuScreen != null) { + if (!sudokuScreen.isHidden()) { + Sudoku sudoku = new Sudoku(util.createTable(client)); + boolean solved = sudoku.solve(); + + renderReferenceRunes(graphics, solved); + renderSolvedPuzzle(graphics, sudoku, solved); + } + + } + + return null; + } + + /** + * highlights the runes on the left handside so you know which runes to place on the board + * @param graphics + * @param solved + */ + private void renderReferenceRunes(Graphics2D graphics, boolean solved) { + //reference runes on the left handside + for (int i = 121 ; i < 130 ; i++) { + Widget widget = client.getWidget(288, i); + if (solved) { + if (!util.makeSimple(util.createTable(client)).contains(0)) { + OverlayUtil.renderPolygon(graphics, util.RectangleToPolygon(widget.getBounds()), Color.GREEN); + } else { + OverlayUtil.renderPolygon(graphics, util.RectangleToPolygon(widget.getBounds()), util.referenceColors(i)); + } + } else { + OverlayUtil.renderPolygon(graphics, util.RectangleToPolygon(widget.getBounds()), RED); + } + + } + } + + /** + * goes through each 9x9 cell and tells you which piece to place in there + * @param graphics + * @param sudoku + * @param solved + */ + private void renderSolvedPuzzle(Graphics2D graphics, Sudoku sudoku, boolean solved) { + ArrayList simpleArr = util.makeSimple(sudoku.getBoard()); + //highlight each cell to tell you which piece to place + int iteration = 0; + for (int i = 10 ; i < 91 ; i++) { + Widget squareToHighlight = client.getWidget(288, i); + if (solved) { + if (!util.makeSimple(util.createTable(client)).contains(0)) { + OverlayUtil.renderPolygon(graphics, util.RectangleToPolygon(squareToHighlight.getBounds()), Color.GREEN); + } else { + OverlayUtil.renderPolygon(graphics, util.RectangleToPolygon(squareToHighlight.getBounds()), util.sudokuPieceToColor(simpleArr.get(iteration))); + } + iteration++; + } else { + OverlayUtil.renderPolygon(graphics, util.RectangleToPolygon(squareToHighlight.getBounds()), RED); + } + + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPiece.java b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPiece.java new file mode 100644 index 0000000000..c6a79628d6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPiece.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.runedoku; + +public enum RunedokuPiece { + + NPC_PLACED_MIND_RUNE(6436, 1), //1 + NPC_PLACED_FIRE_RUNE(6428, 2), //2 + NPC_PLACED_BODY_RUNE(6438, 3), //3 + NPC_PLACED_AIR_RUNE(6422, 4), //4 + NPC_PLACED_DEATH_RUNE(6432, 5), //5 + NPC_PLACED_WATER_RUNE(6424, 6), //6 + NPC_PLACED_CHAOS_RUNE(6430, 7), //7 + NPC_PLACED_EARTH_RUNE(6426, 8), //8 + NPC_PLACED_LAW_RUNE(6434, 9), //9 + + PLAYER_PLACED_MIND_RUNE(558, 1), //1 + PLAYER_PLACED_FIRE_RUNE(554, 2), //2 + PLAYER_PLACED_BODY_RUNE(559, 3), //3 + PLAYER_PLACED_AIR_RUNE(556, 4), //4 + PLAYER_PLACED_DEATH_RUNE(560, 5), //5 + PLAYER_PLACED_WATER_RUNE(555, 6), //6 + PLAYER_PLACED_CHAOS_RUNE(562, 7), //7 + PLAYER_PLACED_EARTH_RUNE(557, 8), //8 + PLAYER_PLACED_LAW_RUNE(563, 9), //9 + ; + + private final int pieceID; + private final int pieceForSudoku; + + RunedokuPiece (int pieceID, int pieceForSudoku) { + this.pieceID = pieceID; + this.pieceForSudoku = pieceForSudoku; + } + + int getId () { + return pieceID; + } + + int getPieceForSudoku() { + return pieceForSudoku; + } + + static RunedokuPiece getById(int pieceID) { + for (RunedokuPiece e : RunedokuPiece.values()) { + if (e.getId() == pieceID) { + return e; + } + } + return null; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPlugin.java new file mode 100644 index 0000000000..81f80367d9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuPlugin.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019, gazivodag + * 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.runedoku; + +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginType; +import net.runelite.client.ui.overlay.OverlayManager; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@PluginDescriptor( + name = "Runedoku Solver", + description = "Show solutions for current Runedoku puzzle.", + tags = {"overlay", "runedoku", "sudoku", "puzzle", "solving"}, + type = PluginType.UTILITY +) +@Slf4j +@Singleton +public class RunedokuPlugin extends Plugin { + + @Inject + private Client client; + + @Inject + RunedokuUtil util; + + @Inject + private OverlayManager overlayManager; + + @Inject + private RunedokuOverlay runedokuOverlay; + + @Inject + private RunedokuConfig config; + + @Provides + RunedokuConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(RunedokuConfig.class); + } + + @Override + protected void startUp() throws Exception { + overlayManager.add(runedokuOverlay); + } + + @Override + protected void shutDown() throws Exception { + overlayManager.remove(runedokuOverlay); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuUtil.java b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuUtil.java new file mode 100644 index 0000000000..3c15d85c07 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/RunedokuUtil.java @@ -0,0 +1,126 @@ +package net.runelite.client.plugins.runedoku; + +import net.runelite.api.Client; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetItem; + +import javax.inject.Inject; +import java.awt.*; +import java.util.ArrayList; + +public class RunedokuUtil { + + private final RunedokuConfig config; + + @Inject + RunedokuUtil(final RunedokuConfig config) { + this.config = config; + } + + protected Color sudokuPieceToColor(int i) { + switch (i) { + case 1: + return config.mindRuneColor(); + case 2: + return config.fireRuneColor(); + case 3: + return config.bodyRuneColor(); + case 4: + return config.airRuneColor(); + case 5: + return config.deathRuneColor(); + case 6: + return config.waterRuneColor(); + case 7: + return config.chaosRuneColor(); + case 8: + return config.earthRuneColor(); + case 9: + return config.lawRuneColor(); + default: + return Color.RED; + } + } + + protected Color referenceColors(int i) { + switch (i) { + case 121: //earth + return config.earthRuneColor(); + case 122: //water + return config.waterRuneColor(); + case 123: //air + return config.airRuneColor(); + case 124: //mind + return config.mindRuneColor(); + case 125: //fire + return config.fireRuneColor(); + case 126: //body + return config.bodyRuneColor(); + case 127: //death + return config.deathRuneColor(); + case 128: //chaos + return config.chaosRuneColor(); + case 129: //law + return config.lawRuneColor(); + default: + return Color.RED; + } + } + + /** + * Make the 2d array into an arraylist + * @param board + * @return + */ + protected ArrayList makeSimple(int[][] board) { + ArrayList list = new ArrayList<>(); + for (int i = 0 ; i < 9 ; i++) { + for (int ii = 0 ; ii < 9 ; ii++) { + list.add(board[i][ii]); + } + } + return list; + } + + /** + * utility method + * @param rect + * @return + */ + protected Polygon RectangleToPolygon(Rectangle rect) { + int[] xpoints = {rect.x, rect.x + rect.width, rect.x + rect.width, rect.x}; + int[] ypoints = {rect.y, rect.y, rect.y + rect.height, rect.y + rect.height}; + return new Polygon(xpoints, ypoints, 4); + } + + /** + * Pulls data from what's on the Runedoku interface and creates a 2dimensional array for it + * @author gazivodag + * @param client + * @return sudoku table that the client currently sees in a 2d array + */ + protected int[][] createTable(Client client) { + int[][] myArr = new int[9][9]; + Widget sudokuScreen = client.getWidget(288,131); + for (int i = 0 ; i < 9 ; i++) { + for (int ii = 0 ; ii < 9 ; ii++) { + WidgetItem item; + int myIndex; + if (i > 0) { + myIndex = ((i * 10) + ii) - i; + } else { + myIndex = ii; + } + if (myIndex == 81) break; + item = sudokuScreen.getWidgetItem(myIndex); + if (item != null) { + myArr[i][ii] = RunedokuPiece.getById(item.getId()).getPieceForSudoku(); + } else { + myArr[i][ii] = 0; + } + } + } + return myArr; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/Sudoku.java b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/Sudoku.java new file mode 100644 index 0000000000..666e026bd7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runedoku/Sudoku.java @@ -0,0 +1,92 @@ +package net.runelite.client.plugins.runedoku; + +/** + * Credits to whoever wrote this sudoku class. + * @author ? + */ +public class Sudoku { + + private int[][] board; + public static final int EMPTY = 0; + public static final int SIZE = 9; + + Sudoku(int[][] board) { + this.board = new int[SIZE][SIZE]; + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + this.board[i][j] = board[i][j]; + } + } + } + + private boolean isInRow(int row, int number) { + for (int i = 0; i < SIZE; i++) + if (board[row][i] == number) + return true; + + return false; + } + + private boolean isInCol(int col, int number) { + for (int i = 0; i < SIZE; i++) + if (board[i][col] == number) + return true; + + return false; + } + + private boolean isInBox(int row, int col, int number) { + int r = row - row % 3; + int c = col - col % 3; + + for (int i = r; i < r + 3; i++) + for (int j = c; j < c + 3; j++) + if (board[i][j] == number) + return true; + + return false; + } + + private boolean isOk(int row, int col, int number) { + return !isInRow(row, number) && !isInCol(col, number) && !isInBox(row, col, number); + } + + public boolean solve() { + for (int row = 0; row < SIZE; row++) { + for (int col = 0; col < SIZE; col++) { + if (board[row][col] == EMPTY) { + for (int number = 1; number <= SIZE; number++) { + if (isOk(row, col, number)) { + board[row][col] = number; + if (solve()) { + return true; + } else { + board[row][col] = EMPTY; + } + } + } + return false; + } + } + } + + return true; + } + + public void display() { + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + System.out.print(" " + board[i][j]); + } + + System.out.println(); + } + + System.out.println(); + } + + public int[][] getBoard() { + return board; + } + +}