...Head Bone Connected to the Neck Bone...

The BodyParts Package

...from the spiritual song, "Dry Bones" or "Dem Bones".  
Info links:  
w. guitar chords , 2 versions , another version w. MIDI , cute animation w. MIDI

Let's think about a more OOP-ey way of drawing Hangman body parts.  Rather than a collection of conditional statements to determine what body parts to draw,  how can we get the job done "automatically"?

Oops, I hanged myself!...

Consider these ideas about Hangman body parts:

  1.  Body parts are either visible or invisible --> 2 states!
  2.  The way that a body part paints on the screen depends on its state.
  3.  The state of a body part changes if the user guesses wrong.
  4. 2 & 3 above are both variant behaviors of a body part --> can be implemented using visitors.
  5. Invariant body part behaviors: toggle state, execute a visitor-pattern algorithm, & draw its image on the screen.
  6. At any time, the whole picture can be represented by a list containing, as always, lists, some of which are visible and some of which are invisible.
  7. Consider the noose to be a body part --> to draw the noose is to lose the game. 

You'll need to study the documentation about the BodyPart package carefully.

The first thing you should notice is how similar the BodyPart system is to the WordList system. Most parts of it should be self-explanatory, but here's some more on select portions:

ABodyPart

This class represents an abstract non-empty body list.   It has an abstract draw() method that is overriden by its subclasses to draw the specific body part.    It has a visible and invisible state like a NEWord and the execute() and toggleState() methods are likewise delegated to it.    You can write this class yourself.

Why do you think that "host" has a different type in ABodyState.execute() vs. ABodyState.toggleState()? This is very deliberate and it is important that you understand the reason why. Be sure to ask someone if you can't figure it out.

Note that unlike a regular list, ABodyPart has no "first". Is that a problem?

NoosePart

The noose is a funny thing--is it or isn't a body part?   It's a body part in that it is drawn on the screen.   It's a special part though, in that to draw it is to lose the game.    Does the rest of the system need to know that it is drawing the noose?   That is, does the rest of the system need to figure out whether or not the game has been lost?    You know the mantra....

So the noose has to somehow signal the rest of the system that it has been drawn.     But what is the "rest of the system" to the noose?   -- It's view!   So, we can use the same architecture that the whole game uses, but on a smaller scale.    That is, NoosePart needs a adapter to link it to its view.  How does NoosePart.draw() differ from the other concrete ody part draw() methods?  

BodyFactory

Very similar to the WordFactory, except that it makes a list of body parts.    Should the noose go in first (right next to the empty list) or last?

The BodyFactory needs an ILoseAdapter (see below), which you can set to null for now. Comment out the code in NoosePart that needs it too.

We will give you a couple of concrete ABodyPart classes so you can see how the graphics are drawn. You can make the rest. Should your code depend on how many body parts are available?

ILoseAdapter

That's what the ILoseAdapter is for (get it? -- Hey, it's late when I writing this!).   This is the adapter that is handed to the BodyFactory who uses it to construct the NoosePart that the NoosePart uses to tell its view that the game is lost.   Whew! 

Who is the NoosePart's view?

The NoosePart must notify its view (the HangmanGame) who, after doing whatever it needs to do, in turn notifies the HangmanGame's view (the HangmanFrame). 

IBodyAlgo

A visitor pattern algorithm for the state-patterned ABodyPart .   No problems here, right?

PaintAlgo

An IAlgo to paint a list of ABodyParts.    What does the invisible case do? -- there's more than one choice here.

Testing

  1. Implement one subclass with a concrete draw() method.  You won't be able to implement that actual drawing until we get the view up and running, so for now, just comment out any graphics code and have the class print something like "Right leg drawn" to the console when it's draw() method is called.
  2. Graphics is in the java.awt package -- be sure to import it!
  3. Employ the same techniques used to test IWord to check for proper visible/invisible painting behavior.
  4. The input parameter to execute() technically needs to be a Graphics object to execute the PaintAlgo, but since we aren't actually painting anything yet, you can use "null" instead for now.

LoseOneAlgo

This is the algorithm on the list of body parts that will make one more body part show on the screen.    

Why can't this algorithm be used to signal the end of the game?

This algorithm is of the same structure as all the other algorithms we have done so far.   I think you can handle it.

Testing

  1. Add testing code to check if the LoseOneAlgo works properly. You need to make sure that it works more than once, that is, do a sequence like LoseOne then Paint, then LoseOne then Paint, etc.
  2. When should loseOneAlgo be executed by an IBody?   Find out and write a test routine that combines the routines you've already done to create a process that mimics actual Hangman game behavior. Your test code should be able to tell if the game has been won (just print a message on the console for now).

HangmanGame

At this point, we can actually write about 75% of the code for the HangmanGame class. We'll leave the Running/NonRunning state behavior for later. But for now, here's what you should do:

  1. Implement the makeAnswer() method so that it returns some specific String. You can add the ability to return random answers later.
  2. Implement reset() so that it
    1. uses makeAnswer() to set the value of the "_answer" field
    2. initializes the "_word" field using WordFactory and _answer
    3. initializes the "_body" field using BodyFactory and the _loseAdapter field.
  3. Implement the getWord() and getBody() accessor methods.
  4. Adapt your existing test code to implement the paint() method. Be sure to pass the given Graphics object along without assuming anything about it.
  5. Adapt your existing test code to implement the guess() method. Note that the current String representation of _word is always returned.

Testing

  1. Modify your test code to instantiate a HangmanGame and test all the methods you just implemented. Use "null" for the required Graphics input parameter.

Checklist

At this point you should be able to demonstrate:

  1. A working IWord system.
  2. A working IBody system.
  3. A working Hangman game class (which naturally incorporates the two above criteria) whose methods have the following capabilities:
    1. makeAnswer(), reset(), getWord(), and getBody() all work as specified above.
    2. paint() will print indications of the currently visible body parts, which corresponds to the number o incorrect guesses.
    3. guess(c) will
      1. check if c exists in _word, toggling corresponding state to visible everywhere c is located.
      2. show a dash (or some specifically identified symbol) when corresponding IWord is invisible.
      3. Checks if the game has been won and indicates it on the console.
      4. make another body part visible if the guess is incorrect. (Note it should not actually "paint" the body).
      5. Return the new (current) String representation of _word.