Sunday, February 12, 2012

Hangman Postmortem

*Note: In software development, particularly game development, a postmortem is a discussion of the games development after completion. Postmortems enumerate triumphs and pitfalls encountered during development. They're used to reflect on the completed work and make observations on how development in future projects can be improved. Given the morbid nature of Hangman, I thought the meaning of postmortem in this case should be clarified. It is not an attempt at dark humor. It is the vocabulary used by professionals in the game development field. 

Hangman, the first game of the thesis, is complete. You can play it on my student webspace. Hangman is a simple game, and it was fairly easy to develop. It served as a good warmup for developing the framework at large. Further, I learned several important lessons while developing the game.

I chose Hangman for the first game due to its simplicity. Developing a game without complicated control flow or convoluted rules allowed me to focus on refreshing my knowledge of the Javascript language, the DOM API, and application design (particularly design issues specific to Javascript). It also allowed me to familiarize myself with HTML5 syntax, the canvas API, and other new concepts. References I found useful throughout Hangman's development are listed near the end of this post. 

Design and Implementation

The design included three modules: graphics, words, and hangman. The hangman module contained the the primary game logic. The graphics module contained functions for drawing to the canvas. Both modules were similar in length. The words module simply contained an object with an array of words that can be used as guess words in the game. The words module served to make adding additional words easy, precluding the need to search for a words array to modify mixed in the game logic code. 

The graphics module defined a list of functioned used to draw to the canvas. The module helps separate concerns, keeping all the drawing code separate from the game logic. Each function performs a single drawing task, but it significantly improves the maintainability and readability of the game logic since drawing a shape or a text string requires several steps using the canvas API. If the logic needs to print a message to the canvas, it can simply call printMessage(msg) rather than obnoxiously calling several functions of the canvas API. 

The hangman module is the entry point to the game from the browser. It defines the game's control flow and rules. The control flow is simple. It begins by initializing the game state. This step involves choosing a random word from the word list, setting or resetting variables (such as the number of guesses and the list of unguessed letters) to their initial value, and starting the next phase by connecting an event handler to process keyboard events. 

The next step is the game loop--it continues to run until a terminating condition is met. Unlike a standard real-time video game, however, an iteration of the Hangman game loop only executes when an event occurs. The game doesn't update unless the user presses a key on the keyboard. This portion of the game flow is contained in an event handler registered on the document. When a key press occurs, the handler processes the event and checks whether a valid key was pressed. Then it checks the letter against what has already been guessed. If the letter is valid and guessable, the logic determines whether it is a good guess or bad. Finally, it updates the canvas to show the new game state. 

The final step occurs when a terminating condition is met in the game loop. The player can either win or lose to exit the loop. When all the letters in the mystery word are guessed, the loop exits and prints a win message. When the player exhausts his six guesses and the hangman is complete, the loop prints a lose message and exits. The game then rests in a stopped state until the user presses a key. When any key is pressed, the game loops back to the initialization step and play resumes with a new mystery word. 

Lessons Learned

1. Learning the canvas API.
The API is deep and complex, but using its basic functions is easy. By the end of development, I would like to be intimately familiar with the entirety of canvas, but a developer can accomplish a lot just knowing the basics. For Hangman, the rectangle, arc, and path drawing functions were the most useful. 

2. The canvas text API is weak.
Unfortunately, the canvas text API is not very powerful. It manages text much as CSS would--you set properties on the context that correspond to CSS font properties to modify the way the text is drawn. Some properties are conspicuously missing, however. The line-spacing property would have been handy, but there was no way to set it. Further, positioning the text accurately is impossible because there is not a way to poll the text's dimensions. The API will return the length in pixels of a string drawn to the canvas, but it will not return the height or other dimensions. 

For now, the text API will suffice. Creating my own text library would be an unanticipated and laborious task. However, to make the text manageable in the future, I will need to write some functions to make positioning reasonable, though not perfect. Further, using any non-monospaced font adds horrible complications to text positioning. Only monospaced fonts such as courier should be used in games.


3. Input Processing Is Non-trivial.
Processing user input is difficult. Hangman's input processing was easier because it only used the keyboard, it limited the valid keys to the alphabetic a-z, and the game flow was asynchronous. Even with these simplifications, the program had to determine the key pressed from a key code using a key map, filter invalid keys, and involve the convoluted DOM even handler functions.


The next iteration of development will require an input module. The module will be developed incrementally. The first step will be to simply encapsulate the key map functionality implemented in Hangman. The key map will need to be expanded to include non-alphabetic keys such as numbers and punctuation. Further, the module will need to accommodate the problems that arise when the game flow is not asynchronous. These problems and their solutions will be discussed in more depth in upcoming blog entries. 


4. Modules Are Necessary.
I was aware that modules would be necessary prior to beginning development. However, I was unsure of how to provide modules to a language without a built-in module system. I considered rolling my own solution, but that job would have been difficult. I decided to avoid reinventing the wheel by using RequireJS, an AMD-compliant module library. I was afraid it would add unnecessary baggage to the HTML doc that loads the script in the form of additional script tags, but RequireJS is easy to use and unobtrusive. 


5. i18n Is Non-Trivial.
I attempted to make Hangman ready for i18n. However, several roadblocks prevented this goal from reaching fruition. First, though I made it possible to easily add alternate-language word lists, I did not make an easy way to swap the lists. Further, I did not consider making the messages printed to the users easily replaceable. Even if the word list was swapped, the international user would be confused by the English instructions and feedback used in the game. In the next iteration, this problem will be fixed by a string table mechanism. 

References
HTML5 Canvas by Steve and Jeff Fulton, O'Reilly Press. (available on Safari)

No comments:

Post a Comment