Skip to main content

Choose random entries from sequences in a YAML file

Project description

Introduction

There are a number of RPG accessories that allow a GM to build all sorts of things from groups of tables: caverns, catacombs, solar systems, galaxy sectors, names of every description... Now, while it is certainly fun to roll dice it is not always practical and can be time consuming. Hence gametables, a Python program that will read in a YAML file containing the specification of group of tables that will then be randomly rolled. The output is printed on the terminal or sent to a file.

Installation

$ pip install --upgrade gametables

Usage

The gametables script simply takes one or two arguments: the YAML file containing the data and optionally the output (text) file name:

$ python -m gametables monsters.yaml

Or use the console script:

$ gametables monsters.yaml result.txt

YAML file format

Tables and table groups are stored in a YAML 1.2 file. It must contain at least two keys: name, which maps to a string and acts as a reference (or ID) for the table and table which maps to a sequence of entries. For example, the running the file:

---
name: Wandering Monsters
table:
-  goblins
-  orcs
-  hobgoblins

will choose a random entry from the table section. As there are three entries there is a one in three chance of each one being output. In effect, the program is rolling "1d3" on the table. The weighting of an entry can be adjusted by prefixing it with N* where N is the increased weighting. For example:

---
name: Wandering Monsters
table:
-  2* goblins
-  orcs
-  hobgoblins

is the same as:

---
name: Wandering Monsters
table:
-  goblins
-  goblins
-  orcs
-  hobgoblins

An entry can also contain a sequence in "flow" style. For example, consider this file:

---
name: Wandering Monsters
table:
-  [skeletons, zombies]
-  [goblins, hobgoblins]

There is a fifty-fifty chance of choosing the "skeletons, zombies" line, and then a fifty-fifty chance of choosing either "skeletons" or "zombies". Flow sequences can also have weights, e.g. [3* skeletons, zombies], but an entry containing a flow sequence cannot.

An alternative is to use lookup tables, which will be what are printed in most RPGs. Use the lookup parameter, with a value or expression to "roll" and include the ranges on each line. For example:

---
name: Wandering Monsters
lookup: $1d4$
table:
-  1-2 goblins
-  3 orcs
-  4 hobgoblins

Multiple tables can be specified in the same file:

---
name: Wandering Monsters
format: '^'
table:
-  2* goblins
-  orcs
-  hobgoblins
...
---
name: Time of Day
table:
-  morning
-  afternoon
-  night

which would produce something similar to the following:

orcs night

Table Specification

The full specification for a table, with all the allowable options, is as follows:

name

The name of the table, as a string. This is used as a heading (if the header parameter is set to 'True') and to reference the table via a link (see below). Names should be unique, but this is not enforced which might lead to odd results.

table

A sequence of entries that will be selected from. An entry can be:

  • A string, e.g. "goblins", with an optional weighting, e.g. "3* goblins", or
  • A YAML flow sequence, e.g. "['goblins', 'orcs']". Elements of the flow sequence can have weightings as above, but the flow sequence as a whole cannot.
  • It is tricky to have a blank line as an entry in a table. The double underscore "__" sequence will be removed from any entry to facilitate this.

lookup

Instructs the program to treat the table as a lookup table. The ranges must be supplied for each line in A-B format, although if this is not supplied then a "1" is assumed. The default value is false. If true then the dice roll used for the lookup will be calculated from the number of entries in the table, i.e. five entries will lead to a "1d5". (However, this will ignore any ranges that have been given).

However, most of the time lookup field will be an expression, e.g. $2d6$. Variables can also be included (provided they have been set beforehand), e.g. $2d6 + terrain_mod$

format

A string used to format the output of the table. This string should contain the ^ character, which will be replaced by the results of the "roll" on the table. The default is simply ^\n (i.e. the result itself, followed by a newline) and the string ^ (i.e. space and caret) will be added to the format if is does not contain a caret. For example:

---
name: Wandering Monsters
format: There are some ^ in the room
table:
-  goblins
-  orcs
-  hobgoblins

might produce:

There are some orcs in the room

Format strings can contain control characters like tab and newline, but make sure the string is double quoted, e.g. format: "\tNumber: ^ \n". To prevent the table adding a newline replace the default format with the caret character only, i.e. "^".

show

A boolean value determining if the output of the table is displayed. The default is 'true'; set to 'false' if the table will only be referred to via a link.

order

The order (an integer) in which the output from the tables should be displayed. The default order is the order in the YAML document. Specifying two tables with the same order may result in undefined results.

header

A string that is prepended to the output of the entire table (i.e. after all the repeats). As with the format field this will need to be quoted if it contains control characters. For example:

---
name: Wandering Monsters
header: "You have encountered:\n"
format: "    ^\n"
repeat: 2
table:
-  goblins
-  orcs
-  hobgoblins

should result in something similar to:

