From 5a6b39036d6163333fd142fd3640a9d54be68445 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Wed, 12 Jun 2019 22:41:13 -0700 Subject: [PATCH] HotColdClue: Refactor to use enums for temperatures --- .../cluescrolls/clues/HotColdClue.java | 127 +++++------------- .../clues/hotcold/HotColdTemperature.java | 84 ++++++++++++ .../hotcold/HotColdTemperatureChange.java | 55 ++++++++ .../hotcold/HotColdTemperatureChangeTest.java | 68 ++++++++++ .../clues/hotcold/HotColdTemperatureTest.java | 75 +++++++++++ 5 files changed, 319 insertions(+), 90 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperature.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChange.java create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java index 826017744f..87f2b6c5e1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Eadgars Ruse + * Copyright (c) 2019, Jordan Atwood * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,8 +37,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import lombok.Getter; import net.runelite.api.NPC; import net.runelite.api.coords.LocalPoint; @@ -47,6 +48,8 @@ import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin; import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET; import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea; import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdLocation; +import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdTemperature; +import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdTemperatureChange; import net.runelite.client.ui.overlay.OverlayUtil; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.PanelComponent; @@ -55,9 +58,6 @@ import net.runelite.client.ui.overlay.components.TitleComponent; @Getter public class HotColdClue extends ClueScroll implements LocationClueScroll, LocationsClueScroll, TextClueScroll, NpcClueScroll { - private static final Pattern INITIAL_STRANGE_DEVICE_MESSAGE = Pattern.compile("The device is (.*)"); - private static final Pattern STRANGE_DEVICE_MESSAGE = Pattern.compile("The device is (.*), (.*) last time\\."); - private static final Pattern FINAL_STRANGE_DEVICE_MESSAGE = Pattern.compile("The device is visibly shaking.*"); private static final HotColdClue CLUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Jorral may have a clue.", "Jorral", @@ -231,52 +231,31 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat public boolean update(final String message, final ClueScrollPlugin plugin) { - if (!message.startsWith("The device is")) + final HotColdTemperature temperature = HotColdTemperature.of(message); + + if (temperature == null) { return false; } - Matcher m1 = FINAL_STRANGE_DEVICE_MESSAGE.matcher(message); - Matcher m2 = STRANGE_DEVICE_MESSAGE.matcher(message); - Matcher m3 = INITIAL_STRANGE_DEVICE_MESSAGE.matcher(message); + final WorldPoint localWorld = plugin.getClient().getLocalPlayer().getWorldLocation(); - // the order that these pattern matchers are checked is important - if (m1.find()) + if (localWorld == null) { - // final location for hot cold clue has been found - WorldPoint localWorld = plugin.getClient().getLocalPlayer().getWorldLocation(); - - if (localWorld != null) - { - markFinalSpot(localWorld); - return true; - } - } - else if (m2.find()) - { - String temperature = m2.group(1); - String difference = m2.group(2); - WorldPoint localWorld = plugin.getClient().getLocalPlayer().getWorldLocation(); - - if (localWorld != null) - { - updatePossibleArea(localWorld, temperature, difference); - return true; - } - } - else if (m3.find()) - { - String temperature = m3.group(1); - WorldPoint localWorld = plugin.getClient().getLocalPlayer().getWorldLocation(); - - if (localWorld != null) - { - updatePossibleArea(localWorld, temperature, ""); - return true; - } + return false; } - return false; + if (temperature == HotColdTemperature.VISIBLY_SHAKING) + { + markFinalSpot(localWorld); + } + else + { + final HotColdTemperatureChange temperatureChange = HotColdTemperatureChange.of(message); + updatePossibleArea(localWorld, temperature, temperatureChange); + } + + return true; } @Override @@ -286,7 +265,7 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat digLocations.clear(); } - private void updatePossibleArea(WorldPoint currentWp, String temperature, String difference) + private void updatePossibleArea(@Nonnull final WorldPoint worldPoint, @Nonnull final HotColdTemperature temperature, @Nullable final HotColdTemperatureChange temperatureChange) { this.location = null; @@ -295,73 +274,41 @@ public class HotColdClue extends ClueScroll implements LocationClueScroll, Locat digLocations.addAll(Arrays.asList(HotColdLocation.values())); } - int maxSquaresAway = 5000; - int minSquaresAway = 0; - - switch (temperature) - { - // when the strange device reads a temperature, that means that the center of the final dig location - // is a range of squares away from the player's current location (Chebyshev AKA Chess-board distance) - case "ice cold": - maxSquaresAway = 5000; - minSquaresAway = 500; - break; - case "very cold": - maxSquaresAway = 499; - minSquaresAway = 200; - break; - case "cold": - maxSquaresAway = 199; - minSquaresAway = 150; - break; - case "warm": - maxSquaresAway = 149; - minSquaresAway = 100; - break; - case "hot": - maxSquaresAway = 99; - minSquaresAway = 70; - break; - case "very hot": - maxSquaresAway = 69; - minSquaresAway = 30; - break; - case "incredibly hot": - maxSquaresAway = 29; - minSquaresAway = 5; - break; - } + // when the strange device reads a temperature, that means that the center of the final dig location + // is a range of squares away from the player's current location (Chebyshev AKA Chess-board distance) + int maxSquaresAway = temperature.getMaxDistance(); + int minSquaresAway = temperature.getMinDistance(); // rectangle r1 encompasses all of the points that are within the max possible distance from the player - Point p1 = new Point(currentWp.getX() - maxSquaresAway, currentWp.getY() - maxSquaresAway); + Point p1 = new Point(worldPoint.getX() - maxSquaresAway, worldPoint.getY() - maxSquaresAway); Rectangle r1 = new Rectangle((int) p1.getX(), (int) p1.getY(), 2 * maxSquaresAway + 1, 2 * maxSquaresAway + 1); // rectangle r2 encompasses all of the points that are within the min possible distance from the player - Point p2 = new Point(currentWp.getX() - minSquaresAway, currentWp.getY() - minSquaresAway); + Point p2 = new Point(worldPoint.getX() - minSquaresAway, worldPoint.getY() - minSquaresAway); Rectangle r2 = new Rectangle((int) p2.getX(), (int) p2.getY(), 2 * minSquaresAway + 1, 2 * minSquaresAway + 1); // eliminate from consideration dig spots that lie entirely within the min range or entirely outside of the max range digLocations.removeIf(entry -> r2.contains(entry.getRect()) || !r1.intersects(entry.getRect())); // if a previous world point has been recorded, we can consider the warmer/colder result from the strange device - if (lastWorldPoint != null) + if (lastWorldPoint != null && temperatureChange != null) { - switch (difference) + switch (temperatureChange) { - case "but colder than": + case COLDER: // eliminate spots that are absolutely warmer - digLocations.removeIf(entry -> isFirstPointCloserRect(currentWp, lastWorldPoint, entry.getRect())); + digLocations.removeIf(entry -> isFirstPointCloserRect(worldPoint, lastWorldPoint, entry.getRect())); break; - case "and warmer than": + case WARMER: // eliminate spots that are absolutely colder - digLocations.removeIf(entry -> isFirstPointCloserRect(lastWorldPoint, currentWp, entry.getRect())); + digLocations.removeIf(entry -> isFirstPointCloserRect(lastWorldPoint, worldPoint, entry.getRect())); break; - case "and the same temperature as": + case SAME: // I couldn't figure out a clean implementation for this case // not necessary for quickly determining final location } } - lastWorldPoint = currentWp; + lastWorldPoint = worldPoint; } private boolean isFirstPointCloserRect(WorldPoint firstWp, WorldPoint secondWp, Rectangle2D r) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperature.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperature.java new file mode 100644 index 0000000000..850d33aec1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperature.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * 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.cluescrolls.clues.hotcold; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum HotColdTemperature +{ + ICE_COLD("ice cold", 500, 5000), + VERY_COLD("very cold", 200, 499), + COLD("cold", 150, 199), + WARM("warm", 100, 149), + HOT("hot", 70, 99), + VERY_HOT("very hot", 30, 69), + INCREDIBLY_HOT("incredibly hot", 5, 29), + VISIBLY_SHAKING("visibly shaking", 0, 4); + + private final String text; + private final int minDistance; + private final int maxDistance; + + private static final String DEVICE_USED_START_TEXT = "The device is "; + + /** + * Gets the temperature corresponding to the passed string. + * + * @param message A string containing a temperature value + * @return The corresponding enum for the passed string. + *

+ * Note that in cases where two temperature enums are equally likely to be the given temperature (say, two + * temperatures with identical text values), the behavior is undefined. + */ + public static HotColdTemperature of(final String message) + { + if (!message.startsWith(DEVICE_USED_START_TEXT)) + { + return null; + } + + final List possibleTemperatures = new ArrayList<>(); + + for (final HotColdTemperature temperature : values()) + { + if (message.contains(temperature.getText())) + { + possibleTemperatures.add(temperature); + } + } + + return possibleTemperatures.stream() + // For messages such as "The device is very cold", this will choose the Enum with text of greatest length so + // that VERY_COLD would be selected over COLD, though both Enums have matching text for this message. + .max(Comparator.comparingInt(x -> (x.getText()).length())) + .orElse(null); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChange.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChange.java new file mode 100644 index 0000000000..e9077bc9e1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChange.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * 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.cluescrolls.clues.hotcold; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum HotColdTemperatureChange +{ + WARMER("and warmer than"), + SAME("and the same temperature as"), + COLDER("but colder than"); + + private final String text; + + public static HotColdTemperatureChange of(final String message) + { + if (!message.endsWith(" last time.")) + { + return null; + } + + for (final HotColdTemperatureChange change : values()) + { + if (message.contains(change.text)) + { + return change; + } + } + + return null; + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java new file mode 100644 index 0000000000..9b7cdcaf3c --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureChangeTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * 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.cluescrolls.clues.hotcold; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import org.junit.Test; + +public class HotColdTemperatureChangeTest +{ + private static final String[] VALID_MESSAGES = { + "The device is warm, and warmer than last time.", + "The device is cold, but colder than last time.", + "The device is very hot, and the same temperature as last time.", + }; + private static final String[] INVALID_MESSAGES = { + "The device is cold.", + "The device is ice cold.", + "The device is very cold.", + "The device is hot.", + "The device is incredibly hot.", + "The device is an octopus, and is wetter than last time", + "foobar", + "a q p w", + "My feet are cold, I should put them in some lukewarm water, or run hot water over them.", + "and warmer than and colder than and the same temperature", + }; + + @Test + public void testValidTemperatureChangeMessages() + { + for (final String message : VALID_MESSAGES) + { + assertNotNull(message, HotColdTemperatureChange.of(message)); + } + } + + @Test + public void testInvalidTemperatureChangeMessages() + { + for (final String message : INVALID_MESSAGES) + { + assertNull(message, HotColdTemperatureChange.of(message)); + } + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureTest.java new file mode 100644 index 0000000000..505df56346 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdTemperatureTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * 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.cluescrolls.clues.hotcold; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import org.junit.Test; + +public class HotColdTemperatureTest +{ + private static final String[] VALID_MESSAGES = { + "The device is warm, and warmer than last time.", + "The device is visibly shaking and burns to the touch. This must be the spot.", + "The device is cold.", + "The device is ice cold.", + "The device is very cold.", + "The device is hot.", + "The device is incredibly hot.", + }; + private static final String[] INVALID_MESSAGES = { + "The device is an octopus, and is wetter than last time.", + "foobar", + "a q p w", + "My feet are cold, I should put them in some lukewarm water, or run hot water over them.", + }; + + @Test + public void testValidTemperatureMessages() + { + for (final String message : VALID_MESSAGES) + { + assertNotNull(message, HotColdTemperature.of(message)); + } + } + + @Test + public void testInvalidTemperatureMessages() + { + for (final String message : INVALID_MESSAGES) + { + assertNull(message, HotColdTemperature.of(message)); + } + } + + @Test + public void testAmbiguousTemperatureMessages() + { + assertEquals(HotColdTemperature.ICE_COLD, HotColdTemperature.of("The device is ice cold.")); + assertEquals(HotColdTemperature.VERY_COLD, HotColdTemperature.of("The device is very cold.")); + assertEquals(HotColdTemperature.VERY_HOT, HotColdTemperature.of("The device is very hot.")); + } +}