Skip to content

Commit 314dd36

Browse files
committed
Incremental pathing on state updates and more settings
1 parent 1a5a9b9 commit 314dd36

File tree

8 files changed

+165
-60
lines changed

8 files changed

+165
-60
lines changed

common/src/main/kotlin/com/lambda/module/modules/movement/Pathfinder.kt

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package com.lambda.module.modules.movement
1919

20-
import com.lambda.config.groups.RotationSettings
2120
import com.lambda.context.SafeContext
2221
import com.lambda.event.events.MovementEvent
2322
import com.lambda.event.events.RenderEvent
@@ -40,19 +39,26 @@ import com.lambda.pathing.dstar.DStarLite
4039
import com.lambda.pathing.dstar.LazyGraph
4140
import com.lambda.pathing.goal.SimpleGoal
4241
import com.lambda.pathing.move.MoveFinder.moveOptions
42+
import com.lambda.pathing.move.NodeType
43+
import com.lambda.pathing.move.TraverseMove
4344
import com.lambda.threading.runConcurrent
4445
import com.lambda.threading.runSafe
4546
import com.lambda.util.Communication.info
4647
import com.lambda.util.Formatting.string
4748
import com.lambda.util.math.setAlpha
4849
import com.lambda.util.player.MovementUtils.buildMovementInput
4950
import com.lambda.util.player.MovementUtils.mergeFrom
51+
import com.lambda.util.world.FastVector
5052
import com.lambda.util.world.fastVectorOf
5153
import com.lambda.util.world.toBlockPos
5254
import com.lambda.util.world.toFastVec
55+
import com.lambda.util.world.x
56+
import com.lambda.util.world.y
57+
import com.lambda.util.world.z
5358
import net.minecraft.util.math.Box
5459
import net.minecraft.util.math.Vec3d
5560
import java.awt.Color
61+
import kotlin.math.abs
5662
import kotlin.math.cos
5763
import kotlin.math.sin
5864
import kotlin.system.measureTimeMillis
@@ -62,15 +68,21 @@ object Pathfinder : Module(
6268
description = "Get from A to B",
6369
defaultTags = setOf(ModuleTag.MOVEMENT)
6470
) {
65-
enum class Page {
66-
Pathing, Rotation
67-
}
71+
private val pathing = PathingSettings(this)
72+
73+
private fun heuristic(u: FastVector): Double =
74+
(abs(u.x) + abs(u.y) + abs(u.z)).toDouble()
6875

69-
private val page by setting("Page", Page.Pathing)
70-
private val pathing = PathingSettings(this) { page == Page.Pathing }
71-
private val rotation = RotationSettings(this) { page == Page.Rotation }
76+
private fun heuristic(u: FastVector, v: FastVector): Double =
77+
(abs(u.x - v.x) + abs(u.y - v.y) + abs(u.z - v.z)).toDouble()
7278

7379
private val target = fastVectorOf(0, 91, -4)
80+
private val graph = LazyGraph { origin ->
81+
runSafe {
82+
moveOptions(origin, ::heuristic, pathing).associate { it.pos to it.cost }
83+
} ?: emptyMap()
84+
}
85+
private val dStar = DStarLite(graph, fastVectorOf(0, 0, 0), target, ::heuristic)
7486
private var coarsePath = Path()
7587
private var refinedPath = Path()
7688
private var currentTarget: Vec3d? = null
@@ -86,6 +98,9 @@ object Pathfinder : Module(
8698
coarsePath = Path()
8799
refinedPath = Path()
88100
currentTarget = null
101+
graph.clear()
102+
dStar.initialize()
103+
dStar.updateStart(player.pos.toFastVec())
89104
}
90105

91106
listen<TickEvent.Pre> {
@@ -96,7 +111,15 @@ object Pathfinder : Module(
96111
updatePaths()
97112
}
98113

114+
// listen<WorldEvent.BlockUpdate.Client> {
115+
// val pos = it.pos.toFastVec()
116+
// graph.markDirty(pos)
117+
// info("Updated block at ${it.pos} to ${it.newState.block.name.string} rescheduled D*Lite.")
118+
// }
119+
99120
listen<RotationEvent.StrafeInput> { event ->
121+
if (!pathing.moveAlongPath) return@listen
122+
100123
currentTarget?.let { target ->
101124
event.strafeYaw = player.eyePos.rotationTo(target).yaw
102125
val adjustment = calculatePID(target)
@@ -117,68 +140,93 @@ object Pathfinder : Module(
117140
}
118141

119142
onRotate {
143+
if (!pathing.moveAlongPath) return@onRotate
144+
120145
val currentTarget = currentTarget ?: return@onRotate
121146
val part = player.eyePos.rotationTo(currentTarget)
122147
val targetRotation = Rotation(part.yaw, player.pitch.toDouble())
123148

124-
lookAt(targetRotation).requestBy(rotation)
149+
lookAt(targetRotation).requestBy(pathing.rotation)
125150
}
126151

127152
listen<MovementEvent.Sprint> {
153+
if (!pathing.moveAlongPath) return@listen
128154
if (refinedPath.moves.isEmpty()) return@listen
129155

130156
player.isSprinting = pathing.allowSprint
131157
it.sprint = pathing.allowSprint
132158
}
133159

134160
listen<RenderEvent.StaticESP> { event ->
135-
// longPath.render(event.renderer, Color.YELLOW)
136-
refinedPath.render(event.renderer, Color.GREEN)
137-
event.renderer.buildFilled(Box(target.toBlockPos()), Color.PINK.setAlpha(0.25))
161+
if (pathing.renderCoarsePath) coarsePath.render(event.renderer, Color.YELLOW)
162+
if (pathing.renderRefinedPath) refinedPath.render(event.renderer, Color.GREEN)
163+
if (pathing.renderGoal) event.renderer.buildFilled(Box(target.toBlockPos()), Color.PINK.setAlpha(0.25))
138164
}
139165
}
140166

141167
private fun SafeContext.updateTargetNode() {
142-
refinedPath.moves.firstOrNull()?.let { current ->
168+
currentTarget = refinedPath.moves.firstOrNull()?.let { current ->
143169
if (player.pos.distanceTo(current.bottomPos) < pathing.tolerance) {
144170
refinedPath.moves.removeFirst()
145171
integralError = Vec3d.ZERO
146172
}
147-
currentTarget = refinedPath.moves.firstOrNull()?.bottomPos
148-
} ?: run {
149-
currentTarget = null
173+
refinedPath.moves.firstOrNull()?.bottomPos
150174
}
151175
}
152176

153177
private fun SafeContext.updatePaths() {
154178
val goal = SimpleGoal(target)
179+
val start = player.blockPos.toFastVec()
155180
when (pathing.algorithm) {
156-
PathingConfig.PathingAlgorithm.A_STAR -> {
157-
runConcurrent {
158-
calculating = true
159-
val long: Path
160-
val aStar = measureTimeMillis {
161-
long = findPathAStar(player.blockPos.toFastVec(), goal, pathing)
162-
}
163-
val short: Path
164-
val thetaStar = measureTimeMillis {
165-
short = if (pathing.pathRefining) {
166-
thetaStarClearance(long, pathing)
167-
} else long
168-
}
169-
info("A* (Length: ${long.length().string} Nodes: ${long.size} T: $aStar ms) and Theta* (Length: ${short.length().string} Nodes: ${short.size} T: $thetaStar ms)")
170-
println("Long: $long | Short: $short")
171-
short.moves.removeFirstOrNull()
172-
coarsePath = long
173-
refinedPath = short
174-
// calculating = false
175-
}
181+
PathingConfig.PathingAlgorithm.A_STAR -> updateAStar(start, goal)
182+
PathingConfig.PathingAlgorithm.D_STAR_LITE -> updateDStar()
183+
}
184+
}
185+
186+
private fun SafeContext.updateAStar(start: FastVector, goal: SimpleGoal) {
187+
runConcurrent {
188+
calculating = true
189+
val long: Path
190+
val aStar = measureTimeMillis {
191+
long = findPathAStar(start, goal, pathing)
192+
}
193+
val short: Path
194+
val thetaStar = measureTimeMillis {
195+
short = if (pathing.refinePath) {
196+
thetaStarClearance(long, pathing)
197+
} else long
176198
}
177-
PathingConfig.PathingAlgorithm.D_STAR_LITE -> {
178-
runConcurrent {
199+
info("A* (Length: ${long.length().string} Nodes: ${long.size} T: $aStar ms) and \u03b8* (Length: ${short.length().string} Nodes: ${short.size} T: $thetaStar ms)")
200+
println("Long: $long | Short: $short")
201+
short.moves.removeFirstOrNull()
202+
coarsePath = long
203+
refinedPath = short
204+
// calculating = false
205+
}
206+
}
179207

180-
}
208+
private fun SafeContext.updateDStar() {
209+
runConcurrent {
210+
calculating = true
211+
val long: Path
212+
val dStar = measureTimeMillis {
213+
// if (start dist dstar.start > 3) dstar.updateStart(start)
214+
dStar.computeShortestPath()
215+
val nodes = dStar.getPath().map { TraverseMove(it, 0.0, NodeType.OPEN, 0.0, 0.0) }
216+
long = Path(ArrayDeque(nodes))
217+
}
218+
val short: Path
219+
val thetaStar = measureTimeMillis {
220+
short = if (pathing.refinePath) {
221+
thetaStarClearance(long, pathing)
222+
} else long
181223
}
224+
info("Lazy D* Lite (Length: ${long.length().string} Nodes: ${long.size} Graph Size: ${graph.size} T: $dStar ms) and \u03b8* (Length: ${short.length().string} Nodes: ${short.size} T: $thetaStar ms)")
225+
println("Long: $long | Short: $short")
226+
short.moves.removeFirstOrNull()
227+
coarsePath = long
228+
refinedPath = short
229+
// calculating = false
182230
}
183231
}
184232

common/src/main/kotlin/com/lambda/pathing/PathingConfig.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,35 @@
1717

1818
package com.lambda.pathing
1919

20+
import com.lambda.interaction.request.rotation.RotationConfig
2021
import com.lambda.util.NamedEnum
2122

2223
interface PathingConfig {
2324
val algorithm: PathingAlgorithm
2425
val cutoffTimeout: Long
2526
val maxFallHeight: Double
2627

27-
val pathRefining: Boolean
28+
val refinePath: Boolean
2829
val shortcutLength: Int
2930
val clearancePrecision: Double
3031
val findShortcutJumps: Boolean
3132

33+
val moveAlongPath: Boolean
3234
val kP: Double
3335
val kI: Double
3436
val kD: Double
3537
val tolerance: Double
3638
val allowSprint: Boolean
3739

40+
val rotation: RotationConfig
41+
42+
val renderCoarsePath: Boolean
43+
val renderRefinedPath: Boolean
44+
val renderGoal: Boolean
3845
val assumeJesus: Boolean
3946

4047
enum class PathingAlgorithm(override val displayName: String) : NamedEnum {
4148
A_STAR("A*"),
42-
D_STAR_LITE("D* Lite"),
49+
D_STAR_LITE("Lazy D* Lite"),
4350
}
4451
}

common/src/main/kotlin/com/lambda/pathing/PathingSettings.kt

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,38 @@
1818
package com.lambda.pathing
1919

2020
import com.lambda.config.Configurable
21+
import com.lambda.config.groups.RotationSettings
2122

2223
class PathingSettings(
2324
c: Configurable,
2425
vis: () -> Boolean = { true }
2526
) : PathingConfig {
2627
enum class Page {
27-
Pathfinding, Refinement, Movement, Misc
28+
Pathfinding, Refinement, Movement, Rotation, Misc
2829
}
2930

3031
private val page by c.setting("Pathing Page", Page.Pathfinding, "Current page", vis)
3132

32-
override val algorithm by c.setting("Pathfinding Algorithm", PathingConfig.PathingAlgorithm.A_STAR) { vis() && page == Page.Pathfinding }
33+
override val algorithm by c.setting("Algorithm", PathingConfig.PathingAlgorithm.A_STAR) { vis() && page == Page.Pathfinding }
3334
override val cutoffTimeout by c.setting("Cutoff Timeout", 500L, 1L..2000L, 10L, "Timeout of path calculation", " ms") { vis() && page == Page.Pathfinding }
3435
override val maxFallHeight by c.setting("Max Fall Height", 3.0, 0.0..30.0, 0.5) { vis() && page == Page.Pathfinding }
3536

36-
override val pathRefining by c.setting("Path Refining", true) { vis() && page == Page.Refinement }
37-
override val shortcutLength by c.setting("Shortcut Length", 10, 1..100, 1) { vis() && pathRefining && page == Page.Refinement }
38-
override val clearancePrecision by c.setting("Clearance Precision", 0.2, 0.0..1.0, 0.01) { vis() && pathRefining && page == Page.Refinement }
39-
override val findShortcutJumps by c.setting("Find Shortcut Jumps", true) { vis() && pathRefining && page == Page.Refinement }
37+
override val refinePath by c.setting("Refine Path with θ*", true) { vis() && page == Page.Refinement }
38+
override val shortcutLength by c.setting("Shortcut Length", 15, 1..100, 1) { vis() && refinePath && page == Page.Refinement }
39+
override val clearancePrecision by c.setting("Clearance Precision", 0.1, 0.0..1.0, 0.01) { vis() && refinePath && page == Page.Refinement }
40+
override val findShortcutJumps by c.setting("Find Shortcut Jumps", true) { vis() && refinePath && page == Page.Refinement }
4041

41-
override val kP by c.setting("P Gain", 0.5, 0.0..2.0, 0.01) { vis() && page == Page.Movement }
42-
override val kI by c.setting("I Gain", 0.0, 0.0..1.0, 0.01) { vis() && page == Page.Movement }
43-
override val kD by c.setting("D Gain", 0.2, 0.0..1.0, 0.01) { vis() && page == Page.Movement }
44-
override val tolerance by c.setting("Node Tolerance", 0.7, 0.01..2.0, 0.05) { vis() && page == Page.Movement }
45-
override val allowSprint by c.setting("Allow Sprint", true) { vis() && page == Page.Movement }
42+
override val moveAlongPath by c.setting("Move Along Path", true) { vis() && page == Page.Movement }
43+
override val kP by c.setting("P Gain", 0.5, 0.0..2.0, 0.01) { vis() && moveAlongPath && page == Page.Movement }
44+
override val kI by c.setting("I Gain", 0.0, 0.0..1.0, 0.01) { vis() && moveAlongPath && page == Page.Movement }
45+
override val kD by c.setting("D Gain", 0.2, 0.0..1.0, 0.01) { vis() && moveAlongPath && page == Page.Movement }
46+
override val tolerance by c.setting("Node Tolerance", 0.6, 0.01..2.0, 0.05) { vis() && moveAlongPath && page == Page.Movement }
47+
override val allowSprint by c.setting("Allow Sprint", true) { vis() && moveAlongPath && page == Page.Movement }
4648

49+
override val rotation = RotationSettings(c) { page == Page.Rotation }
50+
51+
override val renderCoarsePath by c.setting("Render Coarse Path", false) { vis() && page == Page.Misc }
52+
override val renderRefinedPath by c.setting("Render Refined Path", true) { vis() && page == Page.Misc }
53+
override val renderGoal by c.setting("Render Goal", true) { vis() && page == Page.Misc }
4754
override val assumeJesus by c.setting("Assume Jesus", false) { vis() && page == Page.Misc }
4855
}

common/src/main/kotlin/com/lambda/pathing/dstar/DStarLite.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ import kotlin.math.min
3535
*/
3636
class DStarLite(
3737
private val graph: LazyGraph,
38-
private val heuristic: (FastVector, FastVector) -> Double,
3938
var start: FastVector,
40-
private val goal: FastVector
39+
private val goal: FastVector,
40+
private val heuristic: (FastVector, FastVector) -> Double,
4141
) {
4242
// gMap[u], rhsMap[u] store g(u) and rhs(u) or default to ∞ if not present
4343
private val gMap = mutableMapOf<FastVector, Double>()
@@ -58,7 +58,7 @@ class DStarLite(
5858
* - g(goal)=∞, rhs(goal)=0
5959
* - Insert goal into U with key = calculateKey(goal).
6060
*/
61-
private fun initialize() {
61+
fun initialize() {
6262
gMap.clear()
6363
rhsMap.clear()
6464
setG(goal, INF)

common/src/main/kotlin/com/lambda/pathing/dstar/LazyGraph.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package com.lambda.pathing.dstar
1919

20-
import com.lambda.context.SafeContext
2120
import com.lambda.util.world.FastVector
2221

2322
/**
@@ -33,12 +32,14 @@ import com.lambda.util.world.FastVector
3332
* - O(V + E), where V = number of nodes (vertices) initialized and E = number of edges stored.
3433
* - Additional memory overhead is based on the dynamically expanding hash maps.
3534
*/
36-
3735
class LazyGraph(
3836
private val nodeInitializer: (FastVector) -> Map<FastVector, Double>
3937
) {
4038
private val successors = hashMapOf<FastVector, MutableMap<FastVector, Double>>()
4139
private val predecessors = hashMapOf<FastVector, MutableMap<FastVector, Double>>()
40+
private val dirtyNodes = mutableSetOf<FastVector>()
41+
42+
val size get() = successors.size
4243

4344
/** Initializes a node if not already initialized, then returns successors. */
4445
fun successors(u: FastVector) =
@@ -56,6 +57,15 @@ class LazyGraph(
5657
return predecessors[u] ?: emptyMap()
5758
}
5859

60+
fun markDirty(pos: FastVector) {
61+
dirtyNodes.add(pos)
62+
predecessors[pos]?.keys?.let { pred ->
63+
dirtyNodes.addAll(pred)
64+
}
65+
}
66+
67+
fun clearDirty() = dirtyNodes.clear()
68+
5969
/** Returns the cost of the edge from u to v (or ∞ if none exists) */
6070
fun cost(u: FastVector, v: FastVector): Double = successors(u)[v] ?: Double.POSITIVE_INFINITY
6171

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.pathing.move
19+
20+
import com.lambda.util.world.FastVector
21+
import com.lambda.util.world.toBlockPos
22+
23+
class BreakMove(
24+
override val pos: FastVector,
25+
override val hCost: Double,
26+
override val nodeType: NodeType,
27+
override val feetY: Double,
28+
override val cost: Double
29+
) : Move() {
30+
override val name: String = "Break"
31+
32+
override fun toString() = "BreakMove(pos=(${pos.toBlockPos().toShortString()}), hCost=$hCost, nodeType=$nodeType, feetY=$feetY, cost=$cost)"
33+
}

0 commit comments

Comments
 (0)