a-star-pure.scm 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. (library (graph-algorithms a-star-pure)
  2. (export A*)
  3. (import (except (rnrs base)
  4. let-values
  5. map)
  6. (only (guile)
  7. lambda* λ
  8. when
  9. ;; simple-format
  10. ;; with-output-to-string
  11. )
  12. (ice-9 pretty-print)
  13. (srfi srfi-1) ; lists
  14. (srfi srfi-69) ; hash tables
  15. ;; Functional Sets
  16. (pfds sets)
  17. ;; Priority Search Queues
  18. (pfds psqs)
  19. ;; Bounded Balance Trees
  20. (pfds bbtrees)
  21. (pfds-priority-search-queues-addons))
  22. ;; #;(define A*-pure
  23. ;; (lambda* (start
  24. ;; target
  25. ;; nodes
  26. ;; get-neighbors
  27. ;; get-neighbor-distance
  28. ;; cost-heuristic
  29. ;; node<
  30. ;; distance<)
  31. ;; (let ([fringe (psq-set (make-psq node< <)
  32. ;; ;; Initially put the start node in the open-set,
  33. ;; ;; since we need at least some node to go on from.
  34. ;; start
  35. ;; ;; The start node has priority 0, which is the
  36. ;; ;; highest priority. It does not really matter,
  37. ;; ;; since there is only one element in the open-set.
  38. ;; 0)]
  39. ;; ;; routes stores the node preceding any target node on the cheapest
  40. ;; ;; path to that target node. Initially only the preceding node for
  41. ;; ;; the start node is set. The start node itself.
  42. ;; [routes
  43. ;; (bbtree-set (make-bbtree node<) start start)]
  44. ;; ;; score stores the cost of the cheapest path from the start node to
  45. ;; ;; other nodes as currently known. Initially it is only set for the
  46. ;; ;; start node, as one does not know other costs yet.
  47. ;; [cheapest-path-costs
  48. ;; (bbtree-set (make-bbtree node<) start 0)]
  49. ;; ;; Also keep track of a best estimate (calculated using the
  50. ;; ;; heuristic) of the cost from the start node to the target node via
  51. ;; ;; a node. Initially we have not explored any other nodes than the
  52. ;; ;; start node, so the cost for any path via them to the target node
  53. ;; ;; is pessimistically estimated to be infinite. Update formula:
  54. ;; ;; via-node-score-estimate(node) := current-best-score(node) + heuristic(node).
  55. ;; [via-node-score-estimate
  56. ;; ;; Set cost estimate for start node.
  57. ;; (psq-set
  58. ;; ;; Set initial estimates for all nodes.
  59. ;; (fold (λ (node queue) (psq-set queue node +inf.0))
  60. ;; (make-psq node< <)
  61. ;; nodes)
  62. ;; start
  63. ;; (cost-heuristic start))])
  64. ;; (let iter ([current-node°
  65. ;; ;; Initially the current-node° is the one, that is estimated
  66. ;; ;; to have the lowest cost, when a path to the target
  67. ;; ;; contains it. Initially this should be the start node.
  68. ;; #;(psq-min via-node-score-estimate)
  69. ;; ;; Perhaps one can simply put start here.
  70. ;; start]
  71. ;; [fringe° fringe]
  72. ;; [routes° routes]
  73. ;; [cheapest-path-costs° cheapest-path-costs]
  74. ;; [via-node-score-estimate° via-node-score-estimate])
  75. ;; (cond
  76. ;; ;; If the current node is the target node, return the routes.
  77. ;; [(eq? current-node° target) routes°]
  78. ;; [else
  79. ;; ;; TODO: do not forget to remove the current node from the fringe, since we visited it
  80. ;; (let ([all-neighbors (get-neighbors current-node°)])
  81. ;; ;; per neighbor node update the following:
  82. ;; ;; routes, cheapest path cost, via node path cost estimate
  83. ;; (iter (psq-min via-node-score-estimate)
  84. ;; ;; Add all neighbors to the fringe, if they are not yet in
  85. ;; ;; the fringe. Otherwise update their priority values.
  86. ;; (fold (λ (node queue) (psq-set queue node ... #|cheapest known path cost of node|#))
  87. ;; fringe°
  88. ;; all-neighbors)
  89. ;; #;(pretty-print (bbtree->alist (bbtree-set (bbtree-set
  90. ;; (bbtree-set (make-bbtree (lambda (k1 k2) (string<?
  91. ;; (symbol->string k1) (symbol->string k2)))) 'a 4) 'b 3)
  92. ;; 'c 5)))
  93. ;; ;; --> ((a . 4) (b . 3) (c . 5))
  94. ;; )
  95. ;; (let* ([distance (get-neighbor-distance current-node° neighbor)]
  96. ;; ;; At first the tentative score is the distance from the
  97. ;; ;; start to the neighbor going via the current node.
  98. ;; [tentative-score
  99. ;; (+ (hash-table-ref cheapest-path-costs° current-node°)
  100. ;; distance)])
  101. ;; (cond
  102. ;; ;; If we have found a cheaper path to the neighbor ...
  103. ;; [(< tentative-score (hash-table-ref cheapest-path-costs° neighbor))
  104. ;; ;; if tentative_gScore < gScore[neighbor]
  105. ;; ;; // This path to neighbor is better than any previous one. Record it!
  106. ;; ;; cameFrom[neighbor] := current
  107. ;; ;; gScore[neighbor] := tentative_gScore
  108. ;; ;; fScore[neighbor] := tentative_gScore + h(neighbor)
  109. ;; ;; if neighbor not in openSet
  110. ;; ;; openSet.add(neighbor)
  111. ;; ;; ... update the preceding node on the path to the neighbor
  112. ;; ;; in the routes° map ...
  113. ;; (hash-table-set! routes° neighbor current-node°)
  114. ;; ;; ... and update the cheapest path costs for the neighbor
  115. ;; ;; ... (->1)
  116. ;; (hash-table-set! cheapest-path-costs° tentative-score)
  117. ;; ;; ... and update the estimates of the cost of a path from
  118. ;; ;; the start node through the neighbor to the target node.
  119. ;; #|TODO|#]
  120. ;; [else ...])))
  121. ;; ...])))))
  122. ;; ;; // Open set is empty but goal was never reached
  123. ;; ;; return failure
  124. )