001  (ns cc.journeyman.the-great-game.utils)
002  
003  (defn cyclic?
004    "True if two or more elements of `route` are identical"
005    [route]
006    (not= (count route)(count (set route))))
007  
008  (defn deep-merge
009    "Recursively merges maps. Stolen from
010    https://dnaeon.github.io/recursively-merging-maps-in-clojure/"
011    [& maps]
012    (letfn [(m [& xs]
013               (if (some #(and (map? %) (not (record? %))) xs)
014                 (apply merge-with m xs)
015                 (last xs)))]
016      (reduce m maps)))
017  
018  (defn make-target-filter
019    "Construct a filter which, when applied to a list of maps,
020    will pass those which match these `targets`, where each target
021    is a tuple [key value]."
022    ;; TODO: this would probably be more elegant as a macro
023    [targets]
024    (eval
025      (list
026        'fn
027        (vector 'm)
028        (cons
029          'and
030          (map
031            #(list
032               '=
033               (list (first %) 'm)
034               (nth % 1))
035            targets)))))
036  
037  (defn value-or-default
038    "Return the value of this key `k` in this map `m`, or this `dflt` value if
039    there is none."
040    [m k dflt]
041    (or (when (map? m) (m k)) dflt))
042  
043  ;; (value-or-default {:x 0 :y 0 :altitude 7} :altitude 8)
044  ;; (value-or-default {:x 0 :y 0 :altitude 7} :alt 8)
045  ;; (value-or-default nil :altitude 8)