Sunday, July 10, 2016

My World, Part 3

First off, I want to share the new and improved design of my trade table. Before, when the table existed only as output on a computer terminal, it was difficult to get the monospaced font and rigid layout to play nice with a file viewer or printer. As you can see below, I've given it a new look, which not only makes it easier to read, but also has made it very easy for me to print off. In addition, since last time I've added a "units" column for those items which require a measurement other than their weight.



The beautification was accomplished by outputting a version of the contents annotated with LaTeX markup to a file, which is then compiled by PDF LaTeX into a PDF for easy use and clear reading.

And for another milestone: as of right now I have reached 99 recipes in my system, an increase of 25 from the listing given in this blog's very first post. See if you can spot all the new items on the table (fewer than 25 new ones, since some of the 25 are undisplayed "partial goods.") Several still need tweaking, and some need serious tweaking, but for now I have numbers I can work with.

Now that I've shown the latest version of the price table output, I'll take a few steps back and return to where I was last time with explaining the pipeline of programs which build up to this table itself. Today's agenda is the imports and pricing calculator, which proceeds in several steps.

  1. Since I have access to a list of all the types of resources and services which are available in my world, we first loop through all of the towns listed in ** Town Info**. If a town doesn't have a given service or material, we set the count for it equal to 0. This step is done purely for programming purposes, not because it represents anything special in the pricing algorithm. If I didn't do this "zeroing out" before anything else, I'd have to have repetitive code later in the file to check each town's info storage to check whether a given resource is present. This way, I can just assume that every town has an entry for every resource.
  2. I copy the Town Info items into a dictionary called Original Town Info. These will be kept so that I can refer ot the original values, since I will be updating the values in the Town Info dictionary in-place during the import step. While doing this, in order to save a step, I also calculate the original global totals for all materials. This is saved in the variable originalWorldResourceCounts (ought to be called ...materialCounts, but this code was written before I'd realized I wanted to reserve the word "resource" to refer to both "material" and "service." Oh well. One day I might change it, but it's in no way a priority.)
  3. We do some definitions which will be needed later. First, by dividing world production into originalWorldResourceCounts for each resource, we get the size of each reference of that resource in "concrete" units -- pounds, ounces, cubic feet, and so on. This is one thing we need to find the price of, say, a pound of iron ore.
    • I don't think it's come up before, but: anywhere you see the Decimal() constructor function, just read it as the number which is parenthesized. Decimal() is a tool for representing decimal and fraction numbers exactly, without being subject to minute errors which arise when working with the basic decimal representation of numbers in Python and most other programming languages. I use numbers wrapped in Decimal() in order to preserve accuracy in my calculations, much like significant figures in chemistry equations.
    • Finally, note that I don't have units for services. I don't yet have a need to figure out exactly how many people 1 reference to "blacksmith" represents. Perhaps some future upgrade will require this.
  4. The import step, which is one of the two major calculations performed in this file (the other is the price-per-concrete-unit step, coming up.) The goal is to determine how much the production of goods elsewhere influences the availability of goods at the town where prices are being calculated. Let's walk through a toy example to show how it works.
    • I have labelled on the graph the distance between each town, and the resources each one has.
    • Here is how importing to town A works:
    1. Let's begin with the products at town B, which is 2 units of grain. The distance from A to B is given here as 3. We add one to this distance: otherwise, in the case of a distance of 1 such as exists between B and C, then the exact same amount which is available at B would be available at C, and vice versa. That is to be avoided: we want travel distance to reduce the movement of goods.
    2. So: having added 1 to the distance of 3 to get 4, we then divide the 2 units of grain originally at B by this 4, and get 0.5. I emphasize "originally" because we do not want to be basing calculation of goods imported from A to B on a figure which includes that which B has already imported! Otherwise we'd be double-dipping: import to B based on A, then import to A from B based on figures which take A's import to B into account, then we could import to B from A and the figure goes up again ... it doesn't make sense. You could do it, but it wouldn't make any sense. There is one round of imports, only, and the amount to import from each place is always determined by the references it ORIGINALLY had, before anywhere did any importing from anywhere else.
    3. Thus, in Town Info (NOT in Original Town Info), we increase A's references to grain by 0.5. The higher that number goes, the cheaper grain will end up being at town A, since it represents more availability.
    4. OK! Partway done for town A. Importing from town C to town A is just the same procedure. The distance from A to C is 5, so our divisor will be 6. C has 1 reference each for sugarcane and copper ore, so A will import 1/6  of a reference for each of those. We add those fractions to Town Info for A. We are now done with importing for town A. Its final reference counts are: (1+1/6) or 7/6 copper ore; 1/6 sugarcane; 0.5 grain. Remember that by using Decimal(), I don't have to worry about calculating fractions like 7/6 by hand, or worrying about repeating decimals numbers like 1/6.
    5. We repeat this procedure for each town, importing from all other towns. Thus the number of import operations performed is N * (N-1): for each of N towns, we import to it from all of the (N-1) other towns.
  5. We establish the value of a reference. I believe my comments in the code are sufficient to explain this step.
  6. Using the value of a reference determined in step 5, the size of each reference as determined in step 3, and each town's post-import reference counts for each material, we can calculate the cost of one unit of a resource at each town. Suppose that the size of iron ore references is determined to be 1000 pounds. Then we want to find out the value for a single pound of iron ore, since having things measured in small amounts makes the recipe definitions much, much easier to write.
    • The algorithm here proceeds in two parts:
    1. For a resource R at town T, take the ratio of T's references of R to world total references for R, and divide the base value of a reference into that ratio to get the price per reference for R at T. The higher T's references to something are, the closer the ratio is to 1, and thus the lower that price per reference is.
    2. Then, divide that base price by the size of the reference, and store this value as the cost for a single "smallest unit" (1 lb, 1 oz, whatever) of the resource R.
      • Let's do an example: let's arbitrarily fix the value of a reference at 1000 gold pieces (GP). Continuing the example above, the references to copper ore at town A are (7/6), and the world total references to copper PRE-IMPORT were 2. Thus the ratio is (7/6)/2 = ~0.583; we divide 1000 GP by this number to get ~1,714.286 GP. This is the base price per reference of copper, at town A.
      • Then, assuming that the size of a copper ore reference is 2000 pounds, we divide again: ~1,714.286 GP / 2000 = ~0.857 GP per pound of copper ore.
      • And there you have it! We now have a starting place for any recipe which works with copper ore; or the price paid to the assayer, if the players want to buy copper ore for some purpose or other.
  7. We do this for all materials (not services), at all towns -- and then we are ready to get cracking on calculating the price of trade goods and their components. This post is very long already, so we'll cover recipes and their structure in the next post, which ought to wrap up the section of this series which deals with trade, at least as far as an overview goes.
