001  (ns cc.journeyman.the-great-game.merchants.markets
002    "Adjusting quantities and prices in markets."
003    (:require [taoensso.timbre :as l :refer [info error]]
004              [cc.journeyman.the-great-game.utils :refer [deep-merge]]))
005  
006  (defn new-price
007    "If `stock` is greater than the maximum of `supply` and `demand`, then
008    there is surplus and `old` price is too high, so shold be reduced. If
009    lower, then it is too low and should be increased."
010    [old stock supply demand]
011    (let
012      [delta (dec' (/ (max supply demand 1) (max stock 1)))
013       scaled (/ delta 100)]
014      (+ old scaled)))
015  
016  
017  (defn adjust-quantity-and-price
018    "Adjust the quantity of this `commodity` currently in stock in this `city`
019    of this `world`. Return a fragmentary world which can be deep-merged into
020    this world."
021    [world city commodity]
022    (let [c (cond
023              (keyword? city) (-> world :cities city)
024              (map? city) city)
025          id (:id c)
026          p (or (-> c :prices commodity) 0)
027          d (or (-> c :demands commodity) 0)
028          st (or (-> c :stock commodity) 0)
029          su (or (-> c :supplies commodity) 0)
030          decrement (min st d)
031          increment (cond
032                      ;; if we've two turns' production of this commodity in
033                      ;; stock, halt production
034                      (> st (* su 2))
035                      0
036                      ;; if it is profitable to produce this commodity, the
037                      ;; craftspeople of the city will do so.
038                      (> p 1) su
039                      ;; otherwise, if there isn't a turn's production in
040                      ;; stock, top up the stock, so that there's something for
041                      ;; incoming merchants to buy
042                      (> su st)
043                      (- su st)
044                      :else
045                      0)
046          n (new-price p st su d)]
047      (if
048        (not= p n)
049        (l/info "Price of" commodity "at" id "has changed from" (float p) "to" (float n)))
050      {:cities {id
051                {:stock
052                 {commodity (+ (- st decrement) increment)}
053                 :prices
054                 {commodity n}}}}))
055  
056  
057  (defn update-markets
058    "Return a world like this `world`, with quantities and prices in markets
059    updated to reflect supply and demand. If `city` or `city` and `commodity`
060    are specified, return a fragmentary world with only the changes for that
061    `city` (and `commodity` if specified) populated."
062    ([world]
063     (reduce
064       deep-merge
065       world
066       (map
067         #(update-markets world %)
068         (keys (:cities world)))))
069    ([world city]
070     (reduce
071       deep-merge
072       {}
073       (map #(update-markets world city %)
074            (keys (:commodities world)))))
075    ([world city commodity]
076      (adjust-quantity-and-price world city commodity)))
077  
078  
079  (defn run
080    "Return a world like this `world`, with quantities and prices in markets
081    updated to reflect supply and demand."
082    [world]
083    (update-markets world))
084