From 530760ee02088f9df7cc6dd8ccea2a2d249607f9 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 14 May 2019 14:09:45 +0100 Subject: [PATCH] Adjustment of prices and quantities in markets --- docs/index.html | 2 +- docs/intro.html | 2 +- docs/the-great-game.core.html | 2 +- docs/the-great-game.merchants.markets.html | 3 + docs/the-great-game.merchants.merchants.html | 19 +++- docs/the-great-game.utils.html | 2 +- docs/the-great-game.world.routes.html | 2 +- docs/the-great-game.world.world.html | 2 +- src/the_great_game/merchants/markets.clj | 76 ++++++++++++++++ .../the_great_game/merchants/markets_test.clj | 86 +++++++++++++++++++ 10 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 docs/the-great-game.merchants.markets.html create mode 100644 src/the_great_game/merchants/markets.clj create mode 100644 test/the_great_game/merchants/markets_test.clj diff --git a/docs/index.html b/docs/index.html index a3764d2..d73f19a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,3 +1,3 @@ -The-great-game 0.1.0-SNAPSHOT

The-great-game 0.1.0-SNAPSHOT

Released under the GNU General Public License,version 2.0 or (at your option) any later version

Prototype code towards the great game I've been writing about for ten years, and know I will never finish.

Installation

To install, add the following dependency to your project or build file:

[the-great-game "0.1.0-SNAPSHOT"]

Topics

Namespaces

the-great-game.core

TODO: write docs

Public variables and functions:

the-great-game.merchants.merchants

Trade planning for merchants, primarily.

the-great-game.utils

TODO: write docs

Public variables and functions:

the-great-game.world.routes

Conceptual (plan level) routes, represented as tuples of location ids.

Public variables and functions:

the-great-game.world.world

Access to data about the world

Public variables and functions:

\ No newline at end of file +The-great-game 0.1.0-SNAPSHOT

The-great-game 0.1.0-SNAPSHOT

Released under the GNU General Public License,version 2.0 or (at your option) any later version

Prototype code towards the great game I've been writing about for ten years, and know I will never finish.

Installation

To install, add the following dependency to your project or build file:

[the-great-game "0.1.0-SNAPSHOT"]

Topics

Namespaces

the-great-game.core

TODO: write docs

Public variables and functions:

the-great-game.merchants.markets

Adjusting quantities and prices in markets.

Public variables and functions:

the-great-game.utils

TODO: write docs

Public variables and functions:

the-great-game.world.routes

Conceptual (plan level) routes, represented as tuples of location ids.

Public variables and functions:

the-great-game.world.world

Access to data about the world

Public variables and functions:

\ 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

Introduction to the-great-game

+Introduction to the-great-game

Introduction to the-great-game

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.

So, firstly, how does one characterise this game?

diff --git a/docs/the-great-game.core.html b/docs/the-great-game.core.html index 3567217..7697732 100644 --- a/docs/the-great-game.core.html +++ b/docs/the-great-game.core.html @@ -1,3 +1,3 @@ -the-great-game.core documentation

the-great-game.core

TODO: write docs

foo

(foo x)

I don’t do a whole lot.

\ No newline at end of file +the-great-game.core documentation

the-great-game.core

TODO: write docs

foo

(foo x)

I don’t do a whole lot.

\ 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

the-great-game.merchants.markets

Adjusting quantities and prices in markets.

adjust-quantity-and-price

(adjust-quantity-and-price world city commodity)

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.

new-price

(new-price old stock supply demand)

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.

run

(run world)

Return a world like this world, with quantities and prices in markets updated to reflect supply and demand.

update-markets

(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

the-great-game.merchants.merchants

Trade planning for merchants, primarily.

augment-plan

(augment-plan merchant world plan)

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.

-

Returns the augmented plan.

burden

(burden merchant world)

The total weight of the current cargo carried by this merchant in this world.

expected-price

(expected-price merchant commodity city)

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-trade-plan

(find-trade-plan merchant world commodity)

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:

+the-great-game.merchants.merchants documentation

the-great-game.merchants.merchants

Trade planning for merchants, primarily.

augment-plan

(augment-plan merchant world plan)

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.

+

Returns the augmented plan.

burden

(burden merchant world)

The total weight of the current cargo carried by this merchant in this world.

can-afford

(can-afford merchant world commodity)

TODO: write docs

can-carry

(can-carry merchant world commodity)

TODO: write docs

expected-price

(expected-price merchant commodity city)

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.

generate-trade-plans

(generate-trade-plans merchant world commodity)

Generate all possible trade plans for this merchant and this commodity in this world.

+

Returned plans are maps with keys:

  • :merchant - the id of the merchant for whom the plan was created;
  • :origin - the city from which the trade starts;
  • @@ -12,4 +12,15 @@
  • :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.
  • -

select-cargo

(select-cargo merchant world)

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 +

make-target-filter

(make-target-filter targets)

Construct a filter which, when applied to a list of maps, will pass those which match these targets, where each target is a tuple [key value].

nearest-with-targets

(nearest-with-targets plans targets)

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.

plan-trade

(plan-trade merchant world commodity)

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.
  • +

select-cargo

(select-cargo merchant world)

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

the-great-game.utils

TODO: write docs

cyclic?

(cyclic? route)

True if two or more elements of route are identical

deep-merge

(deep-merge & maps)
\ No newline at end of file +the-great-game.utils documentation

the-great-game.utils

TODO: write docs

cyclic?

(cyclic? route)

True if two or more elements of route are identical

deep-merge

(deep-merge & maps)
\ 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

the-great-game.world.routes

Conceptual (plan level) routes, represented as tuples of location ids.

find-routes

(find-routes routes from)(find-routes routes from to)(find-routes routes from to steps)

Find routes from among these routes from from; if to is supplied, to to, by breadth-first search.

\ No newline at end of file +the-great-game.world.routes documentation

the-great-game.world.routes

Conceptual (plan level) routes, represented as tuples of location ids.

find-routes

(find-routes routes from)(find-routes routes from to)(find-routes routes from to steps)

Find routes from among these routes from from; if to is supplied, to to, by breadth-first search.

\ 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

the-great-game.world.world

Access to data about the world

actual-price

(actual-price world commodity city)

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.

default-world

A basic world for testing concepts

\ No newline at end of file +the-great-game.world.world documentation

the-great-game.world.world

Access to data about the world

actual-price

(actual-price world commodity city)

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.

default-world

A basic world for testing concepts

\ 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.")) + + ) + + )