Multiple tables on the same command
So now, what about doing more than one table at a time? Getting three dragons and one snake from the same command line? As soon as you start thinking about using a type of “thing” more than once with different properties, you’re talking about a class of item. Classes are used to take some pieces of code and variables, and put them all together to define that kind of thing. Once we do that, we can use them a lot like we have been using lists and randoms already.
So the first step is to separate the table code into a class:
[toggle code]
- #!/usr/bin/python
- import random
- import optparse
- parser = optparse.OptionParser()
- (options, args) = parser.parse_args()
- #a table is a file of items that we choose randomly from
-
class Table(object):
- #load the table and save it
-
def __init__(self, name):
- filename = name + '.txt'
- items = open(filename).read()
- self.items = items.splitlines()
- #print some random items from the table
-
def choose(self, count):
-
for counter in range(count):
- print counter+1, random.choice(self.items)
-
for counter in range(count):
- firstArgument = args.pop(0)
- #if the first argument is a number, it's the number of random items we want
- #otherwise, it is the table and we want one item from it
-
if firstArgument.isdigit():
- count = int(firstArgument)
- table = args.pop(0)
-
else:
- table = firstArgument
- count = 1
- table = Table(table)
- table.choose(count)
If you run this from the command line now, it will do exactly what it did before. You should test it to make sure.
What’s new? The word “def” stands for “define function”. We’ve created a class and defined some functionality on it. The “__init__()” function is a special function that runs every time you create a new object from a class of something; in this case, every time we create a new table it will run—initialize itself using—that code. So we use that to load the file as a list of items and save it on the object (“self”) for later use.
Now that we’ve separated the table code into a class, however, we can make a more readable multiple-table command line.
[toggle code]
- #!/usr/bin/python
- import random
- import optparse
- parser = optparse.OptionParser()
- (options, args) = parser.parse_args()
- #a table is a file of items that we choose randomly from
-
class Table(object):
- #load the table and save it
-
def __init__(self, name):
- filename = name + '.txt'
- items = open(filename).read()
- self.items = items.splitlines()
- #print some random items from the table
-
def choose(self, count):
-
for counter in range(count):
- print counter+1, random.choice(self.items)
-
for counter in range(count):
-
while args:
- firstArgument = args.pop(0)
- #if the first argument is a number, it's the number of random items we want
- #otherwise, it is the table and we want one item from it
-
if firstArgument.isdigit():
- count = int(firstArgument)
- table = args.pop(0)
-
else:
- table = firstArgument
- count = 1
- table = Table(table)
- table.choose(count)
Note that the only thing different about the lines between “while args” and “print” at the end are that they are indented one tab. Remember that in Python, you indent blocks to show what belongs to what. We did it with the “for”, we did it with the “if” and the “else”, we did it with the “class” and the “def” inside the class, and we’re doing it now with the “while”.
A “while” is a lot like a “for” except that instead of needing a specific list of things to loop over, “while” continues looping as long as some condition is met. In this case, that condition is that there are still arguments to look at.
Because we now are displaying results from more than one table at a time, we also print an empty line after each table, to make each set of results easier to read.
Now you can do more than one thing on the line at a time:
- $ ./random dragons suns snakes
- 1 fire dragon
- 1 Yellow Sun
- 1 coral
Or you can get more than one out of some tables but not others:
- $ ./random 3 dragons suns 2 snakes
- 1 albino dragon
- 2 Rainbow Dragon
- 3 laughing dragon
- 1 Paisley Sun
- 1 viper
- 2 giant snake
One dragon or three?
Take a look at the last command example again. Do you want one random sun, or three? That is, when you leave out the number of random items for a second or third table, do you want it to default to one random item, or do you want it to default to whatever the previous number was? Either is easy, but it’s the kind of thing you’ll need to think about, and watch how you use the script later. Part of the point of writing your own scripts is to make things easier for you.
If you want to default to the previous number, move “count = 1” up above the while:
[toggle code]
- count = 1
-
while args:
- firstArgument = args.pop(0)
- #if the first argument is a number, it's the number of random items we want
- #otherwise, it is the table and we want one item from it
-
if firstArgument.isdigit():
- count = int(firstArgument)
- table = args.pop(0)
-
else:
- table = firstArgument
- table = Table(table)
- table.choose(count)
Different tables for different locales
If you use a script like this extensively you are likely to create different sets of tables for different parts of your game world. You might have one “dragons” table for one part of the world, and another for another part; one snakes table for each part, and so on. The easiest way to manage these different locales is to make a folder for them. The script currently handles that fine.
Go ahead and make two folders in your work area, where you’ve been putting these tables. Call one “West“ and one “East”1. Into each folder, put a “gems.txt” file.
- East/gems.txt
-
- jasper
- sapphire
- chalcedony
- emerald
- sardonyx
- sardius
- West/gems.txt
-
- chrysolyte
- beryl
- topaz
- chrysoprasus
- jacinth
- amethyst
You can run the script to get random gems from the East or West:
- $ ./random East/gems
- 1 emerald
- $ ./random 2 West/gems
- 1 chrysolyte
- 2 topaz
You can also do “./random 2 West/gems 3 dragons” and it will give you two gems from the West and 3 dragons from the general area. But if you get into using a script like this for all of your random tables throughout many areas, you might not remember if the West has its own dragon table or if it uses the generic dragon table.
We can add an option for locale, and then have the script use the table from the locale it if exists, or from the main area if it doesn’t.
[toggle code]
- #!/usr/bin/python
- import random
- import optparse
- import os
- parser = optparse.OptionParser()
- parser.add_option('-l', '--locale')
- (options, args) = parser.parse_args()
- #a table is a file of items that we choose randomly from
-
class Table(object):
- #load the table and save it
-
def __init__(self, name):
- filename = name + '.txt'
- filepath = filename
-
if options.locale:
- localepath = os.path.join(options.locale, filename)
-
if os.path.exists(localepath):
- filepath = localepath
- items = open(filepath).read()
- self.items = items.splitlines()
- #print some random items from the table
-
def choose(self, count):
-
for counter in range(count):
- print counter+1, random.choice(self.items)
-
for counter in range(count):
- count = 1
-
while args:
- firstArgument = args.pop(0)
- #if the first argument is a number, it's the number of random items we want
- #otherwise, it is the table and we want one item from it
-
if firstArgument.isdigit():
- count = int(firstArgument)
- table = args.pop(0)
-
else:
- table = firstArgument
- table = Table(table)
- table.choose(count)
Now, you can specify which folder to look in with --locale, and the script will look in that folder first before defaulting to your overall folder.
- $ ./random 2 gems 3 dragons --locale West
- 1 chrysoprasus
- 2 amethyst
- 1 Night Dragon
- 2 water dragon
- 3 amethyst dragon
There’s a gems.txt in the West folder, but no dragons.txt. So, it pulls dragons from the main file.
In response to Programming for Gamers: Choosing a random item: If you can understand a roleplaying game’s rules, you can understand programming. Programming is a lot easier.
And never the streams shall meet.
↑
More Programming for Gamers
- Are my dice random?
- My d20 appears to have been rolling a lot of ones, a disaster if I were playing D&D but a boon for Gods & Monsters. Is my die really random, or is it skewed towards a particular result? Use the ‘R’ open source statistics tool to find out.
- Programming for Gamers: Choosing a random item
- If you can understand a roleplaying game’s rules, you can understand programming. Programming is a lot easier.
- Easier random tables
- Rather than having to type --table and --count, why not just type the table name and an optional count number?
- Programming a Roman thumb
- Before we move on to more complex stuff with the “random” script, how about something even simpler? Choose or die, Bezonian!
- How random is Python’s random?
- Can you trust a script’s random number generator to actually be random?
- 12 more pages with the topic Programming for Gamers, and other related pages
More Python
- Quick-and-dirty old-school island script
- Here’s a Python-based island generator using the tables from the Judges Guild Island Book 1.
- Astounding Scripts on Monterey
- Monterey removes Python 2, which means that you’ll need to replace it if you’re still using any Python 2 scripts; there’s also a minor change with Layer Windows and GraphicConverter.
- Goodreads: What books did I read last week and last month?
- I occasionally want to look in Goodreads for what I read last month or last week, and that currently means sorting by date read and counting down to the beginning and end of the period in question. This Python script will do that search on an exported Goodreads csv file.
- Test classes and objects in python
- One of the advantages of object-oriented programming is that objects can masquerade as each other.
- Timeout class with retry in Python
- In Paramiko’s ssh client, timeouts don’t seem to work; a signal can handle this—and then can also perform a retry.
- 30 more pages with the topic Python, and other related pages
More random tables
- Island Book 1 and old-school tables
- Judges Guild Island Book 1 is a fascinating playground on which to place a sea-going adventure or campaign. It’s also a great example of the usefulness and wildness of old-school encounter tables.
- Random table rolls
- As often as not, when you roll on a random table you are rolling a random number of times. Now that we have a dice library, we can turn the roll count into a die roll.
- Percentage-based random tables
- Our current random item generator assumes that each item shows up as often as any other item. That’s very OD&D-ish. But AD&D uses percentage dice to weight toward some monsters and items more than others.
- Wandering monster chart assistant
- Use the encounter chart assistant to create wandering monster charts using percentages rather than ranges. Copy your tables into this tool to make sure they add up, adjust them, then copy them back out to your word processor. Never worry about missing or overlapping ranges again!
- Easier random tables
- Rather than having to type --table and --count, why not just type the table name and an optional count number?
- Three more pages with the topic random tables, and other related pages