# the_great_game/merchants/merchant_utils.clj

001 (ns the-great-game.merchants.merchant-utils
002 "Useful functions for doing low-level things with merchants.")
003
004 (defn expected-price
005 "Find the price anticipated, given this `world`, by this `merchant` for
006 this `commodity` in this `city`. If no information, assume 1.
007 `merchant` should be passed as a map, `commodity` and `city` should be passed as keywords."
008 [merchant commodity city]
009 (or
010 (:price
011 (last
012 (sort-by
013 :date
014 (-> merchant :known-prices city commodity))))
015 1))
016
017 (defn burden
018 "The total weight of the current cargo carried by this `merchant` in this
019 `world`."
020 [merchant world]
021 (let [m (cond
022 (keyword? merchant)
023 (-> world :merchants merchant)
024 (map? merchant)
025 merchant)
026 cargo (or (:stock m) {})]
027 (reduce
028 +
029 0
030 (map
031 #(* (cargo %) (-> world :commodities % :weight))
032 (keys cargo)))))
033
034
035 (defn can-carry
036 "Return the number of units of this `commodity` which this `merchant`
037 can carry in this `world`, given their current burden."
038 [merchant world commodity]
039 (let [m (cond
040 (keyword? merchant)
041 (-> world :merchants merchant)
042 (map? merchant)
043 merchant)]
044 (max
045 0
046 (quot
047 (- (or (:capacity m) 0) (burden m world))
048 (-> world :commodities commodity :weight)))))
049
050 (defn can-afford
051 "Return the number of units of this `commodity` which this `merchant`
052 can afford to buy in this `world`."
053 [merchant world commodity]
054 (let [m (cond
055 (keyword? merchant)
056 (-> world :merchants merchant)
057 (map? merchant)
058 merchant)
059 l (:location m)]
060 (cond
061 (nil? m)
062 (throw (Exception. "No merchant?"))
063 (or (nil? l) (nil? (-> world :cities l)))
064 (throw (Exception. (str "No known location for merchant " m)))
065 :else
066 (quot
067 (:cash m)
068 (-> world :cities l :prices commodity)))))
069
070 (defn add-stock
071 "Where `a` and `b` are both maps all of whose values are numbers, return
072 a map whose keys are a union of the keys of `a` and `b` and whose values
073 are the sums of their respective values."
074 [a b]
075 (reduce
076 merge
077 a
078 (map
079 #(hash-map % (+ (or (a %) 0) (or (b %) 0)))
080 (keys b))))
081
082 (defn add-known-prices
083 "Add the current prices at this `merchant`'s location in the `world`
084 to a new cache of known prices, and return it."
085 [merchant world]
086 (let [m (cond
087 (keyword? merchant)
088 (-> world :merchants merchant)
089 (map? merchant)
090 merchant)
091 k (or (:known-prices m) {})
092 l (:location m)
093 d (or (:date world) 0)
094 p (-> world :cities l :prices)]
095 (cond
096 (nil? m)
097 (throw (Exception. "No merchant?"))
098 (or (nil? l) (nil? (-> world :cities l)))
099 (throw (Exception. (str "No known location for merchant " m)))
100 :else
101 (reduce
102 merge
103 k
104 (map
105 #(hash-map % (apply vector cons {:price (p %) :date d} (k %)))
106 (-> world :commodities keys))))))