Tactical commit before major refactor
This commit is contained in:
parent
28db4866eb
commit
23e24bd381
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -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/
|
||||||
|
*~
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
6
src/the_great_game/merchants/planning.clj
Normal file
6
src/the_great_game/merchants/planning.clj
Normal 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]]))
|
2
src/the_great_game/merchants/strategies/simple.clj
Normal file
2
src/the_great_game/merchants/strategies/simple.clj
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(ns the-great-game.merchants.strategies.simple
|
||||||
|
)
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))))))
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
Loading…
Reference in a new issue