From c36583631c656a25a870c83605dabccf89cebcef Mon Sep 17 00:00:00 2001 From: dtookey Date: Sat, 12 Aug 2023 08:08:01 -0400 Subject: [PATCH] Seriously. Weekend. --- .../kotlin/controllers/InputController.kt | 70 ++++++++++++++- .../controllers/MousePointerObserver.kt | 86 +------------------ src/main/kotlin/controllers/RobotAutomaton.kt | 2 +- .../kotlin/game_logic/runescape/RSAgent.kt | 6 +- src/test/kotlin/controllers/AutomatonTest.kt | 17 ++-- .../controllers/DesktopControllerTest.kt | 62 ------------- 6 files changed, 80 insertions(+), 163 deletions(-) delete mode 100644 src/test/kotlin/controllers/DesktopControllerTest.kt diff --git a/src/main/kotlin/controllers/InputController.kt b/src/main/kotlin/controllers/InputController.kt index 545a9e5..d7502e5 100644 --- a/src/main/kotlin/controllers/InputController.kt +++ b/src/main/kotlin/controllers/InputController.kt @@ -1,6 +1,7 @@ package controllers import java.awt.Point +import kotlin.random.Random /** @@ -55,4 +56,71 @@ interface InputController { * Same as [scrollIn] but moves the scroll wheel backward. */ fun scrollOut(sleepDurationMillis: Long, sleepDurationVarianceMillis: Long) -} \ No newline at end of file + + + /** + * Generates a random point near the given [point] within specified bounds. + * + * The returned point will have a random offset from [point] in both x and y directions. The maximum offset in each + * direction is controlled by [params]. + * + * @param point The reference point to offset from + * @param params The max x and y variance + * @return A new random [Point] near [point] + */ + fun getNearbyPoint(point: Point, params: PointerVarianceBounds = PointerVarianceBounds()): Point { + // Generate random x and y offsets within the given variance bounds + val xDelta = Random.nextInt(0, params.xVariance) + val yDelta = Random.nextInt(0, params.yVariance) + + // Determine x and y direction (-1 or 1) by a random Boolean + val xDirection = if (Random.nextBoolean()) 1 else -1 + val yDirection = if (Random.nextBoolean()) 1 else -1 + + // Apply the offsets in their random directions to the x and y values of the reference point + return Point( + point.x + (xDelta * xDirection), + point.y + (yDelta * yDirection) + ) + } + + /** + * Animates mouse movement from a start point to an end point over a duration. + * + * The movement will follow an approximation of a sigmoid velocity curve, accelerating + * at the start and decelerating at the end. + * + * The path between start and end points will be varied randomly within the given + * variance bounds each tick to simulate human imperfection. + * + * @param startPoint The starting mouse location + * @param endPoint The ending mouse location + * @param travelDurationMillis Total time in milliseconds for the movement + * @param varianceBounds The max x and y variance each tick + */ + @Deprecated("Not finished. This will panic. You were warned.", + ReplaceWith("TODO(\"Implement this once we figure out how to approximate sigmoid movement and timing\")") + ) + fun animateMoveMouse( + startPoint: Point, + endPoint: Point, + travelDurationMillis: Long, + varianceBounds: PointerVarianceBounds = PointerVarianceBounds() + ) { + //we're going to leave this as something to do, but this will remove the need to implement it everywhere + TODO("Implement this once we figure out how to approximate sigmoid movement and timing") + } +} + + +/** + * Holds x and y variance bounds for random point generation. + * + * Used as parameter in [MousePointerObserver.getNearbyPoint] to control the allowed offset range. + * + * @see MousePointerObserver.getNearbyPoint() + */ +data class PointerVarianceBounds( + val xVariance: Int = 25, + val yVariance: Int = 25 +) diff --git a/src/main/kotlin/controllers/MousePointerObserver.kt b/src/main/kotlin/controllers/MousePointerObserver.kt index 45a40c4..d2bdbbd 100644 --- a/src/main/kotlin/controllers/MousePointerObserver.kt +++ b/src/main/kotlin/controllers/MousePointerObserver.kt @@ -18,97 +18,13 @@ interface MousePointerObserver { /** * Gets the current pointer/mouse location on the desktop. * - * This returns a [Point] representing the x, y coordinates of the mouse pointer on the screen. - * - * For example: - * - * ``` - * val mouseLocation = getPointerLocation() - * - * println(mouseLocation) // Might print "Point[x=1920,y=1080]" - * ``` - * - * @return The current [Point] location of the mouse pointer on the screen. + * @return The current [Point] representing the x, y coordinates of the mouse pointer on the screen. */ fun getPointerLocation(): Point { return MouseInfo.getPointerInfo().location } - /** - * Gets a point near the given [point], with some random wiggle. - * - * This generates a new point that is randomly offset from the given [point] - * by an amount controlled by the given [MouseWiggleParams]. - * - * Usage example: - * - * ``` - * val base = Point(10, 20) - * val params = WiggleParams(xWiggle = 5, yWiggle = 5) - * val randomPoint = getAlmostPoint(base, params) - * - * // randomPoint might be (8, 22) - * ``` - * - * Alternatively, params may be omitted to use the default WiggleParams values - * ``` - * val base = Point(10, 20) - * val randomPoint = getAlmostPoint(base) - * - * // randomPoint might be (8, 22) - * ``` - * - * @param point The base point to start from - * @param params The wiggle parameters that control the random offset amount - * @return A new [Point] near the given point with some random wiggle applied - */ - fun getAlmostPoint(point: Point, params: MouseWiggleParams = MouseWiggleParams()): Point { - val xDel = Random.nextInt(0, params.xWiggle) - val yDel = Random.nextInt(0, params.yWiggle) - - //flip two coins, determine direction based on the parity of the results - val xDir = if (Random.nextDouble() > 0.5) { - 1 - } else { - -1 - } - val yDir = if (Random.nextDouble() > 0.5) { - 1 - } else { - -1 - } - - return Point(point.x + (xDel * xDir), point.y + (yDel * yDir)) - } } -/** - * Data class to hold wiggle parameters for mouse movement. - * - * This simple data class holds two integer properties for x and y wiggle amounts. - * These are used when generating simulated mouse movements to add some variance - * and randomness to the coordinates. - * - * For example, if a target destination point is (100, 200), the wiggle params - * might generate an actual movement point like (102, 198) to add some randomness. - * - * Usage: - * - * ``` - * val controller = DesktopController() - * val wiggle = WiggleParams(xWiggle = 10, yWiggle = 15) - * - * val target = Point(100, 200) - * val actual = controller.getAlmostPoint(target, wiggle) // (104, 197) - * ``` - * - * @param xWiggle The max amount of variance in x direction. Default 25. - * @param yWiggle The max amount of variance in y direction. Default 25. - */ -data class MouseWiggleParams( - val xWiggle: Int = 25, - val yWiggle: Int = 25 -) - diff --git a/src/main/kotlin/controllers/RobotAutomaton.kt b/src/main/kotlin/controllers/RobotAutomaton.kt index 507fd52..13b3a29 100644 --- a/src/main/kotlin/controllers/RobotAutomaton.kt +++ b/src/main/kotlin/controllers/RobotAutomaton.kt @@ -105,7 +105,7 @@ open class RobotAutomaton(internal val robot: Robot = Robot()) : Automaton { } /** - * Scrolls the mouse wheel down by one unit. + * Scrolls the mouse wheel up by one unit. * * Uses the [Robot.mouseWheel] method to scroll down and then sleeps for a random duration between the given * sleepDurationMillis + rand(0, sleepDurationVarianceMillis) ms. This adds variance in scrolling speed. diff --git a/src/main/kotlin/game_logic/runescape/RSAgent.kt b/src/main/kotlin/game_logic/runescape/RSAgent.kt index b2ff171..285ff7a 100644 --- a/src/main/kotlin/game_logic/runescape/RSAgent.kt +++ b/src/main/kotlin/game_logic/runescape/RSAgent.kt @@ -1,7 +1,7 @@ package game_logic.runescape import controllers.Automaton -import controllers.MouseWiggleParams +import controllers.PointerVarianceBounds import controllers.RobotAutomaton import params.StandingTaskParams import params.TravelTaskParams @@ -86,7 +86,7 @@ class RSAgent(override val automaton: Automaton = RobotAutomaton()) : RSOrchestr taskParams: StandingTaskParams ) { //open the bank located by the chest parameter - moveMouseLeftClickAndSleep(automaton.getAlmostPoint(taskParams.bankPoint, MouseWiggleParams(10, 10)), 900, 400) + moveMouseLeftClickAndSleep(automaton.getNearbyPoint(taskParams.bankPoint, PointerVarianceBounds(10, 10)), 900, 400) //withdraw the desired inventory preset automaton.keyPress(taskParams.bankPresetHotkey) //sleep for a server tick @@ -131,7 +131,7 @@ class RSAgent(override val automaton: Automaton = RobotAutomaton()) : RSOrchestr ) { //move to the bank and open the interface moveMouseLeftClickAndSleep( - automaton.getAlmostPoint(taskParams.bankPoint, MouseWiggleParams(10, 10)), + automaton.getNearbyPoint(taskParams.bankPoint, PointerVarianceBounds(10, 10)), taskParams.travelDurationMillis, taskParams.travelDurationVarianceMillis ) diff --git a/src/test/kotlin/controllers/AutomatonTest.kt b/src/test/kotlin/controllers/AutomatonTest.kt index eb8b20c..015c2e8 100644 --- a/src/test/kotlin/controllers/AutomatonTest.kt +++ b/src/test/kotlin/controllers/AutomatonTest.kt @@ -5,7 +5,7 @@ import org.mockito.Mockito.`when` import java.awt.Point import java.awt.event.InputEvent import kotlin.test.Test -import kotlin.test.assertTrue +import kotlin.test.assertEquals class AutomatonTest { @@ -20,26 +20,21 @@ class AutomatonTest { */ @Test fun `Automaton extends DesktopController`() { - - val automaton = mock(Automaton::class.java) + val automaton = mock(MousePointerObserver::class.java) // Given val basePoint = Point(10, 10) - val wiggleParams = MouseWiggleParams(xWiggle = 5, yWiggle = 5) // When - `when`(automaton.getAlmostPoint(basePoint, wiggleParams)).thenReturn(Point(12, 8)) + `when`(automaton.getPointerLocation()).thenReturn(Point(10, 10)) - val result = automaton.getAlmostPoint(basePoint, wiggleParams) + val result = automaton.getPointerLocation() // Then - assertTrue(result.x >= basePoint.x - wiggleParams.xWiggle) - assertTrue(result.x <= basePoint.x + wiggleParams.xWiggle) - - assertTrue(result.y >= basePoint.y - wiggleParams.yWiggle) - assertTrue(result.y <= basePoint.y + wiggleParams.yWiggle) + assertEquals(basePoint.x, result.x) + assertEquals(basePoint.y, result.y) } /** diff --git a/src/test/kotlin/controllers/DesktopControllerTest.kt b/src/test/kotlin/controllers/DesktopControllerTest.kt deleted file mode 100644 index ba7f5dc..0000000 --- a/src/test/kotlin/controllers/DesktopControllerTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package controllers - -import kotlin.test.* -import org.mockito.Mockito.* -import java.awt.Point - - -class DesktopControllerTest { - - /** - * Tests that getPointerLocation() returns the mocked mouse position. - * - * Creates a mock DesktopController instance. - * Mocks the getPointerLocation() method to return a fixed point. - * Calls getPointerLocation() on the mock controller. - * Retrieves the returned point. - * Asserts the x and y values match the mocked values. - * This validates getPointerLocation() returns the expected mouse position. - */ - @Test - fun `getPointerLocation returns mouse position`() { - val controller = mock(MousePointerObserver::class.java) - - // Mock mouse position - `when`(controller.getPointerLocation()).thenReturn(Point(100, 200)) - - // Assert mouse position is returned - val pos = controller.getPointerLocation() - assertEquals(100, pos.x) - assertEquals(200, pos.y) - } - - - /** - * Tests that getAlmostPoint() returns a wiggly point different than the source. - * - * Creates a mock DesktopController instance. - * Creates a WiggleParams with x and y wiggle amounts. - * Mocks getAlmostPoint() to return a fixed offset point based on the params. - * Calls getAlmostPoint() with a source point and the wiggle params. - * Retrieves the returned wiggly point. - * Asserts the x and y values match the expected wiggly values. - * Also asserts the x and y values are different than the source point. - * This validates getAlmostPoint() returns a randomly offset point. - */ - @Test - fun `getAlmostPoint returns wiggly point`() { - val controller = mock(MousePointerObserver::class.java) - val params = MouseWiggleParams(xWiggle = 10, yWiggle = 10) - - // Mock random wiggle - `when`(controller.getAlmostPoint(Point(100, 200), params)) - .thenReturn(Point(105, 205)) - - // Assert wiggly point - val wiggly = controller.getAlmostPoint(Point(100, 200), params) - assertEquals(105, wiggly.x) - assertEquals(205, wiggly.y) - assertNotEquals(100, wiggly.x) - assertNotEquals(200, wiggly.y) - } -} \ No newline at end of file