Skip to content

Deep Dive: Generator Data Patterns

Jesse Morgan edited this page Mar 9, 2014 · 6 revisions

There are three basic types of data storage pattern for generators- List and Weighted data. Before we get that far, we should talk about how a generator knows what data it owns.

At the very lowest level, the name of the generator is used to name the data fields in the backend. Hence the NPC class will contain the following

  • npc_height
  • npc_phobia_chance
  • npc_phobia
  • npc_wisdom

As long as the data field matches 'npc_*', it's considered NPC data. So, what kind of data is stored in the backend?

Data Types

We're using Redis as the backend, and it has several datatypes which are at our disposal to contain the data. These formats both inform and limit how we can store information.

List

The simplest form of data storage for a generator is a list, which uses the redis list datatype:

# LPUSH key      text
LPUSH npc_height short
LPUSH npc_height average height
LPUSH npc_height tall

To select a height in our code, it works something like this:

    # get the total number of values
    total=redis.llen('npc_height')
    # randomly select a number between 1 and the total
    randomid=random.randint(1,total)
    # return the value at that index (remember to offset by one because arrays start at 0)
    redis.lindex('npc_height', randomint-1)

While Redis does offer the ability to randomly select a value, that's of no use to us; while this implementation appears to be random, it's actually using a seeded random value, which means when you set seed=37, the 140th call of randint(1,100) will always return 25. Using redis for random would remove the ability to reproduce worlds.

Weighted

Sometimes you don't want all things being equal; For example, when generating a magical item, you don't want an equal chance of artifact and trivial magical item- Artifacts are the rarest of the rare. Hence we use the redis sorted set:

# ZADD key         score textstring
ZADD magicitem_value  25 {"name":"trivial magical item",  "score": 25   }
ZADD magicitem_value  65 {"name":"minor magical item",    "score": 65   }
ZADD magicitem_value  90 {"name":"medium magical item",   "score": 90   }
ZADD magicitem_value  97 {"name":"major magical item",    "score": 97   }
ZADD magicitem_value 100 {"name":"artifact",              "score": 100  }

There's a lot going on in these lines, so I'll try and break down each section

Weights (score)

Each line has a score associated with it. a number between 1-100 is selected as our "roll". The matching key is then reverse-sorted by score, and the first value between our roll and 0 is selected. Hence if we roll a 94, we reverse sort between 94 and 0, and select the highest score, which is 90- a "medium magical item".

Hence what those score really represent is the percentage chance it has of being selected, with the number below it being the lower end of the range, and it's own score being the higher end of the range.

The Text (JSON)

You'll notice the text area looks like some sorta special code- it's called JSON. Redis simply treats it as a string, but the python code takes another step and creates an object out of it. This gives us the ability to associated many different types of data with a single information point- using a json string gives us that ability.

I should also mention the seemingly redundant "score" value that's included. That's a lazy shortcut I took to simplify some code, which is outside the scope of this conversation. The important part is to make sure it matches the actual score.