You have encountered:
    goblins
    orcs

Note that the trailing colon also means that the string must be quoted (as per the YAML specification). Also note that the header will not be printed if the result of the table is empty (i.e. a zero length string).

footer

A string that is appended to the output of the entire table (i.e. after all the repeats).

repeat

The number of times to roll on the table. The default is 1. This can be either an integer or a string. When the table is "rolled" this parameter is evaluated for variables (see below), and converted to an integer (defaulting to 1, if it cannot). So the following are all equivalent (see also Variables, below):

---
name: Wandering Monsters
repeat: 2
table:
-  goblins
-  orcs
-  hobgoblins
---
name: Wandering Monsters
repeat: $num$
vars: $num=2$
table:
-  goblins
-  orcs
-  hobgoblins

variables

A string containing any number of variable assignments (see below). This parameter will be evaluated when the table is read (in fact, when all the tables are read) so it can be used to set up variables for use elsewhere. Note that dice rolls are evaluated before the variables are set so:

variables: $num=1d6$

will set num to a value between 1 and 6.

Note that the variables field of each table is read and evaluated in the order they are read, so it is quite possible to overwrite variables. This field is useful when using modifiers to lookup rolls, as it can be used to set default values.

Table Links

Table entries can contain "links" to other tables. The "linked" table is then rolled and the results replace the "link". To link to another table simply enclose the name of the table in caret characters. For example:

---
name: Wandering Monsters
table:
- ^Goblinoids^
- ^The Undead^
...
---
name: Goblinoids
show: false
table:
-  goblins
-  orcs
-  hobgoblins
...
---
name: The Undead
show: false
table:
-  skeletons
-  zombies
...

has a equal chance of "rolling" on the "Goblinoids" or "The Undead" tables. Note that linked tables typically have show: false to prevent that table being printed out and the output repeated. Table names (and therefore links) are case and space sensitive. Linked tables can themselves contain links, but each table can only be visited or "rolled" 20 times (to prevent infinite recursion).

Links can contain variable references, for example: ^$size$ $type$^ might refer to tables called "Large Undead", "Small Beasts" etc, depending on the value of $size$ and $type$.

Expressions

Table entries can contain simple arithmetic expressions. Variables that have been set will be used in these expressions (but recall that the order in which the tables are "rolled" is important). Any strings of the form XdY will be replaced by the appropriate dice roll (with X as the number of dice and Y the number of sides). This makes it possible to construct tables like this:

---
name: Wandering Monsters
variables: $danger=1d4$
table:
-  $1d6+1+danger$ goblins
-  $1d4-1+danger$ orcs
-  $1d7+danger$ hobgoblins
-  $(2d6*2)+danger-1d4$ kobolds

Dice rolls can also appear in the variables field as part of a variable definition, as well as the lookup field.

Variables

Variables can be defined in the variables parameter, as described above. They can also be defined as part of a table entry, in which case they are set when that entry is output.

Variables are set using the notation $name=value$. They are used simply using the name in an expression.

Variables can evaluate to either strings or integers: if a variable (or, for that matter, the result of an expression) can be converted to an integer it will be. Otherwise it will be a string. This means that if, say, the variable num is not defined when the expression $1d4 + num$ is evaluated the result will be the string 1d4 + num. Integer variables are also formatted using comma separators.

For example:

---
name: Wandering Monsters
table:
-  goblins $num=4$
-  orcs $num=1d2+1$
-  hobgoblins $num=3$
...
---
name: Monster Type
repeat: $num$
table:
-  Warrior
-  Soldier
-  Mage
...

Variables are set first, removing the expression from the entry. Then all expressions are evaluated with the result replacing the expression in the entry. Note that variables are defined in the order they are encountered when outputting the tables: if a table has show: false then any variables it defines will not be set unless some other table runs it via a table link.

All variables are "global" and there is no checking for or protection from overwriting an existing variable.

Example Files

The examples folder in the repo contain some annotated examples of input YAML file. The first is the "Adventure Title" generator from Modiphious' Gamemasters Toolkit (conan_adv_titles.yaml). Some sample results are quite good:

Shadow in the Pool
A/an Empress Is/Will Be/Shall Be Cursed
The Iron Gem of the Skull
A/An Storm of Shadow

Others might need some work:

Bone
The Lost of Queen
Fallen of Specter
Circle of Ring

although "Bone" is not a bad name for a Conan adventure!

Another example is from "Catacombs of Hyboria", Second Edition by Mongoose Publishing (conan_catacombs.yaml). This is a much more complex generator with numerous tables (some of them two dimensional) and various modifiers. Some sample output is:

Type:   Hollow  
Size:   Average 
Area:   400 sq. ft.
Depth:  85 ft.
Terrain:
        Man-Made:    Burial Chamber (decrepit, 50% chance of treasure worth 1200 silver pieces)

and

