CSCI 161 Homework Assignment 6 - Evil Hangman

Due Fri Oct 26 at 11:59pm

Introduction

For this assignment, you will be modifying your Hangman game from the last homework and giving it the ability to become... well... evil. To do this, you'll practice with searching and sorting, using all sorts of collections, and extending your previous code.

The Assignment

You will be making an evil version of Hangman--that is, a single-player game of Hangman where the computer cheats. This game will work just like regular hangman, except that if the player guesses the correct letter, the computer changes to a different word. A flowchart describing one way the game works can be found here.

The output from the game should be the same as the previous version--however, it will be incredibly challenging for a player to actually guess the word in time!! Your HangmanGame program will be able to support both versions of Hangman: normal, and evil!


As always, be sure to download and unzip a copy of the the Homework 6 project. so that you can submit your project once completed. This project also contains some extra classes you can use in programming your game (the same WordList class from before).

NOTE: You will need to copy your HangmanGame class from the last assignment into this one! Make a new HangmanGame class in the Homework 6 project, but copy and paste the code from Homework 5 into it! Make sure that you can run this class and it works just like the first homework. If you didn't finish that homework, you will need to for this assignment!


Remember to read all the way through the instructions carefully! I reserve the right to not answer questions that are already answered in the assignment ;p

  1. Read the assignment

    Read all the way through the assignment carefully. Make sure you understand the big picture and what each step will accomplish. Then when you get to each step, focus on just that step, testing until you have it right!

  2. Make new constructor, fields, and the toggleEvil() method

    As always, we'll start with constructors and fields. You'll need to add a new field to represent whether your game is evil or not. Each of your existing constructors will need to initialize this field. Then create a new constructor (see the documentation) that takes in a boolean as a parameter as to whether the game is evil or not. Otherwise, this new constructor should look exactly like your default constructor

    • For this homework, you'll probably want to test it with a shorter list of words to use. You can do this by creating a new file file inside the project folder--for example, test_words.txt. Inside this file you can make a small list of words to choose from. Each word should be lower-case and goes on a separate line (with no other punctuation). You can tell your program to use this list instead of the normal list by specifying the filename in the constructor for your WordList object: WordList wl = new WordList("test_words.txt");
      • HINT: Your test words don't need to be actual words. A nice test set is: bead, beid, beod, baed, baid, boed, which lets you try to guess vowels and see if the proper words are disappearing.
    • You should also add a toggleEvil() method that changes whether the game is evil or not.
    Test that your constructors are making a game either evil or not evil (and that your toggle method works) by making new game options and inspecting them with BlueJ.
  3. Write the method signature for checkGuessEvil()

    So your evil game is going to work pretty much exactly the same way as the non-evil game: it will still ask the player for a guess, etc.. Really, the only difference is in how you check if a guess is correct--rather than saying that it is, you cheat and change the word! So make a new method checkGuessEvil() that will be the evil version of your previous checkGuess() method. Start by just making the signature--you'll fill the method in during a later step.

    • Now you'll need to adjust your playGame method so that it uses checkGuessEvil() instead of checkGuess() when you are in evil mode. You'll need to have your program make a decision based on which mode you are in, and then either call checkGuess or call checkGuessEvil(). In either case, the method called will return whether the letter was found and do any replacing if the letter was found. (Note that if you designed your helper methods differently than suggsted, you will need to adapt these instructions).
    • Test that this works--your game should continue to play as normal if you are not in evil mode, and the checkGuessEvil() method should be called if you are in evil mode (you can see if the method is called by including a temporary print statement such as "checkGuessEvil was called!"). You can even fill checkGuessEvil() temporarily with the code from checkGuess(), which should cause your game to play normally even when in evil mode.
    • Actually, because your word will keep changing, it might be a good idea to print out the current target word at the start of the playGame loop, for debugging purposes (you will of course comment out this println when you turn in your game)!
  4. Define and initialize the possibleWords variable

    The game will cheat by picking a new word whenever the player was about to get a letter right. This means that you'll need to pick a new word from the list. However, you can't just pick any word--the word need to match what you're currently telling the user it looks like. If you tell the user the word looks like "a _ _ _ _", then you can't pick "baseball" as your word! Similarly, if the user has already guessed 'b' and you said the word has no b's, you can't pick "baseball". So you'll want to keep track of what are the possible words you can pick from, so that when you pick a new word you're picking it from a list of valid options.

    • You'll need to make a new field to keep track of the possible words. Think about what kind of data structure this field will be. There may be a hint in the documentation. You'll also need to initialize the variable inside the setupGame method (even though it will only be used in evil mode, you can still keep track of it in all modes). Remember that the possible words lists should only contains that are actually possible given the current target word, revealed letters, and previously guessed letters.
    • Be careful not to change the main word list! You will need to use that list to play multiple games! Instead, you want to make a new list of words and just add all possible words to that list!
    • Remember to test that you've made and initialized this variable correctly by inspecting the HangmanGame object! It may also be helpful to print out this this list of possible words at the start of your playGame loop, for debugging purposes (again, comment out this printing before you turn in your program)!
  5. Write the findPossibleMatchingWords() method

    Next you're going to have to be able to find the list of possible words. no matter what the current state of the game is. Create a method to do this, that returns a new list of possible words. This method will look like a standard searching method. A very basic outline in pseudo-code for this method is below:

    for potentialWord in possible words
        determine if revealed letters of targetWord are at the same spots in potentialWord
        determine if unrevealed letters of targetWord are not in the alreadyGuessed list
        if(both of the above are true)
            add word to list of new possible words
    return new list of possible words
    • You'll need to pick possible words based on 2 criteria: (1) each revealed letter matches the possible word, and (2) any unrevealed letters have not already been guessed.
    • HINT: don't actually remove words from the possible words list! Instead, figure out whether a word is possible, then add it to a new list of possible words, and then return that new list (which you can assign to the possible words variable later)
    • This method is a bit harder to test, but you can temporary try calling it from your playGame or checkGuessEvil method, print out the results, and make sure it returns the words you'd expect it to at each stage in the game.
  6. Write the findPossibleWithoutLetter() method

    UPDATE: I'm not sure what I was thinking when I wrote the asignment, but you should be able to just re-use the above method instead of writing a new one. Writing another method is fine, but may not be necessary! I'm leaving the below instructions in case anyone needs them.

    When a user guesses a correct letter, you're going to want to see if you can tell them that's wrong and pick a different word. That is to say, you're going to want to check to see if there are any other possible words that don't have the guessed letter. Write a method that finds any possible words that don't have a given letter. This method will look very similar to the above method in structure.

    • You'll need to pick possible words based on just one criteria: they do not contain the given letter. You can assume that the words in the possible words list are otherwise valid.
    • Again, don't actually remove words from the possible words list! Instead, build a new list of valid words (words that don't have the letter) and return that.
    • This method is much easier to test than the last: just call it with a given letter, and make sure none of the words it returns has that letter!

  7. Write the checkGuessEvil() method

    Okay, now you should be all ready to fill in your checkGuessEvil() method! This method should work similarly to the original checkGuess() method. However, if it finds the guessed letter, it should instead check if there are any other possible words words that don't have that letter it can switch to. If there are, it can switch to one of those words and pretend that the letter was never there. If there aren't any words without that letter, then you'll have to reveal it and re-update the list of possible words (since you've now revealed a letter).

    Note that checkGuessEvil() is going to do the same work as checkGuess(), except that instead of immediately telling the player they got the right answer, we're going to see if we can change the target word so that they actually go it wrong. Some psuedocode is below

    go through each letter in the current word and see if the guess is right
    if the guess is wrong
      return that they are wrong (no further action required)
    if the guess is right
      check to see if there are any possible words (now that another letter has been guessed)
      if there is another possible word
        switch to another random possible word
        return that they are wrong (because the word has changed to make them wrong!)
      otherwise if there is NOT another possible word
        go through the underscores and reveal the letter
        update to see if there are any possible words (now that another letter has been revealed)
        return that the player got the letter right (and so shouldn't lose a life)
    • Remember to use the methods you just wrote and tested!
    • You can test this method by playing a HangmanGame, or testing the same way you tested your original checkGuess() method!
  8. Check that it works

    In the end, I should be able to play an evil game of hangman and lose. A lot. (You may want to increase the number of lives available so I have a fighting chance). I should be able to play multiple evil games, and use the toggleEvil() method to turn evil on and off between games.

  9. Clean up your code

    Remember to remove any debugging print statements! You don't want to give the game away to the user, do you? ;)

  10. Fill in documentation (if you haven't already!)

    Be sure to document all your code clearly using Javadoc comments and tags for each class. In addition, be sure to include inline comments explaining how your code works--particularly if you have a complicated sequence of if statements. NOTE: Private methods will not show up in BlueJ's Javadoc window, but you should still include Javadoc style comments for these. After you are done testing, feel free to make any of your helper methods private instead of public (this is better design).

Extensions

The same extensions from the last homework apply to this homework; if you haven't done them yet, you can do them now! I particularly recommend doing the graphical print-out, it may come in handy for a future homework! Remember to save a working copy of your code before trying any extensions, just in case something breaks!

Submitting

As always, before submitting, test each of your methods and classes thoroughly. Make sure your game works exactly as expected! When you're convinced it's ready to go, submit the project through BlueJ just like you did in the previous Assignments (selecting Hwk6 of course).

Submitter will look for the HangmanGame.java file.

Grading

This assignment will be graded on the following criteria:

Documentation



Class HangmanGame

java.lang.Object
  HangmanGame

public class HangmanGame

A class to play a game of Hangman. And possibly... an EVIL game of Hangman!

Version:
Oct 2012
Author:
Joel Ross

Constructor Summary
HangmanGame()
          Creates a new HangmanGame object--basicaly a wordlist that can be used to start a new game.
HangmanGame(boolean isEvil)
          Creates a new HangmanGame that is either evil or normal
HangmanGame(String debugWord)
          Creates a new HangmanGame object, with the wordlist containing only the given debugWord.
 
Method Summary
 boolean checkGuess(char letter)
          A helper method (that could be private) that checks to see if a given letter is present in the target word.
 boolean checkGuessEvil(char letter)
          A helper method (that could be private) that checks to see if a given letter is present in the target word, but cheats! If so, all instances of that letter in the target word are revealed.
 String currentGuessString()
          A helper method (that could be private) that returns a String representation of the current guess state.
 ArrayList<String> findPossibleMatchingWords()
          A helper method (that could be private) that finds the list of possible words that match the current revealed word.
 ArrayList<String> findPossibleWithoutLetter(char letter)
          A helper method (that could be private) which gets a list of possible words that don't include the given letter.
 char getGuess()
          A helper method (that could be private) that prompts the user to provide a guessed letter.
 void playGame()
          A method that plays a complete game of Hangman.
 void setupGame()
          A helper method (that could be private) which re-initializes variables for the start of a new game.
 void toggleEvil()
          Toggles whether the game is evil or not.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

HangmanGame

public HangmanGame()
Creates a new HangmanGame object--basicaly a wordlist that can be used to start a new game.


HangmanGame

public HangmanGame(boolean isEvil)
Creates a new HangmanGame that is either evil or normal

Parameters:
isEvil - whether or not the HangmanGame should be evil

HangmanGame

public HangmanGame(String debugWord)
Creates a new HangmanGame object, with the wordlist containing only the given debugWord. This enables testing of the game using a known word.

Parameters:
debugWord - The single word in the wordlist, used for testing and debugging
Method Detail

checkGuess

public boolean checkGuess(char letter)
A helper method (that could be private) that checks to see if a given letter is present in the target word. If so, all instances of that letter in the target word are revealed.

Parameters:
letter - the letter being guessed
Returns:
whether or not the letter was found in the word

checkGuessEvil

public boolean checkGuessEvil(char letter)
A helper method (that could be private) that checks to see if a given letter is present in the target word, but cheats! If so, all instances of that letter in the target word are revealed. This method cheats by picking a new word from the list of possibleWords when necessary, or trims down the size of possibleWords if the letter is correct

Parameters:
letter - the letter being guessed
Returns:
whether or not the letter was found in the word

currentGuessString

public String currentGuessString()
A helper method (that could be private) that returns a String representation of the current guess state. For example: "g _ e _ _" for the word "guess" if the letters 'g' and 'e' have been guessed.

Returns:
A String displaying the current guess of the word. Has the form "g _ e _ _ ".

findPossibleMatchingWords

public ArrayList<String> findPossibleMatchingWords()
A helper method (that could be private) that finds the list of possible words that match the current revealed word. Finds words that match every revealed character, and whose unrevealed letters have not previously been guessed.

Returns:
the list of possible words that match the revealed word

findPossibleWithoutLetter

public ArrayList<String> findPossibleWithoutLetter(char letter)
A helper method (that could be private) which gets a list of possible words that don't include the given letter. This method does not check that the possible words match the revealed characters; it assumes that the existing list of possible words match the current revealed word.

Parameters:
letter - the letter we want to exclude
Returns:
the list of possible words that exclude the letter

getGuess

public char getGuess()
A helper method (that could be private) that prompts the user to provide a guessed letter. This method converts any guesses into a single, lower-case character.

Returns:
a single, lower-case char that the player guessed

playGame

public void playGame()
A method that plays a complete game of Hangman. Method ends when the game has been either won or lost.


setupGame

public void setupGame()
A helper method (that could be private) which re-initializes variables for the start of a new game. Resets the word to guess, the current letters guessed, the number of lives, etc.


toggleEvil

public void toggleEvil()
Toggles whether the game is evil or not. If was evil, becomes not evil. If was not evil, becomes evil.