Mad Libs is a word game that starts from a quote or a short story where some keywords have been replaced with blanks. A lexical or other category is specified underneath each blank, such as "noun", "verb", "place", or "part of the body". One player then asks the other players, in turn, to suggest a word for the category specified under the next blank, but without revealing the context for that word. Finally, the filled quote or the completed story is read aloud. The result is usually comic, surreal and somewhat nonsensical.
Say we start for example from the following partial quote.
______ created _______ so that __________ would learn ____________.
Name thing CITIZENS discipline
The missing words could be completed as follows
God created war so that AMERICANS would learn geography.
or equally well in the following way
Mercator created maps so that BELGIANS would learn navigation.
The game was invented by Leonard Stern en Roger Price, and more than 110 million copies of the Mad Libs books have been sold in the US alone since the series was first published in 1958.
Write a class MadLibs whose objects can be used to complete a given quote or story in which some keywords have been replaced with blanks, by randomly picking words from the indicated categories. Each object of this class must have a vocabulary property that is a reference to a dictionary (dict). This dictionary represents the vocabulary the object has learned so far. Initially the dictionary is empty, but new information can be added throughout the object's lifetime, or the information in the dictionary can be consulted by calling the following methods that must be supported by the class MadLibs:
A method learn that can be used to add one or more words from a certain category to the object's vocabulary. The method takes two arguments: i) the category of the new words (str) and ii) one or more words from this category. In case a single word is passed as the second argument, the word may be passed as a string (str). In case multiple words are passed as the second argument, the words must be passed as a collection (list, tuple or set). The method must make sure that the dictionary referenced by the vocabulary property is extended by adding the words to the set onto which the given category is mapped by the dictionary. In case the dictionary did not have a key corresponding to the given category, a new key-value pair must be added to it, with the key corresponding to the given category and the value being a set containing the given words. All categories and words included in the dictionary must always be converted into lowercase.
A method suggest that can be used to suggest a word from a given category. The category (str) must be passed as an argument to the method. The method must randomly pick one of the words from the set onto which the given category is mapped by the vocabulary property. In case the given category does not occur as a key in the vocabulary property, the method must raise an AssertionError with the message unknown category. If all letters contained in the given category are capitals, all letters in the selected word must also be converted into uppercase. If the first letter contained in the given category is an uppercase letter and all successive letters are lowercase letters, the first letter of the selected word must also be converted into uppercase. Finally, the method must return the (adjusted version of the) selected word (str).
A method fill that takes a string (str) that may contain fragments enclosed in between two underscores (_). These fragments specify the name of a category, and represent keywords from that category that have been replaced with blanks in the given string. The method must replace each of these fragments (including the leading and trailing underscores) with a randomly selected word from the specified category. This must be done by calling the method suggest, which also applies the rules for adjusting the selected word as implemented by this method. The string in which all blanked keywords have been replaced by randomly chosen words from the specified category, must be returned by the method. In case the given string contains a fragment that does not correspond to a category in the vocabulary property, the method must raise an AssertionError with the message unknown category.
>>> madlib = MadLibs()
>>> madlib.vocabulary
{}
>>> madlib.learn('name', 'God')
>>> madlib.learn('thing', 'war')
>>> madlib.learn('citizens', 'Americans')
>>> madlib.learn('discipline', 'geography')
>>> madlib.vocabulary
{'discipline': {'geography'}, 'thing': {'war'}, 'citizens': {'americans'}, 'name': {'god'}}
>>> madlib.suggest('name')
'god'
>>> madlib.suggest('NAME')
'GOD'
>>> madlib.suggest('Name')
'God'
>>> madlib.fill('_Name_ created _thing_ so that _CITIZENS_ would learn _discipline_.')
'God created war so that AMERICANS would learn geography.'
>>> madlib.learn('name', ('Mercator', 'Caesar'))
>>> madlib.learn('thing', ['maps', 'coordinates'])
>>> madlib.learn('citizens', {'Belgians', 'Martians', 'Germans'})
>>> madlib.learn('discipline', 'navigation')
>>> madlib.learn('discipline', 'colonisation')
>>> madlib.vocabulary
{'discipline': {'colonisation', 'navigation', 'geography'}, 'thing': {'maps', 'war', 'coordinates'}, 'citizens': {'belgians', 'americans', 'germans', 'martians'}, 'name': {'god', 'caesar', 'mercator'}}
>>> madlib.fill('_Name_ created _thing_ so that _CITIZENS_ would learn _discipline_.')
'Mercator created maps so that BELGIANS would learn geography.'
>>> madlib.fill('_Name_ created _thing_ so that _CITIZENS_ would learn _discipline_.')
'Mercator created war so that BELGIANS would learn navigation.'
>>> madlib.suggest('country')
Traceback (most recent call last):
AssertionError: unknown category
>>> madlib.suggest('_CITIZENS_ live in _Country_.')
Traceback (most recent call last):
AssertionError: unknown category