Type:   Cavern System  
Size:   Average 
Area:   1,800,000 sq. ft.
Depth:  2,550 ft.
Terrain:
        Flora:       Common Growth (area 50%)
        Surface:     Gorge/Cliff (span 60 ft., depth 30 ft.)
        Surface:     Pitted Stone (area 40%)
        Vast Depth:  Ancient Art/Symbols (area 40 sq. ft.)
        Surface:     Stalagmite Field (area 300 sq. ft., average height 18 in.)
        Fauna:       Crawling (insect, 100, waste is prodigious and slick)
        Fauna:       Aerial (gliding mammal, pop. 10,000, has perfect sonar and blind sight)
        Fauna:       Burrowing (mammal, pop. 1,500, naturally venomous and predatory)
        Fauna:       Burrowing (mammal, pop. 500, possesses dangerous self-defence toxins in its flesh)
        Fauna:       Large Creature (reptile, pop. 60, advanced intelligence with problem-solving)
        Surface:     Stalagmite Field (area 450 sq. ft., average height 2 ft.)
        Fauna:       Aerial (lacewing, pop. 6,000, small sized, fast)
        Ceiling:     Optical Illusion of Height
        Vast Depth:  Phosphorescent Growths (area 50%)
        Ceiling:     Optical Illusion of Height
        Vast Depth:  Abyssal Crevasse (width 100 ft.)
        Fauna:       Subterranean (worm, pop. 40, found below 100 ft., has a secondary trait)
        Surface:     Waterway Access (underground river, depth 30 ft.)
        Vast Depth:  Abyssal Crevasse (width 160 ft.)
        Surface:     Stalagmite Field (area 200 sq. ft., average height 1 ft.)
        Unique:      Lair of the Dead

Plots:
        Unearthed Graves

Note that the order of the terrain elements is not sorted. It would be nice if this was an option but one could consider this the order in which the elements are encountered. Also note that full descriptions of elements have not been included, so if you want to know what "Pitted Stone" and "Lair of the Dead" refer to you will need to source the book.

Another example is from the "Star Wars Roleplaying Game: Revised, Expanded, Updated" by Womp Rat Press. This is an expansion of "Star Wars: The Role Playing Game" by West End Games. The file star_wars_planets.yaml contains the world creator in the REUP version. Sample planets generated are:

Star Wars Planet

Function:     Military  
Government:   Organized Crime
Planet Type:  Terrestrial
Terrain:      Volcanic  
Temperature:  Temperate
Gravity:      Heavy 
Atmosphere:   Type I (breathable)
Hydrosphere:  Moist
Day Length:   15 standard hours
Year Length:  465 local days
Starport:     Imperial/Republic class
Population:   5,000
Tech Level:   Hyperspace

and

Star Wars Planet

Function:     Homeworld
Government:   Dictatorship
Planet Type:  Terrestrial
Terrain:      Special
Temperature:  Temperate
Gravity:      Standard
Atmosphere:   Type I (breathable)
Hydrosphere:  Saturated
Day Length:   25 standard hours
Year Length:  345 local days
Starport:     Stellar class
Population:   82,000,000,000
Tech Level:   Hyperspace

Note that the one thing not included here are the "Incompatible Conditions", such as not having an Agricultural planet in an Asteroid Belt.

Development Notes

Unit testing

A small number of tests are included in the test_gametables.py file and can be run using the pytest application.

Packaging a distribution

When ready for a release use the bumpversion application to update the version number, e.g.

$ bumpversion major --tag

This will update the source file and the setup configuration. Then build the distribution:

$ python setup.py bdist_wheel

To Do

  • Catch more YAML and expression parser exceptions
  • Warn of unknown YAML fields?
  • Test literals, folded newlines and multi-line flow scalars
  • Functionality to set up a library of files, then roll a table by name (not file itself)
  • Option to dump YAML file in Markdown format (--dump)
  • Option to toggle integer formatting (--integer)
  • Options for tables to run without output (like show: false but with variables being set etc)
  • Read a folder of YAML files (would have to define order)
  • Read a zip file of YAML files
  • Overall headers and footers (possible as template files)
  • Warning of overwriting existing variables, with option to allow/deny?
  • For lookup tables, check ranges are consistent
  • More formatting options, ideally for input to something like Pandoc? Or test have HTML header/footer

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gametables-0.1.0-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file gametables-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gametables-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/51.1.1 requests-toolbelt/0.9.1 tqdm/4.49.0 CPython/3.8.7

File hashes

Hashes for gametables-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 acc66ed11fabfaa7e4320ed349afb3e1dc5f82059a37a584db7719acf3c19bfb
MD5 35f58b77d890242d87bace48167ecc7c
BLAKE2b-256 fe945b1c6f785e73e1ee9d7ea8d9b0ace345203b336baeeea4f4b96c4dacc598

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page