Seriously. Weekend.

This commit is contained in:
dtookey 2023-08-12 08:08:01 -04:00
parent d0dcdffb9c
commit c36583631c
6 changed files with 80 additions and 163 deletions

View File

@ -1,6 +1,7 @@
package controllers package controllers
import java.awt.Point import java.awt.Point
import kotlin.random.Random
/** /**
@ -55,4 +56,71 @@ interface InputController {
* Same as [scrollIn] but moves the scroll wheel backward. * Same as [scrollIn] but moves the scroll wheel backward.
*/ */
fun scrollOut(sleepDurationMillis: Long, sleepDurationVarianceMillis: Long) fun scrollOut(sleepDurationMillis: Long, sleepDurationVarianceMillis: Long)
/**
* 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
)

View File

@ -18,97 +18,13 @@ interface MousePointerObserver {
/** /**
* Gets the current pointer/mouse location on the desktop. * 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. * @return The current [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.
*/ */
fun getPointerLocation(): Point { fun getPointerLocation(): Point {
return MouseInfo.getPointerInfo().location 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
)

View File

@ -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 * 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. * sleepDurationMillis + rand(0, sleepDurationVarianceMillis) ms. This adds variance in scrolling speed.

View File

@ -1,7 +1,7 @@
package game_logic.runescape package game_logic.runescape
import controllers.Automaton import controllers.Automaton
import controllers.MouseWiggleParams import controllers.PointerVarianceBounds
import controllers.RobotAutomaton import controllers.RobotAutomaton
import params.StandingTaskParams import params.StandingTaskParams
import params.TravelTaskParams import params.TravelTaskParams
@ -86,7 +86,7 @@ class RSAgent(override val automaton: Automaton = RobotAutomaton()) : RSOrchestr
taskParams: StandingTaskParams taskParams: StandingTaskParams
) { ) {
//open the bank located by the chest parameter //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 //withdraw the desired inventory preset
automaton.keyPress(taskParams.bankPresetHotkey) automaton.keyPress(taskParams.bankPresetHotkey)
//sleep for a server tick //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 //move to the bank and open the interface
moveMouseLeftClickAndSleep( moveMouseLeftClickAndSleep(
automaton.getAlmostPoint(taskParams.bankPoint, MouseWiggleParams(10, 10)), automaton.getNearbyPoint(taskParams.bankPoint, PointerVarianceBounds(10, 10)),
taskParams.travelDurationMillis, taskParams.travelDurationMillis,
taskParams.travelDurationVarianceMillis taskParams.travelDurationVarianceMillis
) )

View File

@ -5,7 +5,7 @@ import org.mockito.Mockito.`when`
import java.awt.Point import java.awt.Point
import java.awt.event.InputEvent import java.awt.event.InputEvent
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertEquals
class AutomatonTest { class AutomatonTest {
@ -20,26 +20,21 @@ class AutomatonTest {
*/ */
@Test @Test
fun `Automaton extends DesktopController`() { fun `Automaton extends DesktopController`() {
val automaton = mock(MousePointerObserver::class.java)
val automaton = mock(Automaton::class.java)
// Given // Given
val basePoint = Point(10, 10) val basePoint = Point(10, 10)
val wiggleParams = MouseWiggleParams(xWiggle = 5, yWiggle = 5)
// When // 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 // Then
assertTrue(result.x >= basePoint.x - wiggleParams.xWiggle) assertEquals(basePoint.x, result.x)
assertTrue(result.x <= basePoint.x + wiggleParams.xWiggle) assertEquals(basePoint.y, result.y)
assertTrue(result.y >= basePoint.y - wiggleParams.yWiggle)
assertTrue(result.y <= basePoint.y + wiggleParams.yWiggle)
} }
/** /**

View File

@ -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)
}
}