CVS: TapestryBook/hangman1/src/java/hangman1 WordSource.java,NONE,1.1 Guess.java,NONE,1.1 Visit.java,NONE,1.1 Win.java,NONE,1.1 Lose.java,NONE,1.1 Game.java,NONE,1.1 WordList.txt,NONE,1.1 Home.java,NONE,1.1
Howard Lewis Ship <[email protected]> Sat, 12 Jul 2003 08:50:28 -0700
| Newsgroups | gmane.comp.java.tapestry.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /cvsroot/tapestry/TapestryBook/hangman1/src/java/hangman1
In directory sc8-pr-cvs1:/tmp/cvs-serv24404/hangman1/src/java/hangman1
Added Files:
WordSource.java Guess.java Visit.java Win.java Lose.java
Game.java WordList.txt Home.java
Log Message:
Reorganize more examples for common build file.
--- NEW FILE: WordSource.java ---
package hangman1;
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:50:26 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: Guess.java ---
package hangman1;
import org.apache.tapestry.IAsset;
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:50:26 hship Exp $
*
**/
public class Guess extends BasePage
{
private char _letter;
private boolean _letterGuessed;
private int _guessIndex;
/**
* This method must return the page back to its pristine state.
*
**/
public void initialize()
{
_letter = 0;
_letterGuessed = false;
_guessIndex = 0;
}
public char getLetter()
{
return _letter;
}
public void setLetter(char letter)
{
_letter = letter;
}
/**
* The {@link Game} stores the letters of the target word as lower case,
* but the UI looks better if the labels on the images are in upper case.
*
**/
public String getLetterLabel()
{
return ("" + _letter).toUpperCase();
}
/**
* Returns the image for the current letter (which may be an underscore
* as well).
*
**/
public IAsset getLetterImage()
{
if (_letter == '_')
return getAsset("dash");
return getAsset("" + _letter);
}
/**
* Returns true if the letter corresponding to the
* current {@link #getGuessIndex() guess index}
* has already been guessed by the user.
*
**/
public boolean isLetterGuessed()
{
return _letterGuessed;
}
/**
* Returns the current guess index, a number beteween 0
* and 25 which represents the letter to be guessed.
*
**/
public int getGuessIndex()
{
return _guessIndex;
}
public void setLetterGuessed(boolean letterGuessed)
{
_letterGuessed = letterGuessed;
}
public void setGuessIndex(int guessIndex)
{
_guessIndex = guessIndex;
}
/**
* Return the image to display for the current
* {@link #getGuessIndex() guess index}, either a blank
* space if the letter has already been guessed, or
* the image for the corresponding letter.
*
**/
public IAsset getGuessImage()
{
if (_letterGuessed)
return getAsset("space");
String name = "" + getLetterForGuessIndex();
return getAsset(name);
}
/**
* Returns the letter corresponding to the
* current {@link #getGuessIndex() guess index}
* as a letter between 'a' and 'z'.
*
**/
public char getLetterForGuessIndex()
{
return (char) ('a' + _guessIndex);
}
/**
* Returns the label for the guess image, either a space,
* or an upper-case letter.
*
**/
public String getGuessLabel()
{
if (_letterGuessed)
return " ";
char ch = Character.toUpperCase(getLetterForGuessIndex());
return new Character(ch).toString();
}
/**
* 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
Object[] parameters = cycle.getServiceParameters();
Character guess = (Character) parameters[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: Visit.java ---
package hangman1;
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:50:26 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;
}
}
--- NEW FILE: Win.java ---
package hangman1;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
/**
*
* Page displayed when the player wins ... succesfully guesses
* all the letters in the word.
*
* @author Howard Lewis Ship
* @version $Id: Win.java,v 1.1 2003/07/12 15:50:26 hship Exp $
*
**/
public class Win extends BasePage
{
private char _letter;
/**
* This method must return the page back to its pristine state.
*
**/
public void initialize()
{
_letter = 0;
}
public char getLetter()
{
return _letter;
}
public void setLetter(char letter)
{
_letter = letter;
}
public String getLetterLabel()
{
char upper = Character.toUpperCase(_letter);
return new Character(upper).toString();
}
public IAsset getLetterImage()
{
if (_letter == '_')
return getAsset("dash");
return getAsset(new Character(_letter).toString());
}
/**
* Listener method; invokes {@link hangman1.Visit#startGame(IRequestCycle)}.
*
**/
public void playAgain(IRequestCycle cycle)
{
Visit visit = (Visit) getVisit();
visit.startGame(cycle);
}
}
--- NEW FILE: Lose.java ---
package hangman1;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
/**
*
* Page displayed when the player misses too many guesses.
* Displays the target word, and gives the player a chance
* to start a new game.
*
* @author Howard Lewis Ship
* @version $Id: Lose.java,v 1.1 2003/07/12 15:50:26 hship Exp $
*
**/
public class Lose extends BasePage
{
private char _letter;
public void initialize()
{
_letter = 0;
}
public char getLetter()
{
return _letter;
}
public void setLetter(char letter)
{
_letter = letter;
}
public char getLetterLabel()
{
return Character.toUpperCase(_letter);
}
public IAsset getLetterImage()
{
String name = new Character(_letter).toString();
return getAsset(name);
}
/**
* Listener method; invokes
* {@link hangman1.Visit#startGame(IRequestCycle)}.
*
**/
public void playAgain(IRequestCycle cycle)
{
Visit visit = (Visit) getVisit();
visit.startGame(cycle);
}
}
--- NEW FILE: Game.java ---
package hangman1;
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: WordList.txt ---
# $Id: WordList.txt,v 1.1 2003/07/12 15:50:26 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 hangman1;
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:50:26 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);
}
}
-------------------------------------------------------
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