CVS: TapestryBook/hangman2/src/java/hangman2 WordList.txt,NONE,1.1 Home.java,NONE,1.1 WinLose.java,NONE,1.1 Letter.java,NONE,1.1 Guess.java,NONE,1.1 WordSource.java,NONE,1.1 Game.java,NONE,1.1 Visit.java,NONE,1.1
Howard Lewis Ship <[email protected]> Sat, 12 Jul 2003 08:51:54 -0700
| Newsgroups | gmane.comp.java.tapestry.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /cvsroot/tapestry/TapestryBook/hangman2/src/java/hangman2
In directory sc8-pr-cvs1:/tmp/cvs-serv24761/hangman2/src/java/hangman2
Added Files:
WordList.txt Home.java WinLose.java Letter.java Guess.java
WordSource.java Game.java Visit.java
Log Message:
Reorganize more examples for common build file.
--- NEW FILE: WordList.txt ---
# $Id: WordList.txt,v 1.1 2003/07/12 15:51:52 hship Exp $
#
# One word per line. Case and whitespace is ignored. Comments
# are lines that begin with hash. Blank lines are ignored.
# The UI is configured for max word length of eight.
tapestry
peanut
pinball
helmet
stereo
citadel
gargoyle
cranium
axiom
media
virus
orbit
wizard
golden
revenge
bowling
annual
mercy
anatomy
zanzibar
pepper
connect
monster
impress
doctor
horror
cooking
virtual
power
freight
formula
board
eclipse
biology
--- NEW FILE: Home.java ---
package hangman2;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
/**
*
* This class contains the logic for the Home page; currently, that's very straight forward, a
* <em>listener method</em> that is invoked when the user clicks the
* start button on the page.
*
* @author Howard Lewis Ship
* @version $Id: Home.java,v 1.1 2003/07/12 15:51:52 hship Exp $
*
**/
public class Home extends BasePage
{
public void start(IRequestCycle cycle)
{
// Get the visit object and cast it to the application-specific class, Visit.
Visit visit = (Visit)getVisit();
visit.startGame(cycle);
}
}
--- NEW FILE: WinLose.java ---
package hangman2;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
/**
*
* Used with both the Win and the Lose page, simply to
* provide a place for the playAgain listener method.
*
* @author Howard Lewis Ship
* @version $Id: WinLose.java,v 1.1 2003/07/12 15:51:52 hship Exp $
*
**/
public class WinLose extends BasePage
{
/**
* Listener method; invokes {@link hangman1.Visit#startGame(IRequestCycle)}.
*
**/
public void playAgain(IRequestCycle cycle)
{
Visit visit = (Visit) getVisit();
visit.startGame(cycle);
}
}
--- NEW FILE: Letter.java ---
package hangman2;
import org.apache.tapestry.AbstractComponent;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
public abstract class Letter extends AbstractComponent
{
public abstract boolean isDisabled();
public abstract char getLetter();
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
{
writer.beginEmpty("img");
writer.attribute("src", getLetterImage().buildURL(cycle));
writer.attribute("alt", getLetterLabel());
writer.attribute("height", 36);
writer.attribute("width", 36);
writer.attribute("border", 0);
}
public IAsset getLetterImage()
{
if (isDisabled())
return getAsset("space");
char letter = getLetter();
if (letter == '_')
return getAsset("dash");
return getAsset("" + letter);
}
public String getLetterLabel()
{
if (isDisabled())
return " ";
return ("" + getLetter()).toUpperCase();
}
}
--- NEW FILE: Guess.java ---
package hangman2;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
/**
*
* A page that presents the state of the game to the user and allows the user to
* make guesses (by clicking on images for different letters).
*
* @author Howard Lewis Ship
* @version $Id: Guess.java,v 1.1 2003/07/12 15:51:52 hship Exp $
*
**/
public abstract class Guess extends BasePage
{
public abstract char getLetter();
/**
* Returns true if the letter corresponding to the
* current {@link #getGuessIndex() guess index}
* has already been guessed by the user.
*
**/
public abstract boolean isLetterGuessed();
/**
* Returns the current guess index, a number beteween 0
* and 25 which represents the letter to be guessed.
*
**/
public abstract int getGuessIndex();
/**
* Returns the letter corresponding to the
* current {@link #getGuessIndex() guess index}
* as a letter between 'a' and 'z'.
*
**/
public char getLetterForGuessIndex()
{
return (char) ('a' + getGuessIndex());
}
/**
* Listener method for the select link component. We define the parameter
* to be the character to guess.
*
**/
public void makeGuess(IRequestCycle cycle)
{
// Java wraps the char as an instance of Character
Character guess = (Character) cycle.getServiceParameters()[0];
char ch = guess.charValue();
// Get the Visit and cast it to our application-specific
// class.
Visit visit = (Visit) getVisit();
visit.makeGuess(cycle, ch);
}
}
--- NEW FILE: WordSource.java ---
package hangman2;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.tapestry.ApplicationRuntimeException;
/**
*
* Used by {@link hangman1.Game} to obtain a a random word.
*
* @author Howard Lewis Ship
* @version $Id: WordSource.java,v 1.1 2003/07/12 15:51:52 hship Exp $
*
**/
public class WordSource
{
private int _nextWord;
private List _words = new ArrayList();
public WordSource()
{
readWords();
}
private void readWords()
{
try
{
InputStream in = getClass().getResourceAsStream("WordList.txt");
Reader r = new InputStreamReader(in);
LineNumberReader lineReader = new LineNumberReader(r);
while (true)
{
String line = lineReader.readLine();
if (line == null)
break;
if (line.startsWith("#"))
continue;
String word = line.trim().toLowerCase();
if (word.length() == 0)
continue;
_words.add(word);
}
lineReader.close();
}
catch (IOException ex)
{
throw new ApplicationRuntimeException(
"Unable to read list of words from file WordList.txt.",
ex);
}
// Randomize the word order
Collections.shuffle(_words);
}
/**
* Gets the next random word from the list. Once the list is exhausted, it
* is shuffled and the first word is taken; this ensures that the user won't
* see a repeat word until all words in the list have been played.
*
**/
public String nextWord()
{
if (_nextWord >= _words.size())
{
_nextWord = 0;
Collections.shuffle(_words);
}
return (String) _words.get(_nextWord++);
}
}
--- NEW FILE: Game.java ---
package hangman2;
public class Game
{
private String _targetWord;
private int _incorrectGuessesLeft;
private char[] _letters;
private boolean[] _guessed = new boolean[26];
private boolean _win;
/**
* Returns true if the player has guessed all letters in the word.
*
**/
public boolean isWin()
{
return _win;
}
/**
* Returns an array of letters that have been guessed by the player, with
* an underscore for each unguessed position. Once the player loses,
* this returns the array of actual letters in the target word.
*
* <p>
* The caller must not modify this array.
*
**/
public char[] getLetters()
{
return _letters;
}
/**
* Returns the number of incorrect guesses remaining.
* An incorrect guess when this is already zero results
* in a loss.
*
**/
public int getIncorrectGuessesLeft()
{
return _incorrectGuessesLeft;
}
/**
* Returns an array of flags indicating which letters have already been guessed.
* There are 26 flags, one for each letter, starting with 'A' at index 0, up to 'Z' at
* index 25.
*
* <p>The caller must not modify this array.
*
**/
public boolean[] getGuessedLetters()
{
return _guessed;
}
/**
* Initializes the Game with a new target word. This resets the
* incorrectGuessesLeft count, and initializes the array of
* letters and letters guessed.
*
**/
public void start(String word)
{
_targetWord = word;
_incorrectGuessesLeft = 5;
_win = false;
int count = word.length();
_letters = new char[count];
for (int i = 0; i < count; i++)
_letters[i] = '_';
for (int i = 0; i < 26; i++)
_guessed[i] = false;
}
/**
* The player makes a guess. If the letter has already been
* guessed, then no change occurs. Otherwise, there's a check
* to see if the guess fills in any positions in the target word.
* This may result in a win. If the guess doesn't match any
* letter of the word, then a failure occurs; when enough
* failures occur, the game results in a loss.
*
* @return true if further guesses are allowed (this guess did
* not result in a win or a loss), or false if further guesses
* are not allowed (the player guessed the word, or used up
* all possible incorrect guesses).
*
**/
public boolean makeGuess(char letter)
{
char ch = Character.toLowerCase(letter);
if (ch < 'a' || ch > 'z')
throw new IllegalArgumentException("Must provide an alphabetic character.");
int index = ch - 'a';
// If the player (somehow) guesses the same letter more than once, it does not affect
// state of the game.
if (_guessed[index])
return true;
_guessed[index] = true;
boolean good = false;
boolean complete = true;
for (int i = 0; i < _letters.length; i++)
{
if (_letters[i] != '_')
continue;
if (_targetWord.charAt(i) == ch)
{
good = true;
_letters[i] = ch;
continue;
}
// An empty slot that does not match
// the guess, so the word is not
// complete.
complete = false;
}
if (good)
{
_win = complete;
return !complete;
}
if (_incorrectGuessesLeft == 0)
{
// Replace the letters array with the solution
_letters = _targetWord.toCharArray();
return false;
}
_incorrectGuessesLeft--;
// Not a good guess, but not a loss yet
return true;
}
}
--- NEW FILE: Visit.java ---
package hangman2;
import org.apache.tapestry.IRequestCycle;
/**
*
* The Visit class runs most of the game logic and acts as controller, mediating
* between the pure interface code and the pure logic code.
*
* @author Howard Lewis Ship
* @version $Id: Visit.java,v 1.1 2003/07/12 15:51:52 hship Exp $
*
**/
public class Visit
{
// In a real application, the word source would be shared between all sessions.
// Here, we just allow each Visit to have its own instance.
private WordSource _wordSource = new WordSource();
// On the other hand, the Game is specifically for this
// Visit and only this Visit.
private Game _game = new Game();
public void startGame(IRequestCycle cycle)
{
_game.start(_wordSource.nextWord());
// Now that the Game is initialized, we can go to the Guess
// page to allow the player to start making guesses.
cycle.activate("Guess");
}
/**
* Processes the player's guess, possibly updating the response
* page to be "Win" or "Lose".
*
**/
public void makeGuess(IRequestCycle cycle, char ch)
{
// If this return true, then stay on this page at let
// player keep guessing.
if (_game.makeGuess(ch))
return;
cycle.activate(_game.isWin() ? "Win" : "Lose");
}
/**
* Returns the {@link Game} instance for this Visit; this is used
* primarily by the {@link Guess} page to display things like
* the number of remaining guesses and the list of guessed
* and unguessed letters.
*
**/
public Game getGame()
{
return _game;
}
}
-------------------------------------------------------
This SF.Net email sponsored by: Parasoft
Error proof Web apps, automate testing & more.
Download & eval WebKing and get a free book.
www.parasoft.com/bulletproofapps1