Author Topic: Critique my "emergent economy" code  (Read 13093 times)

Clownmite

  • Newcomer
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Critique my "emergent economy" code
« on: May 15, 2012, 02:09:37 AM »
I've been trying to implement an economy as described in this paper: http://larc.unt.edu/techreports/LARC-2010-03.pdf

The idea is that you give a city a "real" economy, which will set its own prices based on supply and demand, and respond to crises and eventually even itself back out. I'd recommend reading or skimming the (short) paper for an idea of how it works.

I've managed to get basic, workable version together for use in my game. The paper was excellent, but made a few important details unclear to me. Nevertheless, my dumbed-down, slightly buggy version seems to work. In my economy, food, wood, and ore prices tend to stay low since they're easy to produce, while metal and tools tend to stay high since the cost of production is more.

Since this is my first publicly-posted piece of code, any general coding tips would be awesome. Also, since I'm hoping to implement this kind of economy in all in-game cities, I'd like to streamline the code to milk all the efficiency I can out of it (especially since I plan on making the resource model much more complex). However, one "round" only needs to be run once every in-game month or so, meaning it won't constantly be a drain on CPU.

Finally, I really need suggestions on better implementation - there are a few quick hacks to make this thing work, but they really need to be fixed.

Also, I have no problem if anyone wants to adapt this for their own games! Just credit me/let me know so that I can check out your game!

The code: [3] http://pastebin.com/eQQLdi0a (uses python 2.7 - and tabs for indentations (sorry!))

oohara

  • Newcomer
  • Posts: 5
  • Karma: +0/-0
    • View Profile
    • Email
Re: Critique my "emergent economy" code
« Reply #1 on: May 16, 2012, 01:41:18 AM »

The code: [3] http://pastebin.com/eQQLdi0a (uses python 2.7 - and tabs for indentations (sorry!))


--- a/economy.py
+++ b/economy.py
@@ -376,7 +376,7 @@
          # All sellers re-evaluate prices - too simplistic as well
          elif len(auction.asks) > 0:
             for seller in auction.asks:
-               seller.owner.eval_bid_rejected(seller.commodity, None)   
+               seller.owner.eval_ask_rejected(seller.commodity, None)   
                auctions[commodity].asks = []
                
          ## Average prices      

Clownmite

  • Newcomer
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #2 on: May 16, 2012, 02:20:33 AM »
-               seller.owner.eval_bid_rejected(seller.commodity, None)   
+               seller.owner.eval_ask_rejected(seller.commodity, None)

Nice catch, thanks! Come to think of it, I no longer have need of the second parameter in this function (remnants from an older version).

chooseusername

  • Rogueliker
  • ***
  • Posts: 329
  • Karma: +0/-0
    • View Profile
    • Email
Re: Critique my "emergent economy" code
« Reply #3 on: May 18, 2012, 07:02:28 AM »
Code: [Select]
random.shuffle(auction.bids)
random.shuffle(auction.asks)
## Sort the bids by price (highest to lowest) ##
auction.bids = sorted(auction.bids, key=lambda attr: attr.price, reverse=True)
## Sort the asks by price (lowest to hghest) ##
auction.asks = sorted(auction.asks, key=lambda attr: attr.price)
Does the shuffling really make a difference?

chooseusername

  • Rogueliker
  • ***
  • Posts: 329
  • Karma: +0/-0
    • View Profile
    • Email
Re: Critique my "emergent economy" code
« Reply #4 on: May 18, 2012, 07:05:32 AM »
Rather than:
Code: [Select]
while not len(auction.bids) == 0 and not len(auction.asks) == 0:Don't you mean:
Code: [Select]
while len(auction.bids) and len(auction.asks):

chooseusername

  • Rogueliker
  • ***
  • Posts: 329
  • Karma: +0/-0
    • View Profile
    • Email
Re: Critique my "emergent economy" code
« Reply #5 on: May 18, 2012, 07:07:37 AM »
ignore

Clownmite

  • Newcomer
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #6 on: May 19, 2012, 05:18:52 AM »
Does the shuffling really make a difference?

I don't think so, but it was in the paper that I linked, so I implemented it. Also, good call on the while loop - I'm still a very new coder if you couldn't tell.

