How should I model entities choosing an optimal counterparty in specs?

(Chechy Levas) #1

I am new to Rust and new to coding with an ECS.
I am building a simulated economy, and using it as an opportunity to learn Rust and ECS.
The core idea is that you have all these economic agents (people, food stores, farms, etc) interacting by engaging in transactions (trading food, trading labor etc).

The cycle I want to use is that at each iteration of the main loop

  1. Some entities advertise a passive offer. For example, each food store will advertise the prices and quantities it is willing to trade food at.
  2. Some entities will browse available offers and select an optimal offer to to try and trade with. Optimality can be based on any arbitrary function of the offer such as proximity and price.
  3. Trades are matched with offers in random order for each offerer and are accepted for as long as the offerer has stock to satisfy the trade.

Then Offerers update their offers based on new stock levels or some other information and we loop back.

So what is an effective way to model this? My thoughts so far, focusing on the case of a Person buying Food from a FoodStore .

It seems clear the following components are required

#[derive(Component)]
struct Money(f64);

#[derive(Component)]
struct Food(f64);

I probably need a marker component to identify a Person or a FoodStore

#[derive(Default)]
#[derive(Component, Debug)]
#[storage(NullStorage)]
struct Person;

#[derive(Default)]
#[derive(Component, Debug)]
#[storage(NullStorage)]
struct FoodStore;

It bothers me that these 2 aren’t mutually exclusive so that already suggests a design failure.

And I expect I need a system for each of the elements of the above mentioned loop.

Thinking about the first system: setting offers. An offer could, in principle, be a component of a food store.
So the first system will focus on those entities that have FoodStore markers and (maybe) food offers. It then adds a new offer component, or mutates an existing one, or deletes an existing one as deemed necessary.

Moving on to the second system. This will focus on those entities that have Person markers and for each person, it needs to get all available offers and choose the best one. But how does each Person get a list of available food offers?

If FoodOffer is a component attached to a FoodStore then I can’t think of how this can be easily accomplished. Since the run function in the System trait filters entities based on components specified in SystemData (Person) and then runs a loop, I can’t immediately see how to then specify an independent filter for FoodStore so that each Person can iterate over the resulting list.

Alternatively, should FoodOffer be it’s own entity? I don’t immediately see how that would help.

The only solution I can think of is to have a Billboard resource that FoodStores advertise to and Person s browse. Not sure if this is considered best practice here. I would expect there to be an idiomatic ECS way to model this kind of interaction where one entity choose some other entity from a list of candidate entities.

It occurs to me that a similar problem would exists in a RTS game, whereby each unit that can attack needs to select an enemy unit to attack based on some optimality condition (usually proximity and how many other units are attacking the same enemy unit). So I am guessing this is a common enough requirement for there to be a well developed pattern.

Any advice would be greatly appreciated.

(Michael Burns) #2

Hi, welcome!

I’ll start with a caveat: I’m also sort of new in town. This should be taken strictly as my opinion, not as some sort of ECS design gospel.

I think your markers are unnecessary (at least when there’s only one item), and this might be some residual OOP-ing. The ECS idiom is more about an entity’s archetype being defined by its set of associated components than having explicit "this is a Foo" markers. In this example, Person and Money could be the same thing if the “stores” don’t care about their coffers. That said, it sounds like you eventually want your stores to buy from primary producers, so that model probably doesn’t work for you in the long run.

Putting that differently, it might make sense to think of your components a little more generically. The attributes of things in your world you care about are really:

  1. Supply
  2. Demand

Maybe those components have something like a map from item type to ((offer_price|price_ceiling), quantity). A satisficing System implementation for executing the trades could do the n^2 naive operation of: for each Demand, check each Supply and transfer items and money (which always has supply 0 unless you’re adding loan providers :slight_smile: ) if offer_price <= price_ceiling. The RTS example you gave makes sense if you want to do a Primer-esque animation on top of it and these interactions are stateful*, but seems unnecessary otherwise. Things also get complicated when you add spatial optimization to any algorithm. Should the buyers know they won’t get to the store before the stock runs out? That isn’t realistic, but might be nice from the perspective of a simple simulation (e.g. the sort of algorithm I described).

Updating prices could be done either in a separate round by a separate system if you accumulate transaction logs in your Supply component or on the fly in the first system if you want prices to change after every transaction, rather than after each round. Again, if you want a temporal dimension with races for items or accounting for limited information per agent, this will be a lot more complicated.

Hope this helped.

[*] If I were doing this, I’d probably have something like a kd-tree or other fast nearest-neighbor structure that accumulated every unit’s Transform (maybe per team) so units could resolve actions in an attack-move state.