@@ -51,9 +51,6 @@ import kotlin.test.assertTrue
5151 */
5252
5353internal class DStarLiteTest {
54-
55- private val blockedNodes = mutableSetOf<FastVector >()
56-
5754 // Simple Manhattan distance heuristic
5855 private fun manhattanHeuristic (a : FastVector , b : FastVector ): Double {
5956 return (abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z)).toDouble()
@@ -269,4 +266,152 @@ internal class DStarLiteTest {
269266 val path = dStar.path()
270267 assertEquals(listOf (startNode), path) // Path is just the start/goal node
271268 }
269+
270+ @Test
271+ fun `invalidate node forces path recalculation on 6-conn graph` () {
272+ // Create a graph with a straight path from (0,0,0) to (5,0,0)
273+ val startNode = fastVectorOf(0 , 0 , 0 )
274+ val goalNode = fastVectorOf(5 , 0 , 0 )
275+ val localBlockedNodes = mutableSetOf<FastVector >()
276+ val graph = createGridGraph6Conn(localBlockedNodes)
277+ val dStar = DStarLite (graph, startNode, goalNode, ::manhattanHeuristic)
278+
279+ // Compute initial path
280+ dStar.computeShortestPath()
281+ val initialPath = dStar.path()
282+
283+ // Verify initial path is straight
284+ assertEquals(6 , initialPath.size)
285+ assertEquals(startNode, initialPath.first())
286+ assertEquals(goalNode, initialPath.last())
287+ assertEquals(fastVectorOf(1 , 0 , 0 ), initialPath[1 ])
288+ assertEquals(fastVectorOf(2 , 0 , 0 ), initialPath[2 ])
289+
290+ // Invalidate a node in the middle of the path
291+ val nodeToInvalidate = fastVectorOf(2 , 0 , 0 )
292+ localBlockedNodes.add(nodeToInvalidate)
293+ dStar.invalidate(nodeToInvalidate)
294+
295+ // Recompute path
296+ dStar.computeShortestPath()
297+ val newPath = dStar.path()
298+
299+ // Verify new path avoids the invalidated node
300+ assertTrue(nodeToInvalidate !in newPath, " Path should not contain the invalidated node" )
301+ assertEquals(startNode, newPath.first())
302+ assertEquals(goalNode, newPath.last())
303+
304+ // The new path should be longer as it has to go around the blocked node
305+ assertTrue(newPath.size > initialPath.size, " New path should be longer than the initial path" )
306+ }
307+
308+ @Test
309+ fun `invalidate multiple nodes forces complex rerouting on 6-conn graph` () {
310+ // Create a graph with a straight path from (0,0,0) to (5,0,0)
311+ val startNode = fastVectorOf(0 , 0 , 0 )
312+ val goalNode = fastVectorOf(5 , 0 , 0 )
313+
314+ // Pre-block nodes in the blockedNodes set before creating the graph
315+ val localBlockedNodes = mutableSetOf<FastVector >()
316+ val nodesToBlock = listOf (
317+ fastVectorOf(2 , 0 , 0 ), // Block straight path
318+ fastVectorOf(2 , 1 , 0 ), // Block one alternative
319+ fastVectorOf(2 , - 1 , 0 ) // Block another alternative
320+ )
321+
322+ // Add nodes to blocked set before creating the graph
323+ localBlockedNodes.addAll(nodesToBlock)
324+
325+ // Create graph with pre-blocked nodes
326+ val graph = createGridGraph6Conn(localBlockedNodes)
327+ val dStar = DStarLite (graph, startNode, goalNode, ::manhattanHeuristic)
328+
329+ // Compute path with pre-blocked nodes
330+ dStar.computeShortestPath()
331+ val path = dStar.path()
332+
333+ // Print debug info about the path
334+ println (" [DEBUG_LOG] Path with pre-blocked nodes:" )
335+ path.forEach { node ->
336+ println (" [DEBUG_LOG] - Node: $node (x=${node.x} , y=${node.y} , z=${node.z} )" )
337+ }
338+
339+ // Verify path avoids all blocked nodes
340+ nodesToBlock.forEach { node ->
341+ assertTrue(node !in path, " Path should not contain blocked node $node " )
342+ }
343+
344+ // Verify path starts and ends at the correct nodes
345+ assertEquals(startNode, path.first())
346+ assertEquals(goalNode, path.last())
347+
348+ // The path should be longer than a straight line (which would be 6 nodes)
349+ assertTrue(path.size > 6 , " Path should be longer than a straight line" )
350+ }
351+
352+ @Test
353+ fun `nodeInitializer correctly omits blocked nodes from graph` () {
354+ // Create a set of blocked nodes
355+ val localBlockedNodes = mutableSetOf<FastVector >()
356+ val nodeToBlock = fastVectorOf(2 , 0 , 0 )
357+ localBlockedNodes.add(nodeToBlock)
358+
359+ // Create graph with blocked nodes
360+ val graph = createGridGraph6Conn(localBlockedNodes)
361+
362+ // Check that the nodeInitializer correctly omits the blocked node
363+ val startNode = fastVectorOf(1 , 0 , 0 ) // Node adjacent to blocked node
364+ val successors = graph.successors(startNode)
365+
366+ // The blocked node should not be in the successors
367+ assertFalse(nodeToBlock in successors.keys, " Blocked node should not be in successors" )
368+
369+ // Create a DStarLite instance and compute path
370+ val goalNode = fastVectorOf(3 , 0 , 0 ) // Goal is on the other side of blocked node
371+ val dStar = DStarLite (graph, startNode, goalNode, ::manhattanHeuristic)
372+ dStar.computeShortestPath()
373+ val path = dStar.path()
374+
375+ // Verify path avoids the blocked node
376+ assertTrue(nodeToBlock !in path, " Path should not contain the blocked node" )
377+ assertEquals(startNode, path.first())
378+ assertEquals(goalNode, path.last())
379+ }
380+
381+ @Test
382+ fun `invalidate node updates connectivity on diagonal graph` () {
383+ // Create a graph with diagonal connectivity
384+ val startNode = fastVectorOf(0 , 0 , 0 )
385+ val goalNode = fastVectorOf(2 , 2 , 0 )
386+ val localBlockedNodes = mutableSetOf<FastVector >()
387+ val graph = createGridGraph18Conn(localBlockedNodes)
388+ val dStar = DStarLite (graph, startNode, goalNode, ::euclideanHeuristic)
389+
390+ // Compute initial path
391+ dStar.computeShortestPath()
392+ val initialPath = dStar.path()
393+
394+ // Initial path should be diagonal
395+ assertEquals(3 , initialPath.size)
396+ assertEquals(startNode, initialPath.first())
397+ assertEquals(fastVectorOf(1 , 1 , 0 ), initialPath[1 ])
398+ assertEquals(goalNode, initialPath.last())
399+
400+ // Invalidate the diagonal node
401+ val nodeToInvalidate = fastVectorOf(1 , 1 , 0 )
402+ localBlockedNodes.add(nodeToInvalidate)
403+ dStar.invalidate(nodeToInvalidate)
404+
405+ // Recompute path
406+ dStar.computeShortestPath()
407+ val newPath = dStar.path()
408+
409+ // Verify new path avoids the invalidated node
410+ assertTrue(nodeToInvalidate !in newPath, " Path should not contain the invalidated node" )
411+ assertEquals(startNode, newPath.first())
412+ assertEquals(goalNode, newPath.last())
413+
414+ // The new path should go around the blocked diagonal
415+ assertTrue(newPath.size > initialPath.size, " New path should be longer than the initial path" )
416+ }
272417}
0 commit comments