Note: the importing algorithm above, and the basic method described here for calculating price-per-reference and then price-per-lb/oz/etc, were originally described by Alexis at the Tao of D&D. Although he's updated his approach in some areas by now, I think they survive mostly intact in my implementation, so let's give credit where it's due. On the other hand, although the idea of combining resources into recipes to get the price of more complex goodsalso comes from Alexis, once we move forward and start talking about the details of the recipes themselves, we will be talking about my own original work and research into the production of commodities in an early modern world. I hope it is at least a little realistic.

2 comments:

  1. Where do you get/how are you determining your gross material quantities?

    ReplyDelete
  2. Dani,

    If you are referring to total world production of goods, e.g. world production of iron ore is such-and-such lbs: I made those figures up. I wish that wasn't the case but without using the Earth as reference I haven't anything to go on, and anyway with so few cities currently in my system, using Earth-like total production figures would also mean super-cheap goods (since each reference to, say, iron ore would be very large, and thus the price for 1 lb of iron ore would be very cheap.) I believe I covered in one of the posts the file which contains world production values, but I don't think I gave a picture.

    In order to address this kind of issue, I'm slowly teasing out the necessary ideas for producing an Earth-based world in my own style, but it is slow going. I am having trouble, for instance, with the programming which would be necessary to either (1) subdivide my hexes into smaller units for the purpose of more detailed road, city, and resource placement, or (2) throw out map hexes and the approximation of hemispheres altogether and work with an actual spherical world. It's something I'd have to commit to one way or another -- either dig into subdivided hexes, or dig into modeling a sphere-world and putting stuff on it, which would make doing Earth my way easier.

    I wish I were a better programmer so that I could zip through these things instead of seeing them as impassable cliffs. If I want my D&D game to be as good as it can be, then I'm going to have to stretch myself as a person, but what is frustrating is that there seems to be so much "architecture"-level stuff that I am paralyzed by, thinking endlessly, because I know once I commit I'm going to be stuck for a good long while. And whenever I turn to this theoretical-but-necessary area of designing my world, that's less time spent getting visible results from the kind of work that comes from improving my background generator or adding to the recipe system. That makes me feel like it's a waste, sometimes.

    ReplyDelete