X-Git-Url: http://nitlanguage.org diff --git a/lib/a_star.nit b/lib/a_star.nit index 97925ff..7b7ce02 100644 --- a/lib/a_star.nit +++ b/lib/a_star.nit @@ -14,9 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Services related to pathfinding of graphs using A* -# A single graph may have different properties according to the `PathContext` used +# A* pathfinding in graphs # +# A single graph may have different properties according to the `PathContext` used # # Usage: # @@ -55,8 +55,12 @@ # ~~~ module a_star +import serialization + # General graph node class Node + super Serializable + # Type of the others nodes in the `graph` type N: Node @@ -87,14 +91,14 @@ class Node 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] + fun path_to(dest: N, max_cost: Int, context: PathContext): nullable AStarPath[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] + alt_targets: nullable TargetCondition[N]): nullable AStarPath[N] do var cost = 0 @@ -116,7 +120,7 @@ class Node loop var frontier_node: nullable N = null - var bucket_searched: Int = 0 + var bucket_searched = 0 # find next valid node in frontier/buckets loop @@ -143,11 +147,12 @@ class Node else if frontier_node == destination or (alt_targets != null and alt_targets.accept(frontier_node)) then - var path = new Path[N](cost) + var path = new AStarPath[N](cost) while frontier_node != self do path.nodes.unshift(frontier_node) - frontier_node = frontier_node.best_source.as(not null) + frontier_node = frontier_node.best_source + assert frontier_node != null end return path @@ -182,10 +187,32 @@ class Node end end end + + # We customize the serialization process to avoid problems with recursive + # serialization engines. These engines, such as `JsonSerializer`, + # are at danger to serialize the graph as a very deep tree. + # With a large graph it can cause a stack overflow. + # + # Instead, we serialize the nodes first and then the links. + redef fun core_serialize_to(serializer) + do + serializer.serialize_attribute("graph", graph) + end + + redef init from_deserializer(deserializer) + do + deserializer.notify_of_creation self + + var graph = deserializer.deserialize_attribute("graph") + assert graph isa Graph[N, Link] + self.graph = graph + end end # Link between two nodes and associated to a graph class Link + serialize + # Type of the nodes in `graph` type N: Node @@ -209,6 +236,8 @@ end # General graph class Graph[N: Node, L: Link] + super Serializable + # Nodes in this graph var nodes: Set[N] = new HashSet[N] @@ -235,15 +264,35 @@ class Graph[N: Node, L: Link] # Used to check if nodes have been searched in one pathfinding private var pathfinding_current_evocation: Int = 0 + + redef fun core_serialize_to(serializer) + do + serializer.serialize_attribute("nodes", nodes) + serializer.serialize_attribute("links", links) + end + + redef init from_deserializer(deserializer) + do + deserializer.notify_of_creation self + + var nodes = deserializer.deserialize_attribute("nodes") + assert nodes isa HashSet[N] + self.nodes = nodes + + var links = deserializer.deserialize_attribute("links") + assert links isa HashSet[L] + for link in links do add_link link + end end # Result from path finding and a walkable path -class Path[N] +class AStarPath[N] + serialize - # The total cost of this path + # Total cost of this path var total_cost: Int - # The list of nodes composing this path + # Nodes composing this path var nodes = new List[N] private var at: Int = 0 @@ -251,7 +300,7 @@ class Path[N] # Step on the path and get the next node to travel fun step: N do - assert nodes.length >= at else print "a_star::Path::step failed, is at_end_of_path" + assert nodes.length >= at else print "a_star::AStarPath::step failed, is at_end_of_path" var s = nodes[at] at += 1 @@ -267,7 +316,9 @@ class Path[N] end # Context related to an evocation of pathfinding -class PathContext +abstract class PathContext + serialize + # Type of the nodes in `graph` type N: Node @@ -301,6 +352,7 @@ end # Warning: A* is not optimize for such a case class ConstantPathContext super PathContext + serialize redef fun worst_cost do return 1 redef fun cost(l) do return 1 @@ -312,6 +364,7 @@ end # A `PathContext` for graphs with `WeightedLink` class WeightedPathContext super PathContext + serialize redef type L: WeightedLink @@ -327,7 +380,7 @@ class WeightedPathContext self.worst_cost = worst_cost end - redef var worst_cost: Int is noinit + redef var worst_cost is noinit redef fun cost(l) do return l.weight @@ -340,13 +393,16 @@ end # A `Link` with a `weight` class WeightedLink super Link + serialize # The `weight`, or cost, of this link var weight: Int end # Advanced path conditions with customizable accept states -class TargetCondition[N: Node] +abstract class TargetCondition[N: Node] + serialize + # Should the pathfinding accept `node` as a goal? fun accept(node: N): Bool is abstract