Consequences (The Game)

(Originally published on my blog on January 21, 2021)

I had an interesting idea today when I received something I'd ordered, which happened to be a birthday present for my niece. The present is a Mad Libs book, and the idea was: that would be easy to write code for - and I bet my niece and her brothers would enjoy messing with it, too. (Plus it saves the books!)

A little research later (to see how careful I needed to be with naming the script...) and I discovered that the game Mad Libs is actually significantly based on an older game called Consequences, and in fact the same code could work for either style of game, based on content templates. So here I'm going to organize my ideas for the code, and then I'll get to programming!

Technology Decisions

For all the enjoyment I've been getting out of C++ lately, and all that writing in Python now makes me feel strangely insecure ;), this project is relatively uncomplicated, and at a high level of abstraction, and simply doesn't need the level of control that C++ gives. Plus, I already know where to find most of the tools I need to make it work in Python.

Initially I thought maybe I'd just write a command line script, and I might still make it able to run as a command line script if needed, but it's a better exercise if I stretch myself a bit and craft it with a GUI, and easier for a user to get into, too. For the GUI, mostly for licensing practicalities, I'll use PySide2. (For more information about the licensing and other differences, Machine Koder wrote an article covering the main points.)

Content files will be written with JSON, since it's a nice, easy, human readable (and human editable) format that should be simple to work with, unless I get into the thick of writing code and have trouble getting it to do what I want.

Finally, I'll set the program up to run as a package in a self-contained venv to avoid dependency issues and keep users' system installs clean. Plus it's good practice (and a good practice).

Content Files

At this point in ideation I can see basically 3 fields of information that a content file would need to have to work. First, a title, although that's not critical. Second, the base text that user entered words will be applied into, with properly named "blanks" to be filled in. Third, a dictionary of the values to fill, each corresponding to a named blank:

# 1:
title = "Breakfast"
# 2:
text = "{name1} {verb1} to a/an {adjective1} {noun1} of {noun_plural1}."
# 3:
values = {name1: "", verb1: "", adjective1: "", noun1: "", noun_plural1: ""}

Possibly not in that order (it might make more sense to a content producer to have the dictionary above the text, for example), although order shouldn't actually matter if I call each field by name.

NOTE:

I was thinking about using .gitignore to hide all "content" files, since initially I'll be populating it with content from the book I purchased for her, but I think it makes more sense to have a separation between public and private content - public content can be shared with anybody, and makes it easier to get into and start messing with the game, whereas private content can be things that shouldn't be shared publicly, because of copyright or whatever other concerns.

As a side note, I want to also save user generated output files, probably just as a text file based on the content filename + a timestamp. Alternatively, as a JSON including the original title and a "By: " field input by the user. If I go this route, I could also save the dictionary of values used, to allow editing or fine tuning a particular output for maximum entertainment. ;)

Ideally, there'd be a way for a user to create a content file through the GUI, but that's a more complicated proposition that I don't intend to include immediately. Writing a JSON file with the necessary fields isn't too difficult.

Game Loop

When the program starts, the user should be presented with a "Welcome" string, and the list of available content by title (possibly organized by subdirectory). Clicking on a title, then a select button will bring up a dialog in which the user enters each requested value, then a button at the bottom to "Generate" the text. Selecting Generate will produce a text file saved to an output directory, and display the contents on the screen, at which point the game will offer the user to "Select new" or "Exit" - or possibly to "View saved" as well. Select new should return the user to the initial screen, while view saved would be an additional dialog to view the list of generated files and select one to view.

Possible problems

What happens if the user doesn't fill in all the blanks? One could go two ways here: one, by not allowing the user to proceed without filling blanks (which could be annoying), or two, the option I prefer: if a value isn't filled in, just supply the name of the value (in the example above, if someone declined to fill in adjective1, then the text would just output with adjective1.) This would work especially well with the idea of saving the values, and actually could allow content producers to be more specific in their directions: noun1 could be pre-supplied with "noun related to food", or something like that, and if the user chooses not to fill it in, that would be the value used.

Another issue that's less simple to fix is badly formed content. Too many possible values, such that not all of them get used, is not a big deal programmatically, although disappointing to the user when their favorite verb ever doesn't make it into the final text. But what if they format the content incorrectly, such that either the JSON can't be read at all, or the base text doesn't have the input values correctly indicated, or asks for an input value that isn't listed in the dictionary? Some kind of error handling and display is vital to this situation, preferably with helpful hints about what went wrong.

Conclusion

Writing all this out has given me a pretty solid place to start in developing the software of this game. I anticipate the hardest part will be getting PySide2 working as intended, since I haven't done very much GUI work at this point, possibly followed in difficulty by correctly packaging the program to work as intended, although I've made that work a few times.

After writing this post, I immediately dove into implementing the project. You can see my results in this Consequences github repository.

20