CS 124, Spring 2021
Lab 10: Hangman GUI

For the last two weeks of the semester, you will work on a final programming project. One option is a hangman game, which is discussed on this web page. I expect that most people in the class will want to work on hangman, but it is also possible to design a different project. If you want to work on something other than hangman, you should let me know. It would be good to discuss your idea before committing to it.

You have the option of working with a partner on a final project. If you work with a partner, you should have a project that is at least somewhat more ambitious than would be required for one person. For example, for a hangman game, you could find something extra to do in the program, such as a menu of alternative word lists or a "cheat" button that would give the user a free letter.

The completed program is due by midnight on the last day of class, Friday, April 30. I hope that all projects will be completed by that time, but I will consider requests for extensions on a case-by-case basis, for people who can show that they have been working on the project and just have some things to finish up.

If you work on Hangman, you will turn in one file named Hangman.java, using the program submission web site. (For Lab 10, that site will also allow you to upload additional files, in case your program requires them.) If you work on a different project, you should make arrangements with me for turning in your work. If you are working on the project with a partner, you should turn in only one copy, and you should make sure that both names appear in the comment at the top of the program. Remember that any Java files that you submit should follow all style rules, including commenting.

In lab on April 13, I showed programs from two other old labs that might be the basis for a final projects: Sprites and Midi Keyboard. Those labs did not use JavaFX. If you are interested in using my sprites or keyboard in your project, let me know soon, since I will have to translate the code to JavaFX.

About Hangman

Hangman is a simple word-guessing game. A GUI version was demonstrated in class. The program selects a word for the user to guess. Initially, each letter in the word is shown as an empty space. The user guesses letters. When the user guesses a letter that appears in the word, that letter is filled in; it is filled in at every space where it occurs in the word, not just one space. When the user guesses a letter that is not in the word, a part of a "hanged man" is drawn. If the hanged man is completed before the user finds all the letters in the word, the user loses. The picture at the right shows an example. The hanged man here is complete. In my version of the game, I start with the scaffold already drawn, and the user gets 7 incorrect guesses. For the first incorrect guess, I draw the man's head; for the second, the neck; and then the left arm, right arm, body, left leg, and right leg. Here is my version of the game, translated into JavaScript so it can run on a web page. (The Cheat button is something that might be done for a two-person project. A basic version of the program might have a "Quit" button instead.)

About Layout, Etc.

You will have to design a user interface for Hangman (or whatever program you write). The game will require 26 buttons, one for each letter of the alphabet, as well as a Canvas where you can draw the hanged man. You will also need other components or a menu bar or both. You will need to "lay out" all these components in a main BorderPane. Some of the techniques that you need were covered in the GUI Tutorial, Lab 9. Note in particular that Lab 9 shows how to use a canvas for drawing and a component for holding some buttons, all in a BorderPane. We will also talk about layout in class, and you can read about it in Section 6.5. But here are a few things you might use in this program.

To hold the 26 alphabet buttons, I used a GridPane with two rows each containing 13 buttons. Here is how I added the n-th button to the grid:

button.setPrefWidth(650/13);
letterGrid.add(button,n%13,n/13);

The goal was to have a pane that is 650 pixels wide, to match the size of my canvas. The command button.setPrefWidth(650/13) sets the "preferred width" of the button to be 650/13 pixels. The GridPane will be sized to show all the buttons that it contains at their preferred size, so this line determines the size of the GridPane. In the next line, letterGrid.add(button,n%13,n/13), letterGrid is the GridPane. The parameters specify the component that is to be added to the grid and the column number and row number where it is to be placed in the grid. Here, n is between 0 and 25, so the column number, n%13, is between 0 and 12, and the row number, n/13, is either 0 or 1. You can see what it looks like in the picture of the game, below. You don't have to do exactly the same thing. If your program has a menubar, for example, you might put the buttons below the canvas, or on the side.

