\ No newline at end of file
diff --git a/docs/intro.html b/docs/intro.html
index a07111a..a34ef21 100644
--- a/docs/intro.html
+++ b/docs/intro.html
@@ -1,6 +1,6 @@
-Introduction to the-great-game
In this essay I’m going to try to pull together a number of my architectural ideas about the Great Game which I know I’m never actually going to build - because it’s vastly too big for any one person to build - into one overall vision.
\ No newline at end of file
diff --git a/docs/the-great-game.merchants.markets.html b/docs/the-great-game.merchants.markets.html
new file mode 100644
index 0000000..670da43
--- /dev/null
+++ b/docs/the-great-game.merchants.markets.html
@@ -0,0 +1,3 @@
+
+the-great-game.merchants.markets documentation
Adjust the quantity of this commodity currently in stock in this city of this world. Return a fragmentary world which can be deep-merged into this world.
If stock is greater than the maximum of supply and demand, then there is surplus and old price is too high, so shold be reduced. If lower, then it is too low and should be increased.
(update-markets world)(update-markets world city)(update-markets world city commodity)
Return a world like this world, with quantities and prices in markets updated to reflect supply and demand. If city or city and commodity are specified, return a fragmentary world with only the changes for that city (and commodity if specified) populated.
\ No newline at end of file
diff --git a/docs/the-great-game.merchants.merchants.html b/docs/the-great-game.merchants.merchants.html
index 16035ab..30ea5d6 100644
--- a/docs/the-great-game.merchants.merchants.html
+++ b/docs/the-great-game.merchants.merchants.html
@@ -1,8 +1,8 @@
-the-great-game.merchants.merchants documentation
Augment this plan constructed in this world for this merchant with the :quantity of goods which should be bought and the :expected-profit of the trade.
Find the price anticipated, given this world, by this merchant for this commodity in this city. If no information, assume 1. merchant should be passed as a map, commodity and city should be passed as keywords.
Find the best destination in this world for this commodity given this merchant and this origin. If two cities are anticipated to offer the same price, the nearer should be preferred; if two are equally distant, the ones nearer to the merchant’s home should be preferred. merchant may be passed as a map or a keyword; commodity should be passed as a keyword.
Augment this plan constructed in this world for this merchant with the :quantity of goods which should be bought and the :expected-profit of the trade.
Find the price anticipated, given this world, by this merchant for this commodity in this city. If no information, assume 1. merchant should be passed as a map, commodity and city should be passed as keywords.
A merchant, in a given location in a world, will choose to buy a cargo within the limit they are capable of carrying, which they can anticipate selling for a profit at a destination.
Return the distance to the nearest destination among those of these plans which match these targets. Plans are expected to be plans as returned by generate-trade-plans, q.v.; targets are expected to be as accepted by make-target-filter, q.v.
Find the best destination in this world for this commodity given this merchant and this origin. If two cities are anticipated to offer the same price, the nearer should be preferred; if two are equally distant, the ones nearer to the merchant’s home should be preferred. merchant may be passed as a map or a keyword; commodity should be passed as a keyword.
+
The returned plan is a map with keys:
+
+
:merchant - the id of the merchant for whom the plan was created;
+
:origin - the city from which the trade starts;
+
:destination - the city to which the trade is planned;
+
:commodity - the commodity to be carried;
+
:buy-price - the price at which that commodity can be bought;
+
:expected-price - the price at which the merchant anticipates that commodity can be sold;
+
:distance - the number of stages in the planned journey
+
:dist-to-home - the distance from destination to the merchant’s home city.
A merchant, in a given location in a world, will choose to buy a cargo within the limit they are capable of carrying, which they can anticipate selling for a profit at a destination.
\ No newline at end of file
diff --git a/docs/the-great-game.utils.html b/docs/the-great-game.utils.html
index 21859e4..ec15aeb 100644
--- a/docs/the-great-game.utils.html
+++ b/docs/the-great-game.utils.html
@@ -1,3 +1,3 @@
-the-great-game.utils documentation
\ No newline at end of file
diff --git a/docs/the-great-game.world.routes.html b/docs/the-great-game.world.routes.html
index b4b3c08..77af9bb 100644
--- a/docs/the-great-game.world.routes.html
+++ b/docs/the-great-game.world.routes.html
@@ -1,3 +1,3 @@
-the-great-game.world.routes documentation
\ No newline at end of file
diff --git a/docs/the-great-game.world.world.html b/docs/the-great-game.world.world.html
index 0db779d..dc7acdd 100644
--- a/docs/the-great-game.world.world.html
+++ b/docs/the-great-game.world.world.html
@@ -1,3 +1,3 @@
-the-great-game.world.world documentation
Find the actual current price of this commodity in this city given this world. NOTE that merchants can only know the actual prices in the city in which they are currently located.
Find the actual current price of this commodity in this city given this world. NOTE that merchants can only know the actual prices in the city in which they are currently located.
\ No newline at end of file
diff --git a/src/the_great_game/merchants/markets.clj b/src/the_great_game/merchants/markets.clj
new file mode 100644
index 0000000..6ec2d95
--- /dev/null
+++ b/src/the_great_game/merchants/markets.clj
@@ -0,0 +1,76 @@
+(ns the-great-game.merchants.markets
+ "Adjusting quantities and prices in markets."
+ (:require [the-great-game.utils :refer [deep-merge]]
+ [the-great-game.world.world :refer [actual-price default-world]]))
+
+(defn new-price
+ "If `stock` is greater than the maximum of `supply` and `demand`, then
+ there is surplus and `old` price is too high, so shold be reduced. If
+ lower, then it is too low and should be increased."
+ [old stock supply demand]
+ (let
+ [delta (dec' (/ (max supply demand 1) (max stock 1)))
+ scaled (/ delta 100)]
+ (+ old scaled)))
+
+
+(defn adjust-quantity-and-price
+ "Adjust the quantity of this `commodity` currently in stock in this `city`
+ of this `world`. Return a fragmentary world which can be deep-merged into
+ this world."
+ [world city commodity]
+ (let [c (cond
+ (keyword? city) (-> world :cities city)
+ (map? city) city)
+ id (:id c)
+ p (or (-> c :prices commodity) 0)
+ d (or (-> c :demands commodity) 0)
+ st (or (-> c :stock commodity) 0)
+ su (or (-> c :supplies commodity) 0)
+ decrement (min st d)
+ increment (cond
+ ;; if its profitable to produce this commodity, the craftspeople
+ ;; of the city will do so.
+ (> p 1) su
+ ;; otherwise, if there isn't a turn's production in stock, top up
+ ;; the stock, so that there's something for incoming merchants to
+ ;; buy
+ (> su st)
+ (- su st)
+ true 0)
+ price (new-price p st su d)]
+ {:cities {id
+ {:stock
+ {commodity (+ (- st decrement) increment)}
+ :prices
+ {commodity price}}}}))
+
+
+(defn update-markets
+ "Return a world like this `world`, with quantities and prices in markets
+ updated to reflect supply and demand. If `city` or `city` and `commodity`
+ are specified, return a fragmentary world with only the changes for that
+ `city` (and `commodity` if specified) populated."
+ ([world]
+ (reduce
+ deep-merge
+ world
+ (map
+ #(update-markets world %)
+ (keys (:cities world)))))
+ ([world city]
+ (reduce
+ deep-merge
+ {}
+ (map #(update-markets world city %)
+ (keys (:commodities world)))))
+ ([world city commodity]
+ (adjust-quantity-and-price world city commodity)))
+
+
+(defn run
+ "Return a world like this `world`, with quantities and prices in markets
+ updated to reflect supply and demand."
+ [world]
+ (update-markets world))
+
diff --git a/test/the_great_game/merchants/markets_test.clj b/test/the_great_game/merchants/markets_test.clj
new file mode 100644
index 0000000..bc3f515
--- /dev/null
+++ b/test/the_great_game/merchants/markets_test.clj
@@ -0,0 +1,86 @@
+(ns the-great-game.merchants.markets-test
+ (:require [clojure.test :refer :all]
+ [the-great-game.utils :refer [deep-merge]]
+ [the-great-game.world.world :refer [default-world]]
+ [the-great-game.merchants.markets :refer :all]))
+
+
+(deftest new-price-test
+ (testing "Adjustment of prices based on supply and demand"
+ (is (> (new-price 1 0 10 10) 1)
+ "If no stock but there is supply and demand, price should rise")
+ (is (> (new-price 1 0 0 10) 1)
+ "If no stock but there is demand, price should rise")
+ (is (> (new-price 1 5 0 10) 1)
+ "If there is insufficient stock to meet demand, price should rise")
+ (is (= (new-price 1 10 0 10) 1)
+ "If there is exactly sufficient stock to meet demand, price should not change")
+ (is (< (new-price 1 10 0 5) 1)
+ "If there is surplus stock beyond demand, price should fall")
+ (is (> (new-price 1 0 0 10) (new-price 1 5 0 10))
+ "The greater the relative undersupply, the more prices should rise")
+ (is (< (new-price 1 10 0 0)(new-price 1 10 0 5) 1)
+ "The greater the relative oversupply, the more prices should fall")
+ ))
+
+(deftest adjust-qunatity-and-price-test
+ (testing "Adjustment in quantity and price: supply only."
+ (let [world (deep-merge
+ default-world
+ {:cities
+ {:falkirk
+ {:stock {:iron 0}
+ :supplies {:iron 12}}}})
+ actual (adjust-quantity-and-price world :falkirk :iron)]
+ (is
+ (=
+ (-> actual :cities :falkirk :stock :iron)
+ (-> world :cities :falkirk :supplies :iron))
+ "If stock is empty it should be topped up by supply amount."))
+ (let [world (deep-merge
+ default-world
+ {:cities
+ {:falkirk
+ {:stock {:iron 5}
+ :supplies {:iron 12}
+ :prices {:iron 0.9}}}})
+ actual (adjust-quantity-and-price world :falkirk :iron)]
+ (is
+ (=
+ (-> actual :cities :falkirk :stock :iron)
+ (-> world :cities :falkirk :supplies :iron))
+ "If stock is not empty and price is below cost, stock should be topped up only to supply amount."))
+ (let [world (deep-merge
+ default-world
+ {:cities
+ {:falkirk
+ {:stock {:iron 5}
+ :supplies {:iron 12}
+ :prices {:iron 1.1}}}})
+ actual (adjust-quantity-and-price world :falkirk :iron)]
+ (is
+ (=
+ (-> actual :cities :falkirk :stock :iron)
+ (+ (-> world :cities :falkirk :supplies :iron)
+ (-> world :cities :falkirk :stock :iron)))
+ "If stock is not empty and price is above cost, stock should be topped up by supply amount.")))
+ (testing "Adjustment in quantity and price: supply and demand."
+ (let [world (deep-merge
+ default-world
+ {:cities
+ {:falkirk
+ {:stock {:iron 10}
+ :demands {:iron 5}
+ :supplies {:iron 12}
+ :prices {:iron 1.1}}}})
+ actual (adjust-quantity-and-price world :falkirk :iron)]
+ (is
+ (=
+ (-> actual :cities :falkirk :stock :iron)
+ 17)
+ "Stock should be topped up by the difference between the supply and
+ the demand amount."))
+
+ )
+
+ )