TextTwist

This program is based off of the Java Applet game TextTwist

Note: To use this game you must have a dictionary text file. You can find one here.

First, make two classes. Name one of them "Dictionary". The code should be as folows:

import java.io.*;
import java.util.Random; // for finding a random dictionary word
import java.util.ArrayList; // for the ArrayList

class Dictionary
{
final String FILENAME = "dictionary.txt"; // declare filename to use for dictionary
static String[] theWords; // declare dictionary array. It is static,
// so that multiple instances all share the
// one array of words
int numberOfDictionaryWords = 0; // used to count the number of words
Random rand = new Random(7); // 7 is the random number generator seed

public Dictionary() throws IOException
{ readDictionaryWordsFromFile();
rand = new Random(7); // 7 is the random number generator seed
}// end while
/**
* read from dictionary file first into an ArrayList, then copy the ArrayList
* into an array of Strings to be used elsewhere in the program.
*/
private void readDictionaryWordsFromFile() throws IOException
{
ArrayList wordList = new ArrayList();
String inputLine;

BufferedReader reader;
reader = new BufferedReader( new FileReader( "F:/dictionary.txt") ); // Associate with a file

int index = 0;
while ( (inputLine = reader.readLine()) != null) {
// convert the string to all lower case and store in array
int wordLength = inputLine.length();
// verify word length is valid and is not a proper name (begins with a capital)
if ( ( wordLength >= 3) && (wordLength <= 6) &&
Character.isLowerCase( inputLine.charAt(0)) &&
stringIsAllAlpha( inputLine)
) {
wordList.add( inputLine.toLowerCase() );
}
}
numberOfDictionaryWords = wordList.size(); // set the class variable for use elsewhere
// Now we know array size, so create array
theWords = new String[ numberOfDictionaryWords]; // declare the array to be correct size
// copy elements from ArrayList into theWords array of Strings to be used in rest of program
for (int i=0; i theWords[ i] = (String)wordList.get( i);
}
}//end class readDictionaryWordsFromFile()
private boolean stringIsAllAlpha( String theWord)
{
for (int i=0; i if ( ! Character.isLetter( theWord.charAt(i))) {
return false; // it DOES have nonalpha
}
}
return true; // all characters were alpha
}

/**
* Lookup word in dictionary using binary search, returning true if found,
* returning false otherwise.
*/
public boolean binarySearch( String lookupWord)
{
int low, mid, high; // array indices for binary search
int searchResult; // Stores 0 if search succeeded

// Binary search for word
low = 0;
high = numberOfDictionaryWords - 1;
while ( low <= high) {
mid = (low + high) / 2;
// Compare the lookupWord to the dictionary, returning a negative if
// the lookupWord preceeds the dictionary word, returning 0 if they
// are equal, and returning a positive number if the lookupWord is
// after the dictionary word.
searchResult = lookupWord.compareToIgnoreCase( theWords[ mid]);
if ( searchResult == 0) {
return true; // found the word
}
else if (searchResult < 0) {
high = mid - 1; // word should be located prior to mid location
}
else {
low = mid + 1; // word should be located after mid location
}
}

/* // Brute force method, takes *much* longer than binary search shown above
for (i=0; i< numberOfDictionaryWords; i++) {
if( lookupWord.compareToIgnoreCase( theWords[ mid]) == 0) {
return true; // Found the word!
}
}
*/
return false; // Did not find the word
}
/**
* Get a random word from the dictionary
*/
public String getRandomWord()
{
String possibleWord = ""; // initialize to an empty String

do {
possibleWord = theWords[ rand.nextInt( numberOfDictionaryWords)];
} while (possibleWord.length()!= 6);

return new String( possibleWord); // create a new string to return
}

public ArrayList findAllMatchingWords( char[] lettersToUse)
{
ArrayList wordList = new ArrayList(); // declare ArrayList to hold all words found
int i; // loop counter

for (i=0; i if ( lettersFromWordAreInArray( theWords[ i], lettersToUse) ) {
// dictionary word can be formed from letters, so store
// this word. First convert it to a String, then
// cast the string into a generic Object so that it
// can be stored in the ArrayList.
wordList.add( (Object)(new String( theWords[ i]) ) );
}
}
// Now create a new word list, sorted by size
ArrayList sortedWordList = new ArrayList();
for (int wordLength=1; wordLength<=6; wordLength++) {
for (i=0; i // compare current length of words we are looking for to
// the length of the word in the ArrayList
if ( ((String)(wordList.get(i))).length() == wordLength) {
sortedWordList.add( wordList.get(i));
}
}
}
return sortedWordList;
} public boolean lettersFromWordAreInArray( String dictionaryWord,
char[] originalLettersToUse)
{
// loop counting variables
int i,j;
int numberOfMatches = 0;

// copy the array so original is unchanged
char[] lettersToUse = new char[ originalLettersToUse.length];
for (i=0; i lettersToUse[ i] = originalLettersToUse[ i];
}
// Now compare letter by letter, blanking out matches so that
// they only match once
for (i=0; i boolean letterFound = false;
for (j=0; j if ( dictionaryWord.charAt( i) == lettersToUse[j]) {
// letter matched, so blank it out so it isn't used again
lettersToUse[ j] = ' ';
numberOfMatches++; // increment count of number matching
letterFound = true;
break; // break out of for loop, since letter already found }
}// end for (j...
// Verify letter was found
if ( !letterFound) {
return false; // this letter was not found, so word isn't a match
}
}// end for (i...
// see if entire word was found
if ( numberOfMatches == dictionaryWord.length() ) {
return true;
}
else {
return false; // word was not found
}
}
}

Next create a class called "Game". The code should be as follows:

import javax.swing.*; // for input dialog...
import java.io.*; // for file I/O
import java.io.*;
import java.util.Random;
import java.util.ArrayList;
import java.util.Date; // used for the timer

public class Game
{
// constants final int NUMBER_OF_COLUMNS = 3; // number of columns for display of words
static int TOTAL_TIME = 60; // total seconds for game
static String WORD; // starting word

// instance variables
private BufferedReader reader; // declare the input reader
private Dictionary theWords; // create an instance of dictionary words
private Random rand; // declare the random number generator
private ArrayList allWordsFound; // all possible dictionary words that
// can be found using given letters
boolean[] wordFoundFlag; // array of flags to track whether words were found
int score = 0; // score for game
long startMilliseconds; // stores time when game was started

/**
* Main method, used as entry point into the program
* for non-BlueJ use.
* Get time per session from command line.
*/
public static void main(String[] args) throws IOException
{
Game theGame = new Game(); // create the Game object, which calls constructor
WORD = new String("");
if ( args.length > 0) {
TOTAL_TIME = Integer.parseInt( args[0]);
}
if ( args.length == 2) {
WORD = args[1];
}
theGame.startGame();
}
/**
* Constructor for objects of class Game
*/
public Game() throws IOException
{
// initialize the console input reader
reader = new BufferedReader( new InputStreamReader(System.in) );
// create the dictionary of words
theWords = new Dictionary();
rand = new Random(11); // seed the random number generator
}

/** * Start the game. Display identifying information and game instructions.
*/
private void startGame() throws IOException
{
int i; // loop counter

// declare console reader
BufferedReader reader = new BufferedReader(
new InputStreamReader(System.in) );
displayIDInfo(); // display id information
displayInstructions(); // display program instructions
// Find a random 6-letter word (as a String). If a word was supplied
// from the command line, use that one instead.
String theWord;
if ( WORD.length() > 0) {
theWord = WORD;
}
else {
theWord = theWords.getRandomWord();
}
// convert the word to a char array
char[] lettersToUse = theWord.toCharArray();

// set up Game. This does several things:
// - set score to 0
// - set starting time in milliseconds
// - randomize the characters in array lettersToUse
// - create the ArrayList of all possible dictionary words
// that can be created using the lettersToUse
// - setup the wordFoundFlag boolean array that keeps track of which
// of the words have been guessed so far
lettersToUse = setupGame( lettersToUse);

// Main loop to play the game
do {
// display the words found so far
displayWordsFoundSoFar( allWordsFound, wordFoundFlag, "DisplayFound");

// verify that we still have time
long currentMilliseconds = new Date().getTime();
int secondsRemaining = (int) (TOTAL_TIME - (currentMilliseconds - startMilliseconds) / 1000);
System.out.print("Time Remaining: " + secondsRemaining);
if ( secondsRemaining < 0) {
System.out.println("\nSorry, time is up. Exiting...\n\n");
break; // break out of main loop
}
// Prompt for input
System.out.print(" Score: " + score + ". "); // show score
System.out.print("Letters to use are: ");
System.out.println( (new String(lettersToUse) ).toUpperCase() );
System.out.print("Enter command (x:exit, t:twist, s:set, c:cheat) or word: ");

// Read the next line of input and return it to the user
String userInput = new String( reader.readLine());
// Handle 'x' to exit separately, since we want to exit outer loop
if ( (userInput.length() == 1) && (userInput.charAt(0) == 'x') ) {
break; // exit from main loop to leave program
}
// Only handle special commands if a single character was entered
if ( userInput.length() == 1) {
switch ( userInput.charAt(0)) {
case 't': // twist letters by re-randomizing their order
lettersToUse = randomizeLetters( lettersToUse);
break;
case 's': System.out.print("Enter the new word to use: ");
String response = reader.readLine();
lettersToUse = setupGame( response.toCharArray() );
break;
case 'c': displayWordsFoundSoFar( allWordsFound, wordFoundFlag,
"DisplayAll");
break;
default: System.out.println("Invalid input, retry.");
break;
}
// Continue back up to loop to redisplay prompt
continue;
}
// See if userInput word is one of the possible words
int positionOfWordFound = allWordsFound.indexOf( (Object)userInput);
if ( positionOfWordFound != -1) {
// word was found. Display message and set boolean flag
System.out.println("\t \"" + userInput + "\" IS one of the words.");
if ( wordFoundFlag[ positionOfWordFound] != true) {
wordFoundFlag[ positionOfWordFound] = true;
// update score only if not already found
switch ( userInput.length()) {
case 3: score += 1; break;
case 4: score += 2; break;
case 5: score += 4; break;
case 6: score += 8; break;
default: System.out.println("*** Error in score value ***");
break;
}
}
// convert stored word to upper case
allWordsFound.set(positionOfWordFound, userInput.toUpperCase() );
}
else {
System.out.println("\t \"" + userInput +
"\" is NOT one of the words" +
" or was already found.");
}

} while (true); // last guess not yet reached

// display all the words
System.out.println("Words found are shown in capital letters.");
displayWordsFoundSoFar( allWordsFound, wordFoundFlag, "DisplayAll");

System.out.println("Score was " + score);
System.out.println("Thanks for playing, goodbye...\n");

}

/**
* Display the user identifying information
*/
private void displayIDInfo()
{
System.out.println(
"TextTwist: Guess as many 3 to 6 letter words as you \n" +
" can using only the supplied letters.\n" +
"CS 107 program #3\n" +
"Dale Reed, 2/25/04\n" +
"Java 1.4.2\n" +
"\n");
}

/**
* Display the program instructions
*/
private void displayInstructions()
{
System.out.println(
"Instructions: \n" +
"Enter a valid word that is 3 to 6 characters in length \n" +
"that can be formed using the displayed letters. \n" +
"Continue doing this until as many of the words are found as \n" +
"possible within the given time limits. \n" +
"Possible commands are: \n" +
" x to exit the game \n" +
" t to twist the letters into a different order \n" +
" s to set the word to letters of your choosing \n" +
" c to cheat, displaying all words \n");
}


/** * Set up Game. This does several things:
* - set score to 0
* - set starting time in milliseconds
* - randomize the characters in array lettersToUse
* - create the ArrayList of all possible dictionary words
* that can be created using the lettersToUse
* - setup the wordFoundFlag boolean array that keeps track of which
* of the words have been guessed so far
*/
private char[] setupGame( char[] lettersToUse)
{
// initialize score
score = 0;

// Set starting time
startMilliseconds = new Date().getTime();

// store original for checking later.
String originalWord = new String( lettersToUse);

// verify original word is actually in the dictionary
if ( ! theWords.binarySearch( originalWord)) {
System.out.println("The selected word \"" + originalWord + "\" is invalid. Exiting program...");
System.exit( -1); //abnormal termination
}

// Randomize the letters. Continue doing this while the resulting
// randomization is a valid dictionary word. This ensures the
// randomized word is NOT a valid dictionary word.
do {
lettersToUse = randomizeLetters( lettersToUse);
} while ( theWords.binarySearch( new String( lettersToUse)) == true );

// Find all other words in the dictionary that have the same letters, or
// a subset of the letters.
allWordsFound = theWords.findAllMatchingWords( lettersToUse);

// create parallel array of flags to track whether words were found
wordFoundFlag = new boolean[ allWordsFound.size()];
// initialize boolean flags for words found to all false
for (int i=0; i wordFoundFlag[ i] = false;
}

return lettersToUse;
}


/**
* Display the words found so far.
* the 'mode' parameter is one of:
* "DisplayFound": display only words found so far
* "DisplayAll": display all words (a cheat)
*/
private void displayWordsFoundSoFar( ArrayList allWordsFound,
boolean[] wordFoundFlag,
String mode)
{
int i; // loop counter

// display the words if it has been found, otherwise display underscores
// Display words in two columns, 8 characters per column.
for (i=0; i if ( (wordFoundFlag[ i]) || (mode.equals("DisplayAll")) ) {
// display word
System.out.print( (String)(allWordsFound.get( i)) );
}
else {
// display underscores to represent the letters
String theWord = (String)allWordsFound.get( i);
for (int j=0; j System.out.print('_');
}
}

// display blanks to fill out the column
int wordLength = ((String)(allWordsFound.get( i))).length();
for (int j=0; j<8-wordLength; j++) {
System.out.print(' ');
}
// if index is odd, display a new line
if ( ( (i+1) % NUMBER_OF_COLUMNS) == 0) {
System.out.println();
}
}//end for

System.out.println("\n\n");
}


/** * Receive a word in a char array, and randomize the letter arrangement,
* ensuring that the resulting sequence is not itself a valid dictionary
* word.
*/
private char[] randomizeLetters( char[] originalLetters)
{
int sizeOfOriginal = originalLetters.length;
int i; // loop counter
// create a temp array of same size as original
char[] tempLetters = new char[ sizeOfOriginal];

// Initialize tempLetters to blanks
for (i=0; i tempLetters[i] = ' ';
}
// for each letter in original array, place in a random spot
// in the new array
int nextIndex;
for (i=0; i // Keep looping until a blank destination is found
do {
nextIndex = rand.nextInt( sizeOfOriginal);
} while ( tempLetters[ nextIndex] != ' ');
// blank destination was found, so place character there
tempLetters[ nextIndex] = originalLetters[ i];
}

return tempLetters; // return the array with randomized characters
}