You will need to know more about buttons. You can read about them in Section 6.4. Note in particular that button.setDisable(true) will disable the button (so the user can't click it), and button.setDisable(false) will re-enable it. And button.setOnAction is used to specify an event handler for the event that is generated when the user clicks the button. You can write One event handler method that will work for all of the letter buttons. It will just need to know which button was clicked. You should be able to do something like this:

button.setOnAction( e -> doLetterButton(button) );

In the event handler, you will need to know what letter was clicked. You can get the text from the button by calling button.getText(), and the letter on the button would then be button.getText().charAt(0).

My program has three more buttons, below the canvas. For layout, I put the buttons in an HBox, with its alignment set to Pos.CENTER, and added the HBox to the bottom of the main BorderPane. You can read about HBox in Subsection 6.5.3.

For drawing on the canvas I recommend, as usual, having a draw() method that does all the drawing, using information about the current state of the game. When any change is made to the state of the game that affects what should be shown in the canvas, you just have to call draw() to completely redraw the canvas.

First Steps: The GUI

Your first task is to design and set up the GUI for your program and test that it is working correctly. You can start implementing the actual logic of the game after the GUI is mostly in place. You will want to use some ideas from Lab 9.

For your Hangman game, you are required to have 26 buttons representing the letters of the alphabet. The user will select a letter by clicking one of the buttons. Furthermore, you must disable the button when the user clicks it, so that they can't choose the same letter again. After the user finishes with one word, the user must be able to begin a new game with a new word. At that time, all the letter buttons will have to be enabled again—this means that you will need to keep references to the alphabet buttons in an array of Buttons, so that you can loop through the entire array when you need to enable or disable all the letter buttons.

You are required to draw the hanged man, step-by-step, as the user makes incorrect guesses. You are required to display information to the user about the progress of the game, such as a message saying that the user's letter does or does not occur in the word. You should show all the the guessed letters in the word, with some indication of the blank spaces that have not yet been guessed. And you are required to have extra buttons and/or menu items for starting a new game and for quitting the program.

You should begin by setting up the user interface, with all the panes, buttons, etc., that your program needs. You should write event-handler methods to respond to buttons and to menu commands if you use them. To test that the event handlers are properly installed, you might print a message in the event handler to show that it is functional. You might want to implement the disabling of the alphabet buttons when they are clicked, and you might want to start writing a newGame method that will, among other things, re-enable all the alphabet buttons and select a new word. You will want to make sure that you can draw the hanged man for all stages of the game. Remember that what you draw at any given time will depend on the number of incorrect guesses, which should be stored in an instance variable; your draw() method should test the value of that variable to decide what part of the hanged man to draw.

You can also implement some way to display the word to the user, most likely by drawing the characters of the word in the draw() method, as my program does. But another option might be to to show the word in a Label. Similarly, you want to be able to show messages about the progress of the game to the user. One way to do that is to have an instance variable of type String to hold a message, and draw that string on the canvas in the draw() method. To change the message, the program can set the value of that instance variable and call draw(). The draw() method could also display additional messages based on the state of the game, such as the number of wrong guesses remaining.

Game Logic

As you make progress on your GUI, you can start adding in the implementation of the game logic. That is, you should make it possible for the user to play the game. Almost all of the game action, except for some setup in the start() method, takes place in the event handler methods for the buttons (or menu items).

You will need to store some global state data for the game. In addition to the buttons, this includes at least the word that the user is trying to guess, the string of letters that the user has guessed so far, the number of incorrect guesses that the user has made, and a boolean variable to record whether or not the game is over. Remember that global data are stored in instance variables. The values of the variables change in response to events, and they are used by draw() to decide what to draw. (So, be sure to call draw() when you change any instance variable that is used by that method.) You should carefully analyze what has to happen in your event handlers and what the draw() method needs to display at any given time both during a game and when a game is over.

You will also need a source of words for the game. At some point, you can add these two files to your project: the Java file WordList.java that represents a list of words read from a resource file, and the resource file, named wordlist.txt which contains a list of words. WordList.java should be in the default package, and wordlist.txt should be in the src folder, but not in any subfolder.

To use the WordList class, you need to create a variable of type WordList. The constructor of the object specifies the path to the resource file. That is, you should create an object of type WordList with

wordlist = new WordList("wordlist.txt");

It makes sense to have a single WordList object for your program, stored in an instance variable, and to use the above command in the start() method to create that object.

Then, in your program, when you want to get a randomly selected word from the list, you can call wordlist.removeRandomWord(), which returns a randomly selected word from the list. (Note that all the words in my wordlist file are lowercase, and you probably want to convert the word to uppercase.) See the comments in WordList.java for more information. The file wordlist.txt contains 425 words. All of the words are between 5 and 11 characters in length. They are a random selection from a much longer list. You might want to provide your own lists of words. You might even provide several alternative lists of words for the user to choose from.

Polish

The program that you turn in for this project should be well-documented, and it should carefully follow all the other rules of good programming style. The code should be well-organized and efficient. Define subroutines where appropriate to avoid having long segments of code in one method.

A working but minimalistic game will not receive full credit. The game should look nice, and it should be fun to play. It should keep the user informed about exactly what is going on.

Think about adding some extra features, especially if you are working with a partner. I've mentioned providing the user with a choice of several different word lists to choose from. You might implement levels of difficulty by varying the number of incorrect guesses that are allowed. You could think about adding Midi sound effects (using ideas from Lab 8). Maybe even some animation (Section 6.3.5).