12.9. Project: Mad Dictionaries

Let’s use dictionaries to play Mad-Libs!

Note

If you are not familiar with Mad-Libs, you can learn about them here, and then play a few short samples with your friends.

If your teacher added you to a repl.it classroom or a Trinket course, login to your account to access the starter code and complete the project.

If you are NOT enrolled in a repl.it classroom or Trinket course, you can find copies of the starter code here:

  1. Trinket code
  2. repl.it code

The code runs, but it does not really do anything. Your job is to build three functions that ask the player questions and complete a Mad-Lib.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def create_madlib_dict(mlib_string):
   words = mlib_string.split() # Split string into a list of words.

   return {} # Return the new dictionary instead of {}.

def prompt_user_for_words(mad_lib_dict):
   answers_dict = mad_lib_dict.copy() # Make an independent copy of the dictionary.

   return answers_dict

def create_output(ml_dict, text):
   new_text = text  # Assign the starting value to new_text.

   return new_text

def main():
   mad_lib = ''

   mlib_dict = create_madlib_dict(mad_lib)
   user_responses = prompt_user_for_words(mlib_dict)
   output = create_output(user_responses, mad_lib)

main()

12.9.1. Part 1: Mad-Lib Text

First, you need to create a string that looks something like a Mad-Lib statement.

A Mad-Lib sentence with two blanks labeled "plural noun" and "adjective".

Mad-Lib sentence with labeled blanks.

Instead of blanks, you will use symbols to indicate the words a player needs to provide. For example, if they need to give a verb for the sentence, "The turtle ____ home," your string might look like, "The turtle _verb1/ home."

Note the _ and / symbols on each side of the label verb1. We will use this notation to indicate where the player needs to fill in a blank.

  1. In the starter code, find the main() function. Assign the mad_lib variable a short Mad-Lib type sentence.

  2. Your mad_lib text should contain two or three blanks where the player will need to suggest words. For example:

    "My _noun1/ colors are _color1/ and _color2/."

    Here, the player would be asked to give one noun and two colors to complete the sentence.

  3. For now, keep the text simple. You can use more complicated Mad-Lib strings later.

Note

You do not have to use _ and / around a label, but you should use a different symbol on each side. Just choose symbols that are NOT usually used in normal text.

Examples:

  1. {noun1}
  2. *noun1_
  3. ^noun1|

12.9.2. Part 2: Build a Dictionary

Once you have your mad_lib string ready, the next statement in main() calls the create_madlib_dict function. The mad_lib string gets passed in as the argument.

The create_madlib_dict function takes the labels from the string and turns them into keys for a new dictionary. The function then returns that dictionary.

Code the create_madlib_dict function:

  1. Line 1 defines the function and takes a string as a parameter. Line 2 splits the mlib_string into a list of words.

  2. On line 3, print words to see what this list looks like (run the program). Note that the _ and / symbols remain with the words we want the player to suggest.

    ['My', '_noun1/', 'colors', 'are', '_color1/', 'and', '_color2/.']
    
  3. Replace the print statement with new_dict = {}, to create an empty dictionary.

  4. Now add a for loop. It should:

    1. Iterate through the words list.

    2. Check each word to see if it contains the underscore _ symbol.

    3. If True, take a slice from the word and assign it to a variable. The slice should NOT include the symbols or any punctuation.

      key = word[1:word.find('/')] # Returns the characters between _ and /
      
    4. If True, add a new key/value pair to new_dict. Use key for the key, and use the empty string as the value.

    5. No else statement is required for the if.

  5. Print new_dict after the loop to check your progress. Properly done, it should look something like:

    {'noun1': '', 'color1': '', 'color2': ''}
    
  6. Remove the print statement and return new_dict from the function. This gets assigned to the mlib_dict variable in main().

12.9.3. Part 3: Query the User

OK, now you’re ready to ask the player for the fill-in-the-blank words.

The next statement in main() calls the prompt_user_for_words function and sends mlib_dict as the argument. We want the function to prompt the player for each of the words needed in the Mad-Lib.

The function should work something like this:

Gif showing three prompts asking the player for a noun and two colors.

Prompt the user to fill in the Mad-Lib blanks.

The first line in the prompt_user_for_words function creates a copy of the dictionary you built in part 2. You need to add a loop to replace the values in the collection with player-supplied words.

  1. Set up a for loop to iterate through the keys in answers_dict.

  2. Each time the loop repeats, prompt the player to supply one of the missing words. Use the key name as part of the prompt. Note that any numbers in the key name should NOT be displayed in the prompt, so noun1 shows up as just noun.

  3. When the player enters a word, update the dictionary to link the current key to that word. The word should be all lowercase.

  4. Print answers_dict after the loop to check your progress. Properly done, the output should look something like:

    {'noun1': 'school', 'color1': 'salmon', 'color2': 'brown'}
    

    'school', 'salmon', and 'brown' were the words entered in the console.

  5. Remove the print statement and return answers_dict from the function.

Back in the main() function, the returned dictionary gets assigned to the user_responses variable.

12.9.4. Part 4: Print the Result

Almost done! Now you just need to complete the Mad-Lib and display it in the console.

The next statement in main() calls the create_output function and sends it the user_responses dictionary and the original mad_lib string. These get assigned to the ml_dict and text parameters in the function.

We want create_output to build and return the final message. The function will NOT print the message.

Note

Yep. It’s time to use the accumulator pattern again!

  1. Instead of the empty string, the accumulator variable new_text is assigned the original text. This is important, and you will see why soon. For now, just roll with it.

  2. Code a for statement to loop through ml_dict.items(). If you need to review this idea, look back at the Loop by Key/Value Pairs section.

  3. Inside the loop, paste the following statements:

    label = '_' + key + '/'
    new_text = new_text.replace(label, value)
    

    key and value are the names of the two loop variables.

  4. Next, return new_text, which gets assigned to the output variable in main().

  5. Now check to see if your function produces the expected Mad-Lib. In main(), add a print statement to display the value of output on the screen.

    Choose a(n) noun:  School
    Choose a(n) color:  salmon
    Choose a(n) color:  BROWN
    My school colors are salmon and brown.
    

Tip

Why new_text = text instead of new_text = ''? This has to do with the replace statement in the loop.

  1. Try running the program with new_text = ''. What do you notice about the output?
  2. Try running the program with new_text = text.replace(label, value) inside the loop. What do you notice about the output?

The replace method creates a new string that swaps one of the labels (like _noun1/) with a different word. However, if new_text = '', then the replace method finds nothing to trade. new_text never becomes anything other than the empty string.

new_text = text.replace(label, value) always uses the unchanged text string as a starting point, so only the last trade gets saved.

To preserve all of the replacements, new_text needs to start with the same labels as text, then get reassigned after each change is made.

12.9.5. Part 5: Try Bigger Mad-Libs

Now that your program runs, it’s time to play!

  1. Try giving your program a Mad-Lib with more than 2 or 3 blanks to fill.
  2. What happens if you use a label with more than one word, like _plural noun1/? Is this a code issue, or a label syntax issue?
  3. Ask your teacher to play your Mad-Lib game!

12.9.6. Part 6: Bonus Mission

What if a Mad-Lib requires a capitalized word?

"The _adjective1/ bus dropped us off in the middle of _city1/."

City names should be capitalized!

Refactor your code to .capitalize() the player’s word whenever the key name begins with a capital letter.

"The _adjective1/ bus dropped us off in the middle of _City1/."

Choose a(n) adjective:  golden
Choose a(n) City:  phoenix
The golden bus dropped us off in the middle of Phoenix.