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)
046  
047  (defn truthy? 
048    "Returns `true` unless `val` is `nil`, `false` or an empty sequence.
049     Otherwise always 'false'; never any other value."
050    [val]
051    (and (or val false) true))
052  
053  
054  (defn inc-or-one
055    "If this `val` is a number, return that number incremented by one; otherwise,
056     return 1. TODO: should probably be in `utils`."
057    [val]
058    (if
059     (number? val)
060      (inc val)
061      1))