CS 161 Homework 7 - Hangman
Due Fri Nov 15 at 11:59pm
Overview
For this assignment, you will be making a simple version of the word game Hangman, This game will be played in the terminal window. In this game, the computer picks a random word from a pre-supplied list. The player then guesses a letter that they think may appear in the word. The computer checks whether the letter appears in the word and either reveals all instances of it (if it exists) or takes away one of the player's lives. The computer then displays the progress that the player has made towards guessing the word. The game continues until either all of the letters have been guessed (and the player wins) or all of the player's lives are used up (and the player loses). In either case, the computer should announce this event and the game should end.
An example exchange is below. Read this carefully--your program should be able to produce very similar output! (This is one of the ways I will test your code).
Welcome to Hangman! The word is: _ _ _ _ _ Guess a letter: a The word is: _ _ a _ _ Guess a letter: e Computer: I'm sorry, the word has no 'e'. You have 4 lives left. The word is: _ _ a _ _ Guess a letter: s The word is: _ _ a s s Guess a letter: e Computer: You already guessed 'e'! You have 4 lives left. Guess a letter: l The word is: _ l a s s Guess a letter: c Congratulations, the word is 'class'! You figured out the word with 4 lives remaining. Would you like to play again? (Y/N)
If the player loses, they should see (as an example):
No more lives remaining. The word was 'class'. Would you like to play again? (Y/N)
If the user enters "Y" when asked to play again, the variables in the game (the word to guess, the guessed letters, the number of lives) should reset and the game should continue. Otherwise, the program ends.
Note that the player is told the size of the word before they start guessing. The player is also able to guess a previously guessed letter without penalty.
This assignment should be completed individually. You are welcome to ask for help (either from me or from your classmates), but remember the Gilligan's Island rule!
Objectives
- To practice working with
array
s andArrayList
s - To practice using loops and conditionals do to simple searching
-
To practice designing and implementing programs
- Determing the variables and methods needed
- Using pseudocode to plan out complex algorithms
- To practice breaking up code into appropriate helper methods to support testing and readability
Necessary Files
You will need a copy of the Hwk7.zip file. The BlueJ project in this file contains two classes:
-
WordFile
is a class that represents a file full of words. You instantiate a newWordFile
object in order to load the file, and then you can call thegetWordArrayList()
method to get aArrayList<String>
that contains all the words in the file. You can see futher details about this class by looking at it's Java Documentation (select Documentation in the upper-right corner of the class window in BlueJ). You do not need to modify this class. If there are any questions, let me know! -
HangmanGame
this is the class that will contain your entire Hangman Game. Your task is to complete the implementation of this class. I have included the public interface of the class---that is, what public methods need to exist so that other classes can use it. Specifically, the class has two constructors and aplayGame()
method. Be sure and look at the Javadoc comments for details about the functionality of these methods! -
HangmanGameTester
A simple tester class for theHangmanGame
, demonstrating how to use the methods of the public interface.
The project also contains two word lists: words1500.txt
(the default) and words127000.txt
(a bigger list). Note that these lists do not contain exactly that number of words---you'll need to get the size
of the word list that you're using; do not "hard-code" the value!
And last but certainly not least, the zip file also contains the README.txt that you will need to fill out and turn in along with your assignment.
Remember to extract the files from the zip folder before proceeding!
Implementation Notes
This assignment will be different from the last few assignments in that your task is less clearly defined. Below are a list of details and requirements for the program--however, it is up to you to determine what methods you will need to make and how those methods will interact.
Be sure and read through all of the assignment carefully. Think about what variables you will need, what your functions should do, etc.
- I highly recommend first writing out method signatures and pseudocode to lay out your program and see how it will fit together, and then start filling code.
- When writing a method, be sure and write out a method comment that explains what the method should do, and then make sure that the method you are writing matches the comment.
- Finally, write one method at a time and test the method thoroughly--once you are sure that method works, you can move on to the next.
This will be more difficult than previous assignments, but I know you are all up to the task! There are lots of hints in the program description to help you on your way. And of course if you get stuck or need or just want to talk things over, please let me know and I'm happy to help :)
Assignment Details
As with any class you create, you should start by thinking about and declaring the instance variables. What attributes will your program have?
-
For example, your HangmanGame will likely need to keep track of the word-to-guess (e.g., "class" in the above example), and the word-so-far (e.g., "cl_ss"---the underscores represent letters that have not been guessed yet). You'll need to keep track of a growing list (that's a hint) of letters that have already been guessed, so that you don't deduct points for reguessing. You'll also need to keep track of the player's lives in some way. Finally, you'll want to keep track of the list of possible words to pick from, so that you can pick a new word every game!
- Remember that you can always add more instance variables later, but we want to try and keep variables as local as possible! So if you can have a method return a value rather than save it as an instance variable, that is much better style!
-
Helpful hint:
Normally you'd use a String to keep track of a word. However, for this program we're interested in the individual characters of a word. You could use methods like
word.substring(0,1)
(to fetch the first letter of the String for example). However, because Strings are immutable, it is awkward to change an individual letter in a String, such as converting a '_' to an 'a'.-
Instead, I recommend you represent both the word-to-guess and the word-so-far as arrays of chars,
char[]
. An array will let you access and change each letter using an index (so you can get and change the 3rd letter). You can easily convert a String into achar[]
by using thetoCharArray()
method of the String object.- Note that in this case, you should initially fill your word-so-far
char[]
with underscores ('_') to start.
- Note that in this case, you should initially fill your word-so-far
-
Instead, I recommend you represent both the word-to-guess and the word-so-far as arrays of chars,
After you've determined your instance variables, you will want to initialize them in your constructors. The constructor signatures have been provided for you, but you will need to fill these in!
-
Remember: the goal of constructors is to set up your instance variables. This may involve calling other methods or using other control structures (conditionals, loops, etc) to create the values for the instance variables
- Since you'll need to "reset" some instance variables when a new game is started, you might consider making a resetGame helper method that assigns "new game" values to the instance variables. You can then just call this method from within your constructor, so that you don't have to write the same code twice!
-
The first constructor takes no parameters, and is your "normal" constructor. Note that this constructor will need to fetch the list of possible words to guess from a new
WordFile
object. -
The second constructor takes a
String[]
as a parameter that will provide the list of words for all games played. This constructor should be used for testing and grading--a smaller word list will make it easy to see if your program is working. In fact, I recommend testing your program with a provided word list of 1 or 2 words! (See the provided HangmanGameTester for an example).
Now that you've set up your HangmanGame
object, you'll want to start adding methods to represent each piece of functionality your program will need. Below is a list of "internal" functionality that your program will likely need to have, along with further hints and details:
-
Because your program supports playing multiple games, you'll want to have some way to easily "reset" the game to a new game state. This would involve resetting the word-to-guess (picking a random word from the list!) and the word-so-far, the number of lives, and emptying out the list of letters guessed.
- Remember that you can pick a a random word from an
array
or anArrayList
by selecting a random index using a random number generator--the same way you selected Colors for the BouncingBall lab.
- Remember that you can pick a a random word from an
-
You'll be printing out the word-so-far quite a lot. But since the word-so-far should be stored as a
char[]
, you can't simply print it out. Instead, you'll need to have some way to convert it to a String, so that you can print out the String!- You can do this by looping through the entries in your array and adding each letter to the end of a String you are building (an 'output' String), just like we did with the Dialect assignment.
- Note that you should put a space between each letter, that way your word-so-far will look like "c l _ _ _" rather than "cl___" (the second of which is hard to read!)
- You may want to convert different
char[]
into Strings, and you may want to use those Strings for things other than printing. Creating a method such that it takes in achar[]
as a parameter and then returns the generated String version will let you easily reuse code you've already written--you can just call your method!
-
Your program will need to be able to determine if a particular letter (e.g., the letter that was guessed) is inside the word-to-guess. Again, you should implement this functionality in such a way that you can re-use it if needed, such as by making a helper method that returns whether or not the word-to-guess contains a given letter.
- This will involve a simple linear search: loop through each character in the word-to-guess, and see if that character is the same as the given letter. If so, then the word-to-guess contains the letter. If you get through the whole word without finding anything, then the word-to-guess does not contain the letter.
-
Your program will also need to be able to replace a letter in the word-so-far (for when the user correctly finds a letter).
- You will need to go through the word-so-far and replace each '_' with the guessed letter only if the letter in the word-to-guess at that index of the '_' is the one that you're trying to replace.
- Note that you can do this with a single loop, but you'll need to consider both the word-so-far (to see if there is an '_' to replace) and the word-to-guess (to see if the '_' is one that should be replaced).
- Your program should also be able to count the letters left to guess--that is, count how many '_' are in the word-so-far. If this number is 0, then that means the player has finished guessing the word!
Once you have this internal functionality implemented as "helper" methods, you can use those helpers to write the method(s) that will let a user play your game!
-
This functionality will be run from the program's
playGame()
method--that is, when someone callsplayGame()
, the game starts. In this way, you can think ofplayGame()
as being like your program'smain
method!- User interaction (printing, using the Scanner, etc) will go here, rather than in a tester's main!
- This method will work somewhat similarly to the "main" method of your GuessingGame program; I recommend you review that as a place to start.
-
Your game should play like this (note this is not pseudocode, just instructions broken up into steps for readability):
- You will need a loop that shows the user the word-so-far, and then prompts them for a guess.
- Read in their guess using the Scanner (I recommend using
nextLine()
; you can convert their response into a character by using thecharAt()
method to get the first character). - First you'll want to check if they already guessed the letter--if so let them know (and if not, add that word to the list of guessed letters).
- Then you should check if the guessed letter is in the word-to-guess. If so, then replace that letter in the word-so-far; if not, tell the user they got the letter wrong and lose a life.
- You'll need to check whether they are out of lives (and if so, tell them they lost), or if they have guessed all the letters (and if so, tell them they won!)
- If the game is over, ask them to play again--if they do want to play, then reset the board (and go back to asking questions). Otherwise, your playGame() method should be finished.
-
This is a complex algorithm! There are lots of different conditions as well as a loop to repeatedly ask for letters. You should write out the code structure in pseudocode first. Once you have that, you can start replacing each piece of pseudocode with the appropriate Java code.
- You will likely be calling helper methods you've written earlier in the assignment; this should help keep your clean and readable. Again, think carefully about what each method does and what it returns, and make sure you do something with a returned variables!
- Note that a single call to the
playGame()
method can play multiple games--use a loop to keep asking the user for guesses if they decide to play again (after resetting the game). Do not call theplayGame()
method again from inside itself! You do not callplayGame()
, you simply run it! -
Although not required, you might decide to "factor out" some helper methods from your
playGame()
code. For example, maybe you pull out the code that asks a player for a guess (into aaskForGuess()
method which returns the character they guessed, for example), or the code asking the player if they want to play again (into aaskToPlayAgain()
method which returns whether they want to keep going, for example).
Testing
Be sure and text your code thoroughly. Use the "testing" constructor with a single word in your list will let you not need to guess at the computer's choosen word. Use copious System.out.println() calls to print out what is going on in your program (but be sure to comment them out when you are done!)
- Again, your program should be able to produce similar output to the example at the top of the page! That is, I should be able to type in those letters and get similar output. (I say similar because you are allowed to change the text of the messages somewhat, though please keep your program polite).
Documentation
Include lots of inline comments that explain your code, both to help me and to help you keep your place. Documentation is worth quite a bit in this assignment.
In addition, every one of your methods (including all of your helper methods) should have a full Javadoc comment (complete with @param and @return tags). Hint: Fill in these comments along with the skeleton (the method signatures of all your classes) and your pseudocode when you first start--that way you will know what each method is supposed to do!
- Be sure and double-check the formatting of the Javadoc comments (see the Vocab wiki on Moodle and pages 89-92 in the text).
Development Schedule
This is a big assignment--a bit bigger than any we've done before. You will want to start on this right away, and do a little bit each and every day. Try to add one piece of functionality per day, testing as you go. Work for an hour or so; if you get stuck you can put it down and come back later (this is often enough), or ask me for help. Develop iteratively---you should build a small piece, test that it works, and then move on to the next bit.
Below is a suggested order of steps, as well as good deadlines for getting each step working (if you haven't figure something out by the listed date, definitely ask for help!!):
- Mon 11/04: Create (and initialize!) instance variables and constructors. This is also a good time to add in method signatures and documentation!
- Wed 11/06: Be able to print out a String version of the word-so-far, and check if a letter is inside a word
- Fri 11/08: Be able to replace a letter in the word-so-far, and count the number of letters left to guess
- Mon 11/11: Ask the player for letters one after another replacing the letter in the word-so-far.
- Wed 11/13: Be able to play multiple games of Hangman; check if they finished the game or are out of lives, and if they guessed a previously-guessed letter.
- Fri 11/15: A fully completed and polished game, with full documentation
Scheduling Note: This homework is due the day after the second midterm. This means you'll also likely want to block out time for studying (though this homework does draw on many topics we've covered, so will be good practice). You may notice that there is not much of a jump between steps 5 and 6; this is intentional, so you have time to study as well as complete the homework.
Extensions
There is other functionality you might consider adding. They might be fun (or at leas good practice), and you can earn up to 5 points of extra credit for completing them. Only start on these once you have finished the rest of the assignment and you are sure that works. Be sure to save a copy of your working code in case you don't finish these!
-
Make sure that the letter the user guesses is actually a letter (by using the
Character.isLetter()
method we've talked about in class). If the user gives invalid input, keep asking them to try again until they get it right (hint: this will require a loop, and breaking this functionality into a helper method keeps things clean)! Similarly, you might make sure your program correctly works with upper-case and lower-case letetrs. -
Modify the program to print out a graphical representation of the hangedman! E.g.:
__O__ | / \
The hangedman should gain body parts as the game continues.
-
Alternatively, use a JFrame and JComponent to draw the Hangman as he appears! This involves converting your HangmanGame into a JComponent and adding a paintComponent() method; you can "redraw" the game by calling
this.repaint()
. Be sure to submit your text-based HangmanGame as well.-
You can get input from the user without using the text-based Scanner by using the following static method:
JOptionPane.showInputDialog("message you want to display to the user");
This method will return a String (just like using the .nextLine() method of Scanner), which you can then integrate into your program. We will talk about this more later in the semester
-
You can get input from the user without using the text-based Scanner by using the following static method:
Submitting Your Assignment
- Be sure and test your program thoroughly to make sure it works. Make sure it is documented with lots and lots of informative comments!
- Remember to fill out the README.txt file!
- Upload the entire project directory (including all Java files, as well as the README.txt) to the Hwk6 submission folder on the hedwig server. Make sure you upload your work to the correct folder!.
- This assignment is due at 11:59pm on Friday, Nov 15.
Grading
This assignment will be graded on approximately the following criteria:
- Your program has/uses appropriate instance variables and constructors [15%]
- Your program recognizes correct letters and replaces them in the word-so-far [15%]
- Your program recognizes incorrect letters causes the player to lose a life [10%]
- Your program recognizes previously guessed letters and displays appropriate output [5%]
- Your program gives appropriate output (e.g., of the word-so-far) [10%]
- Your game ends either when the player guesses all the letters or when the player loses all their lives [10%]
- Your program allows the player to play a second time, with a new word and everything [10%]
- Your code is carefully documented, with full Javadoc methods and inline comments [10%]
- Your code adheres to proper style guidelines. This means that variables are given descriptive names, local variables aren't specified as instance variables, you use return values and parameters to move data between methods, etc. Look over notes from previous assignments and labs for some guidance. If you have any specific questions about good style, please ask me! [10%]
- You have completed the included README.txt file [5%]