Programming students often have trouble understanding certain features of Python. Features such as Classes and Dictionaries can be confusing for beginners. Learning how to use these tools is essential for building applications with Python.
It's important that students get a firm grasp of these concepts early on.
I've crafted this exercise specifically to help students gain a deeper understanding of these ideas. We'll build a random character generator that utilizes advanced features of Python.
This tool is essential for fans of tabletop role playing games.
Anyone who's played a TTRPG like Dungeons and Dragons — or Pathfinder — knows how important NPCs (non-player characters) are to the game. Having well crafted characters makes the game much more immersive and enjoyable to explore.
Using Python, we'll make a program that will create an endless supply of random characters for a fantasy role playing game, complete with detailed descriptions and personality traits.
Here's a preview of what we're going to make:
You can download the final version of the project on GitHub.
For the sake of this exercise, I will assume the reader has the most basic knowledge of Python 3, and is ready to start coding. If you are completely new to Python, or are unsure if you have it installed on your system, check out python.org.
To create the random characters, we'll need two files: one for the program, and one for our data. Lucky for you, I've already created the data file we'll need for this project.
You can download the data file from GitHub.
It contains all the information we'll need to generate a random character: names, traits, weapons, etc. Using this data file, our program will build a new character from scratch.
Create a new folder somewhere on your computer and name it RPG_Character_Builder.
Save the data.py file in this folder and then create a new file. Name it random_character_generator.py.
If we take a look at our data.py file, we'll see a lot of information. To create a random character, our program needs to read through this data and randomly choose from the options available.
# an example of a dictionary with the data for a Bard character.
classes = {
'Bard': {
'weapons': ['Club', 'Longsword', 'Rapier', 'Shortbow', 'Dagger'],
'armor': ['Padded', 'Leather', 'Studded Leather'],
'skills': ['Acrobatics', 'Animal Handling', 'Arcana'],
'abilities': ['Jack of All Trades', “Song of Rest”],
'spells': ['Dancing Lights', 'Vicious Mockery'],
'hp': 6,
'gold': '5d4',
'background': 'entertainer',
'saving throws': ['Dexterity', 'Charisma']
}
}
How do we do that? First, we need to understand how the data is stored. Take a look at the information about the different races available for each character. If you've ever seen Lord of the Rings, you'll recognize 'Elf' and 'Dwarf” among the options.
This seemingly complex table is what Python refers to as a dictionary. Dictionaries are extremely useful for a variety of reasons. They work by pairing a key with a value. Using the key, we can later retrieve the paired value.
For instance, if we had a dictionary called books and we created a key called “Moby Dick,” we could pair it with the value “A book about a sea captain who is really mad about a whale.”
In our data set, the key 'Elf' is paired with another dictionary, one containing all the values we need to create an elven character from scratch.
In order to use this data, we'll need to hop over to the other file you created: random_character_generator.py.
We'll start off by importing the data file.
import data
That's it! As long as data.py is in the same folder as our program, we'll have access to our data.
We can test it by writing:
for key in data.classes:
print(key)
Running this code should give you a list of all the classes available for the character generator to choose from.
Before we can hope to generate a character, we'll first need to create something that can represent the character we'd like to make. Looking through our data file, we see that each character has several features: a race, a class, a first name, a last name, etc.
A generated character would have all of these features as well. For example, our program might create a human wizard named Tom Bombadil.
How do we keep track of those features? We'll use a Python Class!
In Python, Classes provide a way of bundling functionality and data. This is refereed to as an object.
For our purposes, we'll use a Class to hold the information about each random character. Once we've created the Class, we can use it to create any number of new characters, each with their own unique data.
To create a new Class in Python, use the 'class' keyword.
class Character():
c_name = “John”
c_age = 42
In this example, we've created a Class with two variables: c_name and c_age.
We can give Classes functionality. It's a good idea to have a string representation of our Class. What if we want to print() the class?
We can use a special function to decide what is printed:
class Character():
c_name = “John”
c_age = 42
def __str__(self):
return self.c_name + ": " + str(self.c_age) + "."
With the Class defined, we can create new character objects and set its data members.
Our final Class will have many data members. It's a good practice to name these variables something self-explanatory and unique.
Once our Class is created, we can move on to randomly generating a character. This will be the core of our program, handling all the logic necessary to create a random fantasy character.
Before we can begin, however, we need to include another library: the random library. With it, we can generate random numbers. Add the following line to the top of random_character_generator.py:
import random
The random library comes with many ways of computing random numbers. We'll use randrange. This function will return a number between two given values. The following example illustrates how randrange works:
Use randrange to find a random number from a to b.
We'll use randrange to select a random starting character class and race for each new character. We'll then use randrange to roll new statistics that can be used to play the game.
Let's start by giving the character a random character class. First, we'll need to get a list of all the character classes available in the data. Then we'll choose a character class at random and assign it to the new_character object we created at the top of the function.
def CreateNewCharacter():
new_character = Character()
classes = []
for _class in data.classes:
classes.append(_class)
new_character.c_class=classes[random.randrange(0,len(classes))]
Next, we can give our character a fantasy race using the same methods.
races = []
for race in data.races:
races.append(race)
new_character.c_race = races[random.randrange(0,len(races))]
In most role playing games, characters have statistics, or stats. These stats give an overall pictures of the character's abilities. Each character we create will have the following stats: strength, dexterity, constitution, intelligence, wisdom, and charisma.
To find a stat, players roll 4 six-sided dice, and then add up the 3 highest values. Before we can roll stats in our program, we'll need to create a function that will simulate the rolling of the dice.
def RollStat():
dice = []
# roll 4 six sided dice.
for i in range(4):
dice.append(random.randrange(1,7))
total = 0
lowest = dice[0]
# add up all the dice
for num in dice:
total += num
# find the lowest value
for num in dice:
if num < lowest:
lowest = num
return total - lowest
This functions first generates four random numbers. Then it finds their total. Lastly, it searches for the lowest number in the set, then subtracts it from the total.
The most complex part of our program is naming our character. Because each fantasy race has its own naming conventions, we'll need to perform a series of checks before we can get an appropriate name.
If the character is human, we'll need to first decide what 'type' of human they are. Our data shows that humans come in a variety of groups (Calishite, Chondathan, Damaran, Illuskan, etc). We'll pick names based on what type of human is randomly chosen.
If the character is Half-Elf, we'll need to compile a list of names that incorporates both humans and elves. We'll also have to choose a type of human before we can get a list of appropriate names.
# names are the most complicated part. Special cases are needed
firstnames = []
if new_character.c_race == 'Human':
types = []
for type in data.races['Human']['first names']:
types.append(type)
type = types[random.randrange(0,len(types))]
for name in data.races[new_character.c_race]['first names'][type]:
firstnames.append(name)
elif new_character.c_race == "Half-Elf":
types = []
for type in data.races['Human']['first names']:
types.append(type)
type = types[random.randrange(0,len(types))]
for name in data.races['Human']['first names'][type]:
firstnames.append(name)
for name in data.races['Elf']['first names']:
firstnames.append(name)
else:
for name in data.races[new_character.c_race]['first names']:
firstnames.append(name)
Once we've gathered a list of names, we can use randrange to assign a random name to the character.
# assign a random name from the list of first names
new_character.c_firstname=firstnames[random.randrange(0,len(firstnames))]
Using this process of first retrieving the values from data.py and then choosing a random option from the appended list, it's relatively easy to give our character any number of traits and features.
for weapon in data.classes[new_character.c_class]['weapons']:
weapons.append(weapon)
new_character.c_weapon = weapons[random.randrange(0,len(weapons))]
Using the character's class information, we can then populate a list of weapons and armor. Each new character will have items chosen at random from the data provided in data.py.
After generating all the random features of our character, all we have left to do is print the information to the console. A series of print statements will do the trick:
print(“A {} {} named {}.”.format(new_character.c_race,
new_character.c_class, new_character.c_firstname))
print("Weapon: " + new_character.c_weapon)
print("Armor: " + new_character.c_armor)
Now we can write a loop that asks for user input. We'll give them two basic options: yes or no. Either the user will want to generate a new character or they will not, in which case the program terminates.
# This is the entry point for the program
print(“Welcome to the Random Character Generator.”)
new_input = “”
while new_input != “quit”:
new_input = input(“Create a new character?: “)
new_input = new_input.lower()
if new_input == “yes” or new_input == “y”:
print(“Ok, let's make a character!\n”)
print(“=======================================================”)
CreateNewCharacter()
elif new_input == “no” or new_input == “n” or new_input == “quit”:
new_input = “quit”
print(“Goodbye!”)
This concludes the tutorial. You can download the complete code here.
For such a simple exercise, we had to cover a lot of territory. I hope you learned something about working with Classes and dictionaries in Python.
Try adding new features to the random character generator, changing the data to fit your own needs. Experimenting is a fantastic way to hone your Python skills.