Skip to content

Commit 699ed38

Browse files
committed
Update rhs of newly created nodes
1 parent 6d94e3c commit 699ed38

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ object Pathfinder : Module(
241241

242242
private fun SafeContext.updateDStar(start: FastVector, goal: SimpleGoal) {
243243
val long: Path
244-
val dStar = measureTimeMillis {
244+
val dStarTime = measureTimeMillis {
245245
dStar.updateStart(start)
246246
dStar.computeShortestPath(pathing.cutoffTimeout)
247247
val nodes = dStar.path(pathing.maxPathLength).map { TraverseMove(it, 0.0, NodeType.OPEN, 0.0, 0.0) }
@@ -253,10 +253,11 @@ object Pathfinder : Module(
253253
thetaStarClearance(long, pathing)
254254
} else long
255255
}
256-
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)")
256+
info("Lazy D* Lite (Length: ${long.length().string} Nodes: ${long.size} Graph Size: ${graph.size} T: $dStarTime ms) and \u03b8* (Length: ${short.length().string} Nodes: ${short.size} T: $thetaStar ms)")
257257
// println("Long: $long | Short: $short")
258258
coarsePath = long
259259
refinedPath = short
260+
println(dStar.toString())
260261
}
261262

262263
private fun SafeContext.calculatePID(target: Vec3d): Vec3d {

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,13 @@ class DStarLite(
146146
* Invalidates a node (e.g., it became an obstacle) and updates affected neighbors.
147147
*/
148148
fun invalidate(u: FastVector) {
149+
val newNodes = mutableSetOf<FastVector>()
150+
149151
graph.neighbors(u).forEach { v ->
150152
val current = graph.successors(v)
151153
val updated = graph.nodeInitializer(v)
152154
val removed = current.filter { (w, _) -> w !in updated }
155+
updated.keys.filter { w -> w !in current.keys && w != u }.forEach { newNodes.add(it) }
153156
updateEdge(u, v, INF)
154157
removed.forEach { (w, _) ->
155158
updateEdge(v, w, INF)
@@ -160,6 +163,14 @@ class DStarLite(
160163
updateEdge(w, v, c)
161164
}
162165
}
166+
167+
// Update rhs values for all new nodes
168+
newNodes.forEach { node ->
169+
if (node != goal) {
170+
setRHS(node, minSuccessorCost(node))
171+
updateVertex(node)
172+
}
173+
}
163174
}
164175

165176
/**
@@ -326,14 +337,15 @@ class DStarLite(
326337
appendLine("Top Key: ${U.topKey(Key.INFINITY)}, Top Node: ${U.top().string}")
327338
}
328339
appendLine("Graph Size: ${graph.size}, Invalidated: ${graph.invalidated.size}")
329-
// appendLine("Known Nodes (${graph.nodes.size}):")
330-
// graph.nodes.take(50).forEach {
331-
// appendLine(" ${it.string} g: ${g(it)}, rhs: ${rhs(it)}, key: ${calculateKeyInternal(it)}")
332-
// }
333-
// if (graph.nodes.size > 50) appendLine(" ... (more nodes)")
340+
appendLine("Known Nodes (${graph.nodes.size}):")
341+
val show = 10
342+
graph.nodes.take(show).forEach {
343+
appendLine(" ${it.string} g: ${g(it)}, rhs: ${rhs(it)}, key: ${calculateKey(it)}")
344+
}
345+
if (graph.nodes.size > show) appendLine(" ... (${graph.nodes.size - show} more nodes)")
334346
}
335347

336348
companion object {
337349
private const val INF = Double.POSITIVE_INFINITY
338350
}
339-
}
351+
}

common/src/test/kotlin/pathing/DStarLiteTest.kt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,58 @@ internal class DStarLiteTest {
414414
// The new path should go around the blocked diagonal
415415
assertTrue(newPath.size > initialPath.size, "New path should be longer than the initial path")
416416
}
417+
418+
@Test
419+
fun `invalidate node correctly updates rhs values for new nodes`() {
420+
// Create a straight line path
421+
val startNode = fastVectorOf(0, 0, 0)
422+
val goalNode = fastVectorOf(0, 0, 3)
423+
val localBlockedNodes = mutableSetOf<FastVector>()
424+
val graph = createGridGraph26Conn(localBlockedNodes)
425+
val dStar = DStarLite(graph, startNode, goalNode, ::euclideanHeuristic)
426+
427+
// Compute initial path
428+
dStar.computeShortestPath()
429+
val initialPath = dStar.path()
430+
431+
// Initial path should be straight
432+
assertEquals(4, initialPath.size)
433+
assertEquals(startNode, initialPath.first())
434+
assertEquals(fastVectorOf(0, 0, 1), initialPath[1])
435+
assertEquals(fastVectorOf(0, 0, 2), initialPath[2])
436+
assertEquals(goalNode, initialPath.last())
437+
438+
// Block a node in the middle of the path
439+
val nodeToInvalidate = fastVectorOf(0, 0, 1)
440+
localBlockedNodes.add(nodeToInvalidate)
441+
dStar.invalidate(nodeToInvalidate)
442+
443+
// Recompute path
444+
dStar.computeShortestPath()
445+
val newPath = dStar.path()
446+
447+
// Verify new path avoids the invalidated node
448+
assertTrue(nodeToInvalidate !in newPath, "Path should not contain the invalidated node")
449+
assertEquals(startNode, newPath.first())
450+
assertEquals(goalNode, newPath.last())
451+
452+
// Check if any new nodes were created (nodes that weren't in the initial path)
453+
val newNodes = newPath.filter { it !in initialPath && it != startNode && it != goalNode }
454+
455+
// Verify that new nodes have correct rhs values
456+
newNodes.forEach { node ->
457+
val rhs = dStar.rhs(node)
458+
val minSuccCost = graph.successors(node)
459+
.mapNotNull { (succ, cost) ->
460+
if (cost == Double.POSITIVE_INFINITY) null else cost + dStar.g(succ)
461+
}
462+
.minOrNull() ?: Double.POSITIVE_INFINITY
463+
464+
assertEquals(minSuccCost, rhs, 0.001,
465+
"Node $node should have rhs value equal to minimum successor cost")
466+
}
467+
468+
// The new path should go around the blocked node
469+
assertTrue(newPath.size >= initialPath.size, "New path should be at least as long as the initial path")
470+
}
417471
}

0 commit comments

Comments
 (0)