|
Comp202: Principles of Object-Oriented Programming II
Fall 2007 -- Hangman:
The BodyParts Package
|
Quick links to supporting pages:
Main page, MVC,
Sounds,
View & controller,
WordList
...Head Bone Connected to the Neck Bone...
...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:
-
Body parts are either visible or invisible --> 2 states!
-
The way that a body part paints on the screen depends on its state.
-
The state of a body part changes if the user guesses wrong.
-
2 & 3 above are both variant behaviors of a body part --> can be
implemented using visitors.
-
Invariant body part behaviors: toggle state, execute a visitor-pattern
algorithm, & draw its image on the screen.
-
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.
-
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).
The ILoseAdapter
is probably the least well understood part of the entire
Hangman project for most students. To help you understand its role,
consider the following questions:
- How exactly does one know when one has lost a game of
Hangman? That is, what event occurs that we know that the game has
been lost?
- Does when the game ends (by losing) depend on how
many or what sorts of body parts are available to be drawn?
- What is always the last item drawn when losing a
game?
- Does the body parts portion of the model have any
idea what to do when the game ends? Does it care what needs to
be done?
- On the other hand, does the body parts portion of the
model know when the game ends?
- What part of the system needs to be notified that the
game has ended? Is there more than one part? If so,
in what order do they need to be notified?
- How does the Command Design Pattern enable one to
create a response by only knowing that a response is needed but yet not
knowing what actually needs to be done?
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
-
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.
- Graphics is in the java.awt package -- be sure to import it!
-
Employ the same techniques used to test IWord to check for proper
visible/invisible painting behavior.
- 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
- 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.
-
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:
- Implement the makeAnswer() method so that it returns some specific String.
You can add the ability to return random answers later.
- Implement reset() so that it
- uses makeAnswer() to set the value of the "_answer" field
- initializes the "_word" field using WordFactory and _answer
- initializes the "_body" field using BodyFactory and the _loseAdapter
field.
- Implement the getWord() and getBody() accessor methods.
- Adapt your existing test code to implement the paint() method. Be sure to
pass the given Graphics object along without assuming anything about it.
- Adapt your existing test code to implement the guess() method. Note that
the current String representation of _word is always returned.
Testing
- 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:
- A working IWord system.
- A working IBody system.
- A working Hangman game class (which naturally incorporates the two above
criteria) whose methods have the following capabilities:
- makeAnswer(), reset(), getWord(), and getBody() all work as specified
above.
- paint() will print indications of the currently visible body parts,
which corresponds to the number o incorrect guesses.
- guess(c) will
- check if c exists in _word, toggling corresponding state to
visible everywhere c is located.
- show a dash (or some specifically identified symbol) when
corresponding IWord is invisible.
- Checks if the game has been won and indicates it on the console.
- make another body part visible if the guess is incorrect. (Note it
should not actually "paint" the body).
- Return the new (current) String representation of _word.
Last Revised
Thursday, 03-Jun-2010 09:52:28 CDT
©2007 Stephen Wong and Dung Nguyen