Skip to content

Commit 2d2bd55

Browse files
committed
Merge branch 'feature/dstar-lite' into feature/pathfinder
2 parents 3d0a784 + 23c8fa2 commit 2d2bd55

File tree

13 files changed

+551
-65
lines changed

13 files changed

+551
-65
lines changed

common/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ dependencies {
5555

5656
// Baritone
5757
modImplementation("baritone-api:baritone-unoptimized-fabric:1.10.2") { isTransitive = false }
58+
testImplementation(kotlin("test"))
5859
}
5960

6061
tasks {
@@ -66,3 +67,9 @@ tasks {
6667
useJUnitPlatform()
6768
}
6869
}
70+
71+
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
72+
kotlinOptions {
73+
jvmTarget = "17"
74+
}
75+
}

common/src/main/kotlin/com/lambda/command/CommandRegistry.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object CommandRegistry : Configurable(LambdaConfig), Loadable {
3232
override val name = "command"
3333
val prefix by setting("prefix", ';')
3434

35-
val commands = getInstances<LambdaCommand> { forPackages("com.lambda.command.commands") }.toMutableList()
35+
val commands = getInstances<LambdaCommand>().toMutableList()
3636

3737
override fun load() = "Loaded ${commands.size} commands with ${dispatcher.root.children()} possible command paths."
3838

common/src/main/kotlin/com/lambda/core/Loader.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object Loader {
3030
val runtime: String
3131
get() = "${(System.currentTimeMillis() - started).milliseconds}"
3232

33-
private val loadables = getInstances<Loadable> { forPackages("com.lambda") }
33+
private val loadables = getInstances<Loadable>()
3434

3535
fun initialize(): Long {
3636
ascii.split("\n").forEach { LOG.info(it) }

common/src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ import net.minecraft.block.BlockState
2424
import net.minecraft.block.Blocks
2525

2626
object ProcessorRegistry : Loadable {
27-
private const val PROCESSOR_PACKAGE = "com.lambda.interaction.construction.processing.processors"
28-
private val processors = getInstances<PlacementProcessor> { forPackages(PROCESSOR_PACKAGE) }
27+
private val processors = getInstances<PlacementProcessor>()
2928
private val processorCache = mutableMapOf<BlockState, PreprocessingStep>()
3029

3130
override fun load() = "Loaded ${processors.size} pre processors"
@@ -41,4 +40,4 @@ object ProcessorRegistry : Loadable {
4140
override fun acceptState(state: BlockState) = true
4241
override fun preProcess(state: BlockState) = PreprocessingStep()
4342
}
44-
}
43+
}

common/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ object ContainerManager : Loadable {
4747
// ToDo: Filter containers based on a filter setting TaskFlowModule.inventory.accessEnderChest etc
4848
get() = compileContainers.filter { it !is EnderChestContainer } + runtimeContainers
4949

50-
private val compileContainers =
51-
getInstances<MaterialContainer> { forPackages("com.lambda.interaction.material.container") }
50+
private val compileContainers = getInstances<MaterialContainer>()
5251
private val runtimeContainers = mutableSetOf<MaterialContainer>()
5352

5453
private var lastInteractedBlockEntity: BlockEntity? = null

common/src/main/kotlin/com/lambda/module/ModuleRegistry.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import com.lambda.util.reflections.getInstances
2525
*/
2626
object ModuleRegistry : Loadable {
2727
override val priority = 1
28-
val modules = getInstances<Module> { forPackages("com.lambda.module.modules") }.toMutableList()
28+
val modules = getInstances<Module>().toMutableList()
2929

3030
val moduleNames: Set<String>
3131
get() = modules.map { it.name }.toSet()
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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.dstar
19+
20+
import com.lambda.util.world.FastVector
21+
import kotlin.math.min
22+
23+
/**
24+
* D* Lite Implementation.
25+
*
26+
* We perform a backward search from the goal to the start, so:
27+
* - rhs(goal) = 0, g(goal) = ∞
28+
* - "start" is the robot's current location from which we want a path *to* the goal
29+
* - 'km' accumulates the heuristic shift so we don't reorder the entire queue after each move
30+
*
31+
* @param graph The graph on which we plan (with forward + reverse adjacency).
32+
* @param heuristic A consistent (or at least nonnegative) heuristic function h(a,b).
33+
* @param start The robot's current position.
34+
* @param goal The fixed goal vertex.
35+
*/
36+
class DStarLite(
37+
private val graph: Graph,
38+
private val heuristic: (FastVector, FastVector) -> Double,
39+
var start: FastVector,
40+
private val goal: FastVector
41+
) {
42+
private val INF = Double.POSITIVE_INFINITY
43+
44+
// gMap[u], rhsMap[u] store g(u) and rhs(u) or default to ∞ if not present
45+
private val gMap = mutableMapOf<FastVector, Double>()
46+
private val rhsMap = mutableMapOf<FastVector, Double>()
47+
48+
// Priority queue holding inconsistent vertices.
49+
private val U = PriorityQueueDStar()
50+
51+
// km accumulates heuristic differences as the start changes.
52+
private var km = 0.0
53+
54+
init {
55+
initialize()
56+
}
57+
58+
/**
59+
* Initialize D* Lite:
60+
* - g(goal)=∞, rhs(goal)=0
61+
* - Insert goal into U with key = calculateKey(goal).
62+
*/
63+
private fun initialize() {
64+
gMap.clear()
65+
rhsMap.clear()
66+
setG(goal, INF)
67+
setRHS(goal, 0.0)
68+
U.insertOrUpdate(goal, calculateKey(goal))
69+
}
70+
71+
private fun g(u: FastVector): Double = gMap[u] ?: INF
72+
private fun setG(u: FastVector, value: Double) { gMap[u] = value }
73+
74+
private fun rhs(u: FastVector): Double = rhsMap[u] ?: INF
75+
private fun setRHS(u: FastVector, value: Double) { rhsMap[u] = value }
76+
77+
/**
78+
* Calculates the key for vertex u.
79+
* Key(u) = ( min(g(u), rhs(u)) + h(start, u) + km, min(g(u), rhs(u)) )
80+
*/
81+
private fun calculateKey(u: FastVector): Key {
82+
val minGRHS = min(g(u), rhs(u))
83+
return Key(minGRHS + heuristic(start, u) + km, minGRHS)
84+
}
85+
86+
/**
87+
* Updates the vertex u.
88+
*
89+
* If u != goal, then:
90+
* rhs(u) = min_{v in Pred(u)} [g(v) + cost(v,u)]
91+
*
92+
* Then u is removed from the queue and reinserted if it is inconsistent.
93+
*/
94+
fun updateVertex(u: FastVector) {
95+
if (u != goal) {
96+
var tmp = INF
97+
graph.predecessors(u).forEach { (pred, cost) ->
98+
val candidate = g(pred) + cost
99+
if (candidate < tmp) {
100+
tmp = candidate
101+
}
102+
}
103+
setRHS(u, tmp)
104+
}
105+
U.remove(u)
106+
if (g(u) != rhs(u)) {
107+
U.insertOrUpdate(u, calculateKey(u))
108+
}
109+
}
110+
111+
/**
112+
* Propagates changes until the start is locally consistent.
113+
* computeShortestPath():
114+
* While the queue top is "less" than calculateKey(start)
115+
* or g(start) < rhs(start), pop and process.
116+
*/
117+
fun computeShortestPath() {
118+
while ((U.topKey() < calculateKey(start)) || (g(start) < rhs(start))) {
119+
val u = U.top() ?: break
120+
val oldKey = U.topKey()
121+
val newKey = calculateKey(u)
122+
123+
if (oldKey < newKey) {
124+
// Priority out-of-date; update it
125+
U.insertOrUpdate(u, newKey)
126+
} else {
127+
U.pop()
128+
if (g(u) > rhs(u)) {
129+
// We found a better path for u
130+
setG(u, rhs(u))
131+
graph.successors(u).forEach { (succ, _) ->
132+
updateVertex(succ)
133+
}
134+
} else {
135+
val oldG = g(u)
136+
setG(u, INF)
137+
updateVertex(u)
138+
// Update successors that may have relied on old g(u)
139+
graph.successors(u).forEach { (succ, _) ->
140+
if (rhs(succ) == oldG + graph.cost(u, succ)) {
141+
updateVertex(succ)
142+
}
143+
}
144+
}
145+
}
146+
}
147+
}
148+
149+
/**
150+
* When the robot moves, update the start.
151+
* The variable km is increased by h(oldStart, newStart) and
152+
* all vertices in the queue are re-keyed.
153+
*/
154+
fun updateStart(newStart: FastVector) {
155+
val oldStart = start
156+
start = newStart
157+
km += heuristic(oldStart, start)
158+
val tmpList = mutableListOf<FastVector>()
159+
while (!U.isEmpty()) {
160+
U.top()?.let { tmpList.add(it) }
161+
U.pop()
162+
}
163+
tmpList.forEach { v ->
164+
U.insertOrUpdate(v, calculateKey(v))
165+
}
166+
}
167+
168+
/**
169+
* Retrieves a path from start to goal by always choosing the successor
170+
* with the lowest g + cost value. If no path is found, the path stops early.
171+
*/
172+
fun getPath(): List<FastVector> {
173+
val path = mutableListOf<FastVector>()
174+
var current = start
175+
path.add(current)
176+
while (current != goal) {
177+
val successors = graph.successors(current)
178+
if (successors.isEmpty()) break
179+
var bestNext: FastVector? = null
180+
var bestVal = INF
181+
for ((succ, cost) in successors) {
182+
val candidate = g(succ) + cost
183+
if (candidate < bestVal) {
184+
bestVal = candidate
185+
bestNext = succ
186+
}
187+
}
188+
// No path
189+
if (bestNext == null) break
190+
current = bestNext
191+
path.add(current)
192+
if (path.size > 100000) break
193+
}
194+
return path
195+
}
196+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.dstar
19+
20+
import com.lambda.util.world.FastVector
21+
22+
/**
23+
* A simple 3D graph that uses FastVector (a Long) to represent 3D nodes.
24+
*
25+
* @param adjMap A map from each vertex to a list of (neighbor, cost) pairs.
26+
*/
27+
class Graph(
28+
private val adjMap: Map<FastVector, List<Pair<FastVector, Double>>>
29+
) {
30+
// Build reverse adjacency from forward edges.
31+
private val revAdjMap: Map<FastVector, List<Pair<FastVector, Double>>> by lazy {
32+
val tmp = mutableMapOf<FastVector, MutableList<Pair<FastVector, Double>>>()
33+
adjMap.forEach { (u, edges) ->
34+
edges.forEach { (v, cost) ->
35+
tmp.getOrPut(v) { mutableListOf() }.add(Pair(u, cost))
36+
}
37+
}
38+
tmp.mapValues { it.value.toList() }
39+
}
40+
41+
/** Returns the successors of a vertex. */
42+
fun successors(u: FastVector) = adjMap[u] ?: emptyList()
43+
44+
/** Returns the predecessors of a vertex. */
45+
fun predecessors(u: FastVector) = revAdjMap[u] ?: emptyList()
46+
47+
/** Returns the cost of the edge from u to v (or ∞ if none exists). */
48+
fun cost(u: FastVector, v: FastVector) =
49+
adjMap[u]?.firstOrNull { it.first == v }?.second ?: Double.POSITIVE_INFINITY
50+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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.dstar
19+
20+
/**
21+
* A Key is a pair (k1, k2) that is used to order vertices in the priority queue.
22+
* They are compared lexicographically.
23+
*/
24+
data class Key(val k1: Double, val k2: Double) : Comparable<Key> {
25+
override fun compareTo(other: Key): Int {
26+
return when {
27+
this.k1 < other.k1 -> -1
28+
this.k1 > other.k1 -> 1
29+
this.k2 < other.k2 -> -1
30+
this.k2 > other.k2 -> 1
31+
else -> 0
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)