lib/a_star: clean up inits
[nit.git] / lib / a_star.nit
index fa4ef17..580169d 100644 (file)
@@ -29,7 +29,7 @@
 # #  /     /
 # # c -3- d -8- e
 # #
-# var graph = new Graph[Node,WeigthedLink[Node]]
+# var graph = new Graph[Node,WeightedLink]
 #
 # var na = new Node(graph)
 # var nb = new Node(graph)
 # ~~~
 module a_star
 
-redef class Object
-       protected fun debug_a_star: Bool do return false
-       private fun debug(msg: String) do if debug_a_star then
-               stderr.write "a_star debug: {msg}\n"
-       end
-end
-
 # General graph node
 class Node
        type N: Node
@@ -69,9 +62,8 @@ class Node
        # parent graph
        var graph: Graph[N, Link]
 
-       init(graph: Graph[N, Link])
+       init
        do
-               self.graph = graph
                graph.add_node(self)
        end
 
@@ -82,21 +74,27 @@ class Node
        private var last_pathfinding_evocation: Int = 0
 
        # cost up to in current evocation
-       # lifetime limited to evocation of path_to
+       # lifetime limited to evocation of `path_to`
        private var best_cost_up_to: Int = 0
 
        # source node
-       # lifetime limited to evocation of path_to
+       # lifetime limited to evocation of `path_to`
        private var best_source: nullable N = null
 
        # is in frontier or buckets
-       # lifetime limited to evocation of path_to
+       # lifetime limited to evocation of `path_to`
        private var open: Bool = false
 
-
        # Main functionnality, returns path from `self` to `dest`
        fun path_to(dest: N, max_cost: Int, context: PathContext): nullable Path[N]
        do
+               return path_to_alts(dest, max_cost, context, null)
+       end
+
+       # Find a path to a possible `destination` or a node accepted by `alt_targets`
+       fun path_to_alts(destination: nullable N, max_cost: Int, context: PathContext,
+               alt_targets: nullable TargetCondition[N]): nullable Path[N]
+       do
                var cost = 0
 
                var nbr_buckets = context.worst_cost + context.worst_heuristic_cost + 1
@@ -124,14 +122,12 @@ class Node
                                var current_bucket = buckets[cost % nbr_buckets]
 
                                if current_bucket.is_empty then # move to next bucket
-                                       debug "b {cost} {cost % nbr_buckets} {buckets[cost % nbr_buckets].hash}"
                                        cost += 1
                                        if cost > max_cost then return null
                                        bucket_searched += 1
 
                                        if bucket_searched > nbr_buckets then break
                                else # found a node
-                                       debug "c {cost}"
                                        frontier_node = current_bucket.pop
 
                                        if frontier_node.open then break
@@ -143,8 +139,8 @@ class Node
                                return null
 
                        # at destination
-                       else if frontier_node == dest then
-                               debug "picked {frontier_node}, is destination"
+                       else if frontier_node == destination or
+                            (alt_targets != null and alt_targets.accept(frontier_node)) then
 
                                var path = new Path[N](cost)
 
@@ -159,27 +155,27 @@ class Node
                        else
                                frontier_node.open = false
 
-                               debug "w exploring adjacents of {frontier_node}"
-
                                for link in frontier_node.links do
                                        var peek_node = link.to
-                                       debug "v {context.is_blocked(link)} {peek_node.last_pathfinding_evocation != graph.pathfinding_current_evocation} {peek_node.best_cost_up_to > frontier_node.best_cost_up_to + context.cost(link)}, {peek_node.best_cost_up_to} > {frontier_node.best_cost_up_to} + {context.cost(link)}"
                                        if not context.is_blocked(link) and
                                         (peek_node.last_pathfinding_evocation != graph.pathfinding_current_evocation or
                                           (peek_node.open and
                                             peek_node.best_cost_up_to > cost + context.cost(link)))
                                        then
-
                                                peek_node.open = true
                                                peek_node.last_pathfinding_evocation = graph.pathfinding_current_evocation
                                                peek_node.best_cost_up_to = cost + context.cost(link)
                                                peek_node.best_source = frontier_node
 
-                                               var est_cost = peek_node.best_cost_up_to+context.heuristic_cost(link.from, peek_node)
+                                               var est_cost
+                                               if destination != null then
+                                                       est_cost = peek_node.best_cost_up_to + context.heuristic_cost(peek_node, destination)
+                                               else if alt_targets != null then
+                                                       est_cost = peek_node.best_cost_up_to + alt_targets.heuristic_cost(peek_node, link)
+                                               else est_cost = 0
+
                                                var at_bucket = buckets[est_cost % nbr_buckets]
                                                at_bucket.add(peek_node)
-
-                                               debug "u putting {peek_node} at {est_cost} -> {est_cost % nbr_buckets} {at_bucket.hash}, {cost}+{context.cost(link)}"
                                        end
                                end
                        end
@@ -197,12 +193,8 @@ class Link
        var from: N
        var to: N
 
-       init(graph: Graph[N, L], from, to: N)
+       init
        do
-               self.graph = graph
-               self.from = from
-               self.to = to
-
                graph.add_link(self)
        end
 end
@@ -239,7 +231,6 @@ class Path[N]
 
        var nodes = new List[N]
 
-       init (cost: Int) do total_cost = cost
 
        var at: Int = 0
        fun step: N
@@ -300,7 +291,7 @@ class WeightedPathContext
 
        redef type L: WeightedLink
 
-       init(graph: Graph[N, L])
+       init
        do
                super
 
@@ -312,7 +303,7 @@ class WeightedPathContext
                self.worst_cost = worst_cost
        end
 
-       redef var worst_cost: Int
+       redef var worst_cost: Int is noinit
 
        redef fun cost(l) do
                return l.weight
@@ -327,10 +318,13 @@ class WeightedLink
 
        var weight: Int
 
-       init(graph: Graph[N, L], from, to: N, weight: Int)
-       do
-               super
+end
 
-               self.weight = weight
-       end
+# Advanced path conditions with customizable accept states
+class TargetCondition[N: Node]
+       # Should the pathfinding accept `node` as a goal?
+       fun accept(node: N): Bool is abstract
+
+       # Approximate cost from `node` to an accept state
+       fun heuristic_cost(node: N, link: Link): Int is abstract
 end