Tactical commit before major refactor

This commit is contained in:
Simon Brooke 2019-05-18 09:33:50 +01:00
parent 28db4866eb
commit 23e24bd381
12 changed files with 161 additions and 104 deletions

5
.gitignore vendored
View file

@ -2,13 +2,18 @@ pom.xml
pom.xml.asc pom.xml.asc
*.jar *.jar
*.class *.class
*.log
[0-9a-f]*-init.clj
/lib/ /lib/
/classes/ /classes/
/target/ /target/
/checkouts/ /checkouts/
/.clj-kondo/
.eastwood
.lein-deps-sum .lein-deps-sum
.lein-repl-history .lein-repl-history
.lein-plugins/ .lein-plugins/
.lein-failures .lein-failures
.nrepl-port .nrepl-port
.cpcache/ .cpcache/
*~

View file

@ -4,6 +4,7 @@
:output-path "docs" :output-path "docs"
:source-uri "https://github.com/simon-brooke/the-great-game/blob/master/{filepath}#L{line}"} :source-uri "https://github.com/simon-brooke/the-great-game/blob/master/{filepath}#L{line}"}
:dependencies [[org.clojure/clojure "1.8.0"] :dependencies [[org.clojure/clojure "1.8.0"]
[environ "1.1.0"]
[com.taoensso/timbre "4.10.0"]] [com.taoensso/timbre "4.10.0"]]
:description "Prototype code towards the great game I've been writing about for ten years, and know I will never finish." :description "Prototype code towards the great game I've been writing about for ten years, and know I will never finish."
:license {:name "GNU General Public License,version 2.0 or (at your option) any later version" :license {:name "GNU General Public License,version 2.0 or (at your option) any later version"

View file

@ -49,9 +49,9 @@
[gossip world new-location] [gossip world new-location]
(let [id (cond (let [id (cond
(map? gossip) (map? gossip)
(:id (-> world :gossipe gossip) (-> world :gossips gossip :id)
(keyword? gossip) (keyword? gossip)
gossip))] gossip)]
(deep-merge (deep-merge
world world
{:gossips {:gossips

View file

@ -1,8 +1,7 @@
(ns the-great-game.merchants.markets (ns the-great-game.merchants.markets
"Adjusting quantities and prices in markets." "Adjusting quantities and prices in markets."
(:require [taoensso.timbre :as l :refer [info error]] (:require [taoensso.timbre :as l :refer [info error]]
[the-great-game.utils :refer [deep-merge]] [the-great-game.utils :refer [deep-merge]]))
[the-great-game.world.world :refer [actual-price default-world]]))
(defn new-price (defn new-price
"If `stock` is greater than the maximum of `supply` and `demand`, then "If `stock` is greater than the maximum of `supply` and `demand`, then
@ -30,18 +29,23 @@
su (or (-> c :supplies commodity) 0) su (or (-> c :supplies commodity) 0)
decrement (min st d) decrement (min st d)
increment (cond increment (cond
;; if its profitable to produce this commodity, the craftspeople ;; if we've two turns' production of this commodity in
;; of the city will do so. ;; stock, halt production
(> st (* su 2))
0
;; if it is profitable to produce this commodity, the
;; craftspeople of the city will do so.
(> p 1) su (> p 1) su
;; otherwise, if there isn't a turn's production in stock, top up ;; otherwise, if there isn't a turn's production in
;; the stock, so that there's something for incoming merchants to ;; stock, top up the stock, so that there's something for
;; buy ;; incoming merchants to buy
(> su st) (> su st)
(- su st) (- su st)
true 0) :else
0)
n (new-price p st su d)] n (new-price p st su d)]
(if (if
(not (= p n)) (not= p n)
(l/info "Price of" commodity "at" id "has changed from" (float p) "to" (float n))) (l/info "Price of" commodity "at" id "has changed from" (float p) "to" (float n)))
{:cities {id {:cities {id
{:stock {:stock

View file

@ -1,6 +1,6 @@
(ns the-great-game.merchants.merchants (ns the-great-game.merchants.merchants
"Trade planning for merchants, primarily." "Trade planning for merchants, primarily."
(:require [taoensso.timbre :as l :refer [info error]] (:require [taoensso.timbre :as l :refer [info error spy]]
[the-great-game.utils :refer [deep-merge]] [the-great-game.utils :refer [deep-merge]]
[the-great-game.gossip.gossip :refer [move-gossip]] [the-great-game.gossip.gossip :refer [move-gossip]]
[the-great-game.world.routes :refer [find-route]] [the-great-game.world.routes :refer [find-route]]
@ -30,7 +30,7 @@
(-> world :merchants merchant) (-> world :merchants merchant)
(map? merchant) (map? merchant)
merchant) merchant)
cargo (-> m :stock)] cargo (:stock m)]
(reduce (reduce
+ +
0 0
@ -81,10 +81,10 @@
(-> world :merchants merchant) (-> world :merchants merchant)
(map? merchant) (map? merchant)
merchant) merchant)
origin (-> m :location)] origin (:location m)]
(map (map
#(hash-map #(hash-map
:merchant (-> m :id) :merchant (:id m)
:origin origin :origin origin
:destination % :destination %
:commodity commodity :commodity commodity
@ -100,7 +100,7 @@
world world
(:home m) (:home m)
%))) %)))
(remove #(= % origin) (keys (-> world :cities)))))) (remove #(= % origin) (-> world :cities keys)))))
(defn nearest-with-targets (defn nearest-with-targets
"Return the distance to the nearest destination among those of these "Return the distance to the nearest destination among those of these
@ -183,7 +183,7 @@
merchant) merchant)
l (:location m)] l (:location m)]
(quot (quot
(-> m :cash) (:cash m)
(-> world :cities l :prices commodity)))) (-> world :cities l :prices commodity))))
(defn augment-plan (defn augment-plan
@ -219,7 +219,7 @@
(-> world :merchants merchant) (-> world :merchants merchant)
(map? merchant) (map? merchant)
merchant) merchant)
origin (-> m :location) origin (:location m)
available (-> world :cities origin :stock) available (-> world :cities origin :stock)
plans (map plans (map
#(augment-plan #(augment-plan
@ -228,7 +228,7 @@
(plan-trade m world %)) (plan-trade m world %))
(filter (filter
#(let [q (-> world :cities origin :stock %)] #(let [q (-> world :cities origin :stock %)]
(and (number? q) (> q 0))) (and (number? q) (pos? q)))
(keys available)))] (keys available)))]
(if (if
(not (empty? plans)) (not (empty? plans))
@ -281,8 +281,6 @@
a new trade, and bought appropriate stock for it. If no profitable trade a new trade, and bought appropriate stock for it. If no profitable trade
can be planned, the merchant is simply moved towards their home." can be planned, the merchant is simply moved towards their home."
[merchant world] [merchant world]
(deep-merge
world
(let [m (cond (let [m (cond
(keyword? merchant) (keyword? merchant)
(-> world :merchants merchant) (-> world :merchants merchant)
@ -292,18 +290,20 @@
location (:location m) location (:location m)
market (-> world :cities location) market (-> world :cities location)
plan (select-cargo merchant world)] plan (select-cargo merchant world)]
(l/debug "plan-and-buy: merchant" id)
(cond (cond
(not (empty? plan)) (not (empty? plan))
(let (let
[c (:commodity plan) [c (:commodity plan)
p (* (:quantity plan) (:buy-price plan)) p (* (:quantity plan) (:buy-price plan))
q (:quantity plan)] q (:quantity plan)]
(l/info "Merchant " id " bought " q " units of " c " at " location " for " p) (l/info "Merchant" id "bought" q "units of" c "at" location "for" p plan)
{:merchants {:merchants
{id {id
{:stock (add-stock (:stock m) {c q}) {:stock (add-stock (:stock m) {c q})
:cash (- (:cash m) p) :cash (- (:cash m) p)
:known-prices (add-known-prices m world)}} :known-prices (add-known-prices m world)
:plan plan}}
:cities :cities
{location {location
{:stock (assoc (:stock market) c (- (-> market :stock c) q)) {:stock (assoc (:stock market) c (- (-> market :stock c) q))
@ -314,7 +314,7 @@
(l/info "Merchant" id "remains at home in" location) (l/info "Merchant" id "remains at home in" location)
{}) {})
;; else move towards home ;; else move towards home
true :else
(let [route (find-route world location (:home m)) (let [route (find-route world location (:home m))
next-location (nth route 1)] next-location (nth route 1)]
(l/info "No trade possible at" location "; merchant" id "moves to" next-location) (l/info "No trade possible at" location "; merchant" id "moves to" next-location)
@ -322,7 +322,7 @@
{:merchants {:merchants
{id {id
{:location next-location}}} {:location next-location}}}
(move-gossip id world next-location))))))) (move-gossip id world next-location))))))
(defn re-plan (defn re-plan
"Having failed to sell a cargo at current location, re-plan a route to "Having failed to sell a cargo at current location, re-plan a route to
@ -336,6 +336,7 @@
id (:id m) id (:id m)
location (:location m) location (:location m)
plan (augment-plan m world (plan-trade m world (-> m :plan :commodity)))] plan (augment-plan m world (plan-trade m world (-> m :plan :commodity)))]
(l/debug "re-plan: merchant" id)
(deep-merge (deep-merge
world world
{:merchants {:merchants
@ -363,11 +364,11 @@
(map (map
#(* (-> m :stock %) (-> market :prices m)) #(* (-> m :stock %) (-> market :prices m))
(keys (:stock m))))] (keys (:stock m))))]
(l/debug "sell-and-buy: merchant" id)
(if (if
(>= (:cash market) stock-value) (>= (:cash market) stock-value)
(do (do
(l/info (l/info "Merchant" id "sells" (:stock m) "at" location "for" stock-value)
(apply str (flatten (list "Merchant " id " sells " (:stock m) " at " location " for " stock-value))))
(plan-and-buy (plan-and-buy
merchant merchant
(deep-merge (deep-merge
@ -385,7 +386,9 @@
(re-plan merchant world)))) (re-plan merchant world))))
(defn move-merchant (defn move-merchant
"Handle general en route movement of this `merchant` in this `world`." "Handle general en route movement of this `merchant` in this `world`;
return a (partial or full) world like this `world` but in which the
merchant may have been moved ot updated."
[merchant world] [merchant world]
(let [m (cond (let [m (cond
(keyword? merchant) (keyword? merchant)
@ -396,28 +399,42 @@
at-destination? (and (:plan m) (= (:location m) (-> m :plan :destination))) at-destination? (and (:plan m) (= (:location m) (-> m :plan :destination)))
plan (:plan m) plan (:plan m)
next-location (if plan next-location (if plan
(nth 1 (find-route world (:location m) (:destination plan))) (nth
(find-route
world
(:location m)
(:destination plan))
1)
(:location m))] (:location m))]
(l/info "Merchant " id " has moved from " (:location m) " to " next-location) (l/debug "move-merchant: merchant" id "at" (:location m)
"destination" (-> m :plan :destination) "next" next-location
"at destination" at-destination?)
(cond (cond
;; if the merchant is at the destination of their current plan ;; if the merchant is at the destination of their current plan
;; sell all cargo and repurchase. ;; sell all cargo and repurchase.
at-destination? at-destination?
(sell-and-buy merchant world plan) (sell-and-buy merchant world)
;; if they don't have a plan, seek to create one ;; if they don't have a plan, seek to create one
(nil? (:plan m)) (nil? plan)
(plan-and-buy merchant world) (plan-and-buy merchant world)
;; otherwise, move one step towards their destination ;; otherwise, move one step towards their destination
true (and next-location (not= next-location (:location m)))
(do
(l/info "Merchant " id " moving from " (:location m) " to " next-location)
(deep-merge (deep-merge
{:merchants {:merchants
{id {id
{:location next-location {:location next-location
:known-prices (add-known-prices m world)}}} :known-prices (add-known-prices m world)}}}
(move-gossip id world next-location))))) (move-gossip id world next-location)))
:else
(do
(l/info "Merchant" id "has plan but no next-location; currently at"
(:location m) ", destination is" (:destination plan))
world))))
(defn run (defn run
"Return a world like this `world`, but with each merchant moved." "Return a partial world based on this `world`, but with each merchant moved."
[world] [world]
(try (try
(reduce (reduce

View file

@ -0,0 +1,6 @@
(ns the-great-game.merchants.planning
"Trade planning for merchants, primarily."
(:require [taoensso.timbre :as l :refer [info error spy]]
[the-great-game.utils :refer [deep-merge]]
[the-great-game.world.routes :refer [find-route]]
[the-great-game.world.world :refer [actual-price default-world]]))

View file

@ -0,0 +1,2 @@
(ns the-great-game.merchants.strategies.simple
)

View file

@ -3,7 +3,7 @@
(defn cyclic? (defn cyclic?
"True if two or more elements of `route` are identical" "True if two or more elements of `route` are identical"
[route] [route]
(not (= (count route)(count (set route))))) (not= (count route)(count (set route))))
(defn deep-merge (defn deep-merge
"Recursively merges maps. Stolen from "Recursively merges maps. Stolen from

View file

@ -9,11 +9,11 @@
(map (map
(fn [to] (cons from to)) (fn [to] (cons from to))
(remove (remove
#(empty? %) empty?
(map (map
(fn [route] (fn [route]
(filter (remove
#(not (= from %)) #(= from %)
(if (some #(= % from) route) route))) (if (some #(= % from) route) route)))
routes)))) routes))))
([routes from to] ([routes from to]
@ -30,14 +30,12 @@
(not (empty? steps)) (not (empty? steps))
(let [paths (remove (let [paths (remove
cyclic? cyclic?
(apply (mapcat
concat
(map
(fn [path] (fn [path]
(map (map
(fn [x] (concat path (rest x))) (fn [x] (concat path (rest x)))
(find-routes routes (last path)))) (find-routes routes (last path))))
steps))) steps))
found (filter found (filter
#(= (last %) to) paths)] #(= (last %) to) paths)]
(if (if

View file

@ -1,17 +1,39 @@
(ns the-great-game.world.run (ns the-great-game.world.run
"Run the whole simulation" "Run the whole simulation"
(:require [taoensso.timbre :as log] (:require [environ.core :refer [env]]
[taoensso.timbre :as timbre]
[taoensso.timbre.appenders.3rd-party.rotor :as rotor]
[the-great-game.gossip.gossip :as g] [the-great-game.gossip.gossip :as g]
[the-great-game.merchants.merchants :as m] [the-great-game.merchants.merchants :as m]
[the-great-game.merchants.markets :as k] [the-great-game.merchants.markets :as k]
[the-great-game.world.world :as w])) [the-great-game.world.world :as w]))
(defn init
([]
(init {}))
([config]
(timbre/merge-config!
{:appenders
{:rotor (rotor/rotor-appender
{:path "the-great-game.log"
:max-size (* 512 1024)
:backlog 10})}
:level (or
(:log-level config)
(if (env :dev) :debug)
:info)})))
(defn run (defn run
"The pipeline to run the simulation each game day. Returns a world like "The pipeline to run the simulation each game day. Returns a world like
this world, with all the various active elements updated." this world, with all the various active elements updated. The optional
[world] `date` argument, if supplied, is set as the `:date` of the returned world."
([world]
(g/run (g/run
(m/run (m/run
(k/run (k/run
(w/run world))))) (w/run world)))))
([world date]
(g/run
(m/run
(k/run
(w/run world date))))))

View file

@ -183,8 +183,10 @@
(-> world :cities city :prices commodity)) (-> world :cities city :prices commodity))
(defn run (defn run
"Return a world like this `world` with only the `:date` value updated "Return a world like this `world` with only the `:date` to this `date`
(incremented by one). For running other aspects of the simulation, see (or id `date` not supplied, the current value incremented by one). For
[[the-great-game.world.run]]." running other aspects of the simulation, see [[the-great-game.world.run]]."
[world] ([world]
(assoc world :date (inc (or (:date world) 0)))) (run world (inc (or (:date world) 0))))
([world date]
(assoc world :date date)))