001  (ns cc.journeyman.the-great-game.world.routes
002    "Conceptual (plan level) routes, represented as tuples of location ids."
003    (:require [cc.journeyman.the-great-game.utils :refer [cyclic?]]))
004  
005  (defn find-routes
006    "Find routes from among these `routes` from `from`; if `to` is supplied,
007    to `to`, by breadth-first search."
008    ([routes from]
009     (map
010       (fn [to] (cons from to))
011       (remove
012         empty?
013         (map
014           (fn [route]
015             (remove
016               #(= from %)
017               (if (some #(= % from) route) route)))
018           routes))))
019    ([routes from to]
020     (let [steps (find-routes routes from)
021           found (filter
022                   (fn [step] (if (some #(= to %) step) step))
023                   steps)]
024       (if
025         (empty? found)
026         (find-routes routes from to steps)
027         found)))
028    ([routes from to steps]
029     (if
030       (not (empty? steps))
031       (let [paths (remove
032                     cyclic?
033                     (mapcat
034                         (fn [path]
035                           (map
036                             (fn [x] (concat path (rest x)))
037                             (find-routes routes (last path))))
038                         steps))
039             found (filter
040                     #(= (last %) to) paths)]
041         (if
042           (empty? found)
043           (find-routes routes from to paths)
044           found)))))
045  
046  (defn find-route
047    "Find a single route from `from` to `to` in this `world-or-routes`, which
048    may be either a world as defined in [[the-great-game.world.world]] or else
049    a sequence of tuples of keywords."
050    [world-or-routes from to]
051    (first
052      (find-routes
053        (or (:routes world-or-routes) world-or-routes)
054        from
055        to)))