@@ -7,22 +7,21 @@ import kotlin.Int.Companion.MAX_VALUE
77// https://en.wikipedia.org/wiki/A*_search_algorithm
88
99
10- private fun <N > reconstructPaths (cameFrom : Map <N , Collection <N >>, last : N ): List <List <N >> {
11- if (! cameFrom.contains(last )) {
12- return listOf (listOf (last ))
10+ private fun <N > reconstructPaths (cameFrom : Map <N , Collection <N >>, currentNode : N ): List <List <N >> {
11+ if (! cameFrom.contains(currentNode )) {
12+ return listOf (listOf (currentNode ))
1313 }
14- return cameFrom.getValue(last )
14+ return cameFrom.getValue(currentNode )
1515 .flatMap { pred -> reconstructPaths(cameFrom, pred) }
16- .map { path -> path + last }
17- .toList()
16+ .map { path -> path + currentNode }
1817}
1918
2019private const val LARGE_VALUE = MAX_VALUE / 2
2120
2221/* *
2322 * A modified A* algorithm that finds all shortest paths from `start` to `goal`.
2423 * @param start the start node
25- * @param isGoal predicate deciding if a node is a goal
24+ * @param isGoal predicate deciding if a node is the goal
2625 * @param neighbors is a function that returns the list of neighbours for a given node.
2726 * @param d is the distance/cost function. d(m,n) provides the distance (or cost) to reach node n from node m.
2827 * @param h is the heuristic function. h(n) estimates the cost to reach goal from node n.
@@ -33,43 +32,45 @@ fun <N> aStarAllPaths(
3332): List <ShortestPath <N >> {
3433 // For node n, fScore[n] := gScore[n] + h(n). fScore[n] represents our current best guess as to
3534 // how cheap a path could be from start to finish if it goes through n.
36- val fScore = mutableMapOf<N , Int >().withDefault { _ -> LARGE_VALUE } // map with default value of Infinity
35+ val fScore = mutableMapOf<N , Int >().withDefault { _ -> LARGE_VALUE } // map with default value of " Infinity"
3736
3837 // The set of discovered nodes that may need to be (re-)expanded.
3938 // Initially, only the start node is known.
4039 // This is usually implemented as a min-heap or priority queue rather than a hash-set.
41- val openSet =
42- PriorityQueue <N > { a, b -> fScore.getValue(a).compareTo(fScore.getValue(b)) }
40+ val openSet = PriorityQueue <N > { a, b -> fScore.getValue(a).compareTo(fScore.getValue(b)) }
4341 openSet.add(start)
4442
45- // For node n, cameFrom[n] is the list of nodes immediately preceding it on the cheapest paths from the start
43+ // For node n, cameFrom[n] is the set of nodes immediately preceding it on the cheapest paths from the start
4644 // to n currently known.
4745 val cameFrom = mutableMapOf<N , MutableSet <N >>()
4846
4947 // For node n, gScore[n] is the currently known cost of the cheapest path from start to n.
50- val gScore = mutableMapOf<N , Int >().withDefault { _ -> LARGE_VALUE } // map with default value of Infinity
48+ val gScore = mutableMapOf<N , Int >().withDefault { _ -> LARGE_VALUE } // map with default value of " Infinity"
5149 gScore[start] = 0
52-
5350 fScore[start] = h(start)
5451
5552 while (openSet.isNotEmpty()) {
5653 // This operation can occur in O(Log(N)) time if openSet is a min-heap or a priority queue
57- val current = openSet.peek ()
54+ val current = openSet.remove ()
5855 if (current.isGoal()) {
59- return reconstructPaths(cameFrom, current).map { x -> ShortestPath (x, gScore.getValue(current)) }
56+ // Search for nodes in openSet with fScore[node] <= gScore[current]
57+ // If the heuristic function is admissible (it never overestimates the actual cost to get to the goal)
58+ // we can be sure to expand all possible paths.
59+ if (openSet.all { n -> fScore.getValue(n) > gScore.getValue(current) }) {
60+ return reconstructPaths(cameFrom, current).map { x -> ShortestPath (x, gScore.getValue(current)) }
61+ }
6062 }
6163
62- openSet.remove(current)
6364 for (neighbor in neighbors(current)) {
6465 // d(current,neighbor) is the weight of the edge from current to neighbor
65- // tentative_gScore is the distance from start to the neighbor through current
66+ // tentativeGScore is the distance from start to the neighbor through current
6667 val tentativeGScore = gScore.getValue(current) + d(current, neighbor)
6768 if (tentativeGScore <= gScore.getValue(neighbor)) {
6869 if (tentativeGScore < gScore.getValue(neighbor)) {
6970 // This path to neighbor is better than any previous one. Record it!
7071 cameFrom[neighbor] = mutableSetOf (current)
7172 } else {
72- // This path to neighbor is equal to the best one. Record it!
73+ // This path to neighbor is equal to the best one. Record it, too !
7374 cameFrom.getOrPut(neighbor) { mutableSetOf () } + = current
7475 }
7576 gScore[neighbor] = tentativeGScore
0 commit comments