Python Madlibs

Long time no blog! Things have been incredibly busy, especially as I’m also working on the #include hack day for 11-13 year old girls which will be held in June 2013!

Anyhow, I thought I’d put up a little game I created as a revision task for my upper 6th who have their January modules (*sniff*) coming up. It’s based around the game of ‘Madlibs’ which we don’t really have in the UK, but Wikipedia says it is “a phrasal template word game where one player prompts another for a list of words to substitute for blanks in a story, usually with funny results”. It’s a little bit like the game of consequences, but not quite the same.

How does the game of Madlibs work?

We start with a story, containing several blanked out words. Here’s a really basic one:

_________ went to __________ and bought a ___________.

We then prompt the user to enter these words, in this case a name, a place and an object. We might end up with something like this:

Lady Gaga went to London and bought a pencilcase

Obviously, the more entertaining the original story, the better the laughs will be at the end!

How do I program it?

Set up the text file

As with all programs, there are many ways of solving this. I wanted to provide a revision task for reading and writing to files as well as string manipulation, so I did it this way.

Firstly, I started with a text file – let’s call it madlibs.txt. Inside my text file I made up some stories, each one on a different line, for example:

object,person# A %1 flew across the road and knocked over %2.
place,person# One day in %1 I saw %2 with a cat on their head.

You can add as many stories as you like to the file as long as they are each on a separate line.

Now let’s inspect the format of the madlibs. The first part before the # symbol is the list of items I need to ask the user to input – I’ll call this the input list. They are separated by commas.

object,person

Then there is a # symbol which divides up the objects part from the story part. This could be any symbol that is unlikely to be in the story. Then we have the story which contains markers like %1

A %1 flew across the road

These tell me where I am going to replace the words in the story. %1 will be replaced by the first word in the input list, an object. %2 will be replaced by the second word in the input list etc. Note that if I want to reuse the word I can simply use it’s appropriate number again, for instance I could add another sentence referring to the first object again:

A %1 flew across the road and knocked over %2. The %1 was destroyed.

(Note: My students were a bit confused because they thought the %1 and %2 were the same as %d, %s etc. from the format operator – we’re not using that though.)

Write the code

Here’s my full code with comments:

# Program to read a random madlib from a file and generate it back
import random

# Open the madlibs text file
f = open('madlibs.txt','r')

# Read the lines from the file into a list
allLines = f.readlines()

# Choose a random madlib from the list
madlib = random.choice(allLines)

# Separate the input list from the story
halves = madlib.split("#")

# The second item is the madlib text 
madlibText = halves[1]

# Now get the inputs required one by one
inputs = halves[0].split(",")

inputStore = [""]

for item in inputs:
    inputStore.append(raw_input("Enter a "+item+": "))

# Print out the madlib but replace the markers with the inputs

for i in range(len(inputStore)):
    madlibText = madlibText.replace("%"+str(i), inputStore[i])

# Get rid of whitespace (this annoys me)
madlibText = madlibText.strip()
print madlibText

Um…some of this looks a bit tricky?

It’s not too bad! We’ve used a few functions you may not have seen which I’ll explain briefly:

Random choice

random.choice(allLines) – this takes a random choice from a list (in this case, a random madlib from all of the ones in the file)

Split (sometimes called explode in other languages)

halves = madlib.split(“#”) – this splits the line into two halves on the # character and returns a list containing the two halves (don’t forget the index for the first half is 0). We do the same thing to split up the inputs, but the character to split on in this case is the comma.

Reading and appending inputs to the list
for item in inputs:
    inputStore.append(raw_input("Enter a "+item+": "))

This part loops through the input list asking the user to enter a <whatever the item was> and then append-ing whatever they said onto a list to store the answers, called inputStore. Notice that I have initialised inputStore to have one (blank) item in it, so the first item I append will be index 1. This is because our first marker to replace in the story text is %1 and not %0.

Replacing the markers with the inputs
for i in range(len(inputStore)):
    madlibText = madlibText.replace("%"+str(i), inputStore[i])

I am looping around however many times there are items in the inputStore and wherever I see %i (where i is the number of the iteration we are on) I replace it with the inputStore[i] – i.e. the corresponding item from the inputStore.

Other possibilities

You may not wish to bother with the file i/o stuff, in which case simply don’t bother with the open file/read lines from file parts and create a list of madlibs instead

# Set up Madlibs from array
allLines = ["person,place,time#I went to the %2 with %1 at %3",
            "object,person#A %1 flew into %2's face."
           ]

You could also suggest extension tasks such as:

  • Add a loop so that you can keep telling Madlibs without having to restart the program each time
  • Allow the user to add their own story – there will need to be some kind of program menu (do you want to tell a madlib or add a new one) and the program should appropriately format the user’s madlib
  • Use hidden input so friends can’t see what you typed – you could try the getpass library which will work although it will throw warnings if used with IDLE

(Madlibs image from http://technorati.com/lifestyle/article/free-mad-libs-for-family-game/)

4 thoughts on “Python Madlibs

  1. A good way to avoid losing parts of the message if it also contains # is by giving split the second argument: halves = madlib.split(“#”, 1).

Leave a comment