The most notable issue that I'm having trouble with is the bit that determines quantities to buy and sell. Right now, I'm forcing them to buy/sell at least 1 or 2 items every time they bid, but the paper uses some unspecified calculation to determine what quantity the agent will desire (which can sometimes be 0 if the market isn't doing well).

I'm also having trouble with buyers' evaluation of when price is unacceptable - they need to be able to refuse to buy an item if the seller is charging too much, but they need to realize when the seller can't go any lower.

The biggest issue though, is that I'll eventually need this to be able to adapt to items made out of different materials and craft quality - for instance, when do we buy iron tools vs copper tools? And how to set up this distinction coding-wise - I think this means each commodity will have to be a class rather than a string, with a material component, a quality component, and an item component. I'm worried this will be taxing computationally.

Which brings me to my next question - what is the most efficient way to actually express these commodities? In my current code, is it most efficient to have them as strings, and append a string to the agent's inventory list every time they acquire one? Or just have it as a string:quantity dict, which gets its value modified each time a transaction occurs?


requerent

  • Rogueliker
  • ***
  • Posts: 355
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #7 on: May 19, 2012, 08:40:59 AM »
Quote
The most notable issue that I'm having trouble with is the bit that determines quantities to buy and sell. Right now, I'm forcing them to buy/sell at least 1 or 2 items every time they bid, but the paper uses some unspecified calculation to determine what quantity the agent will desire (which can sometimes be 0 if the market isn't doing well).

The algorithm more or less 'inspires' each individual to stock up when a particular product is inexpensive. IE- the farmer may buy an extra plow if the price is really low with the expectation that a future plow will break. The product's durability in conjunction with price determines the propensity to consume. That is, the more plows the farmer has the less he will be willing to pay for a new plow. How many plows the blacksmith sells governs how many he'll produce.

How much the farmer paid for his last plow in relationship to how many plows the farmer already owns determines how many he is going to bid for. If the farmer has 0 plows, he'll always buy 1 plow unless the price-productivity trade-off doesn't add up. If the farmer has 0 plows and the price is amazing, he might buy 3 or 4 plows. If the farmer has 3 or 4 plows and the price is amazing, he'll be unlikely to buy more than one more plow. If you store the max value paid and the min value paid for plows in the farmer, and evaluate the current price with the number of plows owned, you should be able to create reasonable demand. This is more or less the basics of supply and demand.


Quote
I'm also having trouble with buyers' evaluation of when price is unacceptable - they need to be able to refuse to buy an item if the seller is charging too much, but they need to realize when the seller can't go any lower.
See above, but it's also okay to end the market if everyone has what they need.

Quote
The biggest issue though, is that I'll eventually need this to be able to adapt to items made out of different materials and craft quality - for instance, when do we buy iron tools vs copper tools? And how to set up this distinction coding-wise - I think this means each commodity will have to be a class rather than a string, with a material component, a quality component, and an item component. I'm worried this will be taxing computationally.

The invisible hand of your market will take care of that for you. Basically- Farmer needs a plow. An iron mine collapsed three seasons ago or a war began. The Farmer may prefer an iron plow, but the price of the iron plow is going to be an extension of the availability of iron over copper and the overall demand of iron, which is spiked by military demand (or even mandate- either way, military is willing to pay more even if it means inadvertantly crashing other blacksmith dependent industry) or some accident. In other words, you don't really need to worry about this- the algorithm will take care of it for you.

That aside- Copper is heavier, stronger, and maintains an edge better, but is harder to sharpen and must be cast again to repair. You only need to represent this by how many +Food a Copper Plow provides over an Iron Plow and that repairing a Copper Plow is more expensive. If Farmers have skills- like Plow Sharpening and Repairing, a Farmer with lower repair and sharpening skills will go with the Copper, while a farmer with some metal skills may want iron. An extremely good farmer would, obviously, want Copper. If you introduce product grades then availability of raw resources will govern productivity, price and demand.



Quote
Which brings me to my next question - what is the most efficient way to actually express these commodities? In my current code, is it most efficient to have them as strings, and append a string to the agent's inventory list every time they acquire one? Or just have it as a string:quantity dict, which gets its value modified each time a transaction occurs?

String manipulation is probably more taxing than using objects. You don't really need either though- just enumerate every tradeable commodity. Each plow, for example, doesn't need to be represented by a plow object. We just need a static way to access the necessary ancillary data- Who uses it, who makes it, what it is made of, and what its effect is. Like Plato's Theory of Forms. We only need to know that plows exist and how many each person has, not the state of each plow. If each enumeration is mapped to a representative static object, then we only need to know the number of plows a farmer has, not a representation of each plow.

Clownmite

  • Newcomer
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #8 on: May 19, 2012, 02:45:54 PM »
Thanks for the reply!

The algorithm more or less 'inspires' each individual to stock up when a particular product is inexpensive. IE- the farmer may buy an extra plow if the price is really low with the expectation that a future plow will break.

I understand what the algorithm is supposed to do, but I'm worried I didn't implement it right. The paper refers to a few variables that it never defines, so I kind of made my own thing up. If I remove my forced minimum bid quantity of 1, nobody bids on anything and no transactions occur.


Quote
If each enumeration is mapped to a representative static object, then we only need to know the number of plows a farmer has, not a representation of each plow.

My question was more aimed at this exactly. How should I represent how many objects each agent has in their inventories? Having their inventory be a list of strings seems inefficient. Having their inventories be a list of dicts [{commodity:amount}] also seems clunky. I can't think of another way to do this though.

I guess this also relates to item materials and quantity. So I can make plows out of iron or copper. When agents are trading, how to represent this? How does the blacksmith know that he should make both copper and iron tools (if copper is cheaper, he'll just buy that every time). How do these get represented in the trades - 2 separate strings ('iron tools', 'copper tools'), or just 'tools' with the material component somehow attached? I'm sorry if these seem basic questions but I can't wrap my mind around this really low-level implementation stuff.

requerent

  • Rogueliker
  • ***
  • Posts: 355
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #9 on: May 19, 2012, 06:02:36 PM »
The list of all possible items is an enumeration, that means they can be used as indices in a dynamically linked data structure.

When the farmer gains a plow-
Code: [Select]
inventory[plow]++;

If you have a LOT of different material types and they play a significant role in the unique value of the product, then this approach is a bit less practical. You can dynamically populate your enumeration though.

You have a set of master recipes, or some such- that master recipe says that any metal can be used to make a plow. When the blacksmith makes a Gold-Balsa plow the engine creates an instance of plow with the gold-balsa properties. That gets appended to a master list of object-types. When a farmer gets that object, it increments the integer at the same index of the object-type in the master list.

Basically- just use parallel arrays. If they are dynamically linked, you can include grades and stuff like that- you'll only ever have the quantity of objects in the master list that correlates to objects that have been created. The fewer the variants the less memory is used.

Clownmite

  • Newcomer
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #10 on: May 19, 2012, 08:31:59 PM »
When a farmer gets that object, it increments the integer at the same index of the object-type in the master list.

Thanks again for the reply - this bit is where I start to lose you. I'm not a CS guy so I don't have knowledge of typically-used data structures.

So what you're saying is that every time an item is created, if that particular variant is not in the master list of items, append it to that list. The thing appended to the list is a string? A class with all material properties? Then, anytime this particular object is used, the agent's dictionary of objects adds 2 pieces of information: the index of the object in the master list, and the amount of inventory that is changing (so something like agent.inventory[master_list_index] += 1).

Where do the parallel arrays come into play? Is each agent's inventory a set of parallel arrays, 1 array for item name, 1 for item materials, and 1 for item quality? Or is this what the master list is for?

requerent

  • Rogueliker
  • ***
  • Posts: 355
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #11 on: May 19, 2012, 10:00:42 PM »
What are your end objectives? If you describe the model and variables that you wish to implement, it would be easier to make more specific pseudo-code. The system is drastically different if each agent has skills and stats and other junk going on.

Quote
Thanks again for the reply - this bit is where I start to lose you. I'm not a CS guy so I don't have knowledge of typically-used data structures.

This is a data-structure intensive algorithm. If you want optimizations it'll be important to do a little studying later on, but I'm sure the Python libraries are fine.

Quote
So what you're saying is that every time an item is created, if that particular variant is not in the master list of items, append it to that list. The thing appended to the list is a string? A class with all material properties? Then, anytime this particular object is used, the agent's dictionary of objects adds 2 pieces of information: the index of the object in the master list, and the amount of inventory that is changing (so something like agent.inventory[master_list_index] += 1).

Yep. The thing appended to the list is an object. That object probably has 4 pieces of information- what agent makes it, what agent uses it, quality, and a list of the materials used. A dictionary of integers or some such represents the inventory of an agent-- the index of the item in inventory and the index of the master item is the same. So the value at that index in the agent's inventory data-structure is the number of those objects the agent has.

I would suggest focusing on making your code as human-readable as possible, and then worry about optimization later. You're still learning how to implement the algorithm, performance shortcuts might limit your ability to implement features later on. In other words- use objects and inheritence whenever possible.
« Last Edit: May 19, 2012, 10:41:38 PM by requerent »

Clownmite

  • Newcomer
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Critique my "emergent economy" code
« Reply #12 on: May 20, 2012, 04:25:18 AM »
Thanks again for clarifying.

What are your end objectives? If you describe the model and variables that you wish to implement, it would be easier to make more specific pseudo-code. The system is drastically different if each agent has skills and stats and other junk going on.

Here's the objectives: The game will be a procedurally-generated open world-type game containing cities + civilizations. Each city will have a local economy (basically, the above script) based on what resources are nearby. The local economy will be supplemented by trade routes/merchants, which connect cities to each other and resource types. The economy will be able to adapt to crises like forest fires or droughts and adjust prices of goods accordingly. Minimally, commodities will have different traits/prices depending on material used.

The economy code won't need to update often, maybe once or twice per in-game month or so. I'm guessing there will be around 100-150 cities on the map which will have economies attached. Cities will unlock "slots" for agents by building certain structures, but the # of agents does not have to be huge. Cities with populations in the thousands will probably only need ~50 agents or less.