Prelab 9

It Boggles the Mind
Due by 10am, Monday 26 Nov 2012

In this prelab, you will familiarize yourself with some of the design and implementation issues of the upcoming lab 9. Please write or type up your solutions, and hand in a paper copy before class on Monday.


Motivation

The game Boggle is played with 16 dice that have letters on all faces. The dice are randomly deposited into a four-by-four grid so that the players see the 16 letters on the top faces. For example, the board may look like this:

Each player has a limited amount of time to identify as many words as can be located on the board. Words can be formed using adjacent letters in any direction (including diagonals) but cannot reuse letters or wrap around the board. For example, the board below contains the words dent, hit, tine, tide, hub, bun, hide, raid, rain, etc. The location of "tide" is highlighted in blue. Players accumulate points based on the number of words found.

In this week's lab, you are going to write a program that lets the user play a one-person version of Boggle. The GUI will be provided for you; your job will be to write the program that rolls the dice, reads and stores a dictionary, finds all dictionary words on the board, and checks whether given guesses are on the board. There's plenty to do, so let's get on with it!


Part 1 - Trie

This week's lab will have you implement your own Trie class called MyTrie, which should be a subclass of AbstractSet<String>. If you need it, you can read these lecture notes for details on the use and implementation of Tries.

  1. Draw the Trie that results from the insertion of the following words: "hello", "hellos", "hella", "apples", "bolivia", and "bologna". (Note that any Trie array entry for a given letter (say, "h") should point to at most one child. If you have more than one child of a given letter, you're constructing your Trie incorrectly and should re-read the lecture notes posted above.) You don't have to draw all possible child locations, just clearly label the edge.
















Data Members

Each Trie instance should contain the following data members:

    boolean isWord;   // whether this trie node is the end of a word
    int     size;     // the number of words represented by this trie
    Trie[]  children; // the children tries of this node

Constructors

The no-argument constructor for a Trie should initialize an empty Trie. The result should be a single Trie node whose isWord flag is false and whose children array is an array of null pointers. (You need to instantiate the array, but not its individual entries.) Recall that the size of the array is determined by the number of characters in the alphabet; in our case, that will be 26. (This is a good place for the use of a "static final" variable to define a constant, so that you don't need to hard-code the number 26 in more than one place in your program.)

Public Methods

int size()
Return the number of words stored in this Trie.

boolean containsEmptyString()
Return true if and only if the trie contains the empty string. (This method just returns the isWord flag in the root of the Trie.)

boolean contains(String string)
Return true if the trie contains the given string, false otherwise.
This should use recursion (on the Trie children).

boolean add(String string)
Insert string in the trie, if it is not already present. Return true if and only if the trie is modified by this operation.
This should use recursion (on the Trie children).

boolean isEmpty()
Return true if the trie contains no strings, false otherwise.
If you're updating your size class member correctly, this will be the usual one-liner.

Trie getChild(int n)
Return the nth subtrie of the given trie; that is, the one representing the letter ('a' + n). If the nth subtrie is empty, this method returns null.

String toString()
Return a string representation of the set of strings contained in the trie.
Hint: Use the private method toList() described below; this should get you an arrayList of the strings in the Trie; you can then just return the result of this arrayList's toString method.

Iterator<String> iterator()
Genereate an iterator over all the string in the trie. (Hint: Lists have an iterator() method that produces what you need! Use your toList method to get you the list you desire.)

Private Methods

private ArrayList<String> toList()
Return a List of strings contained in the trie in alphabetical order. (Hint: Use a recursive helper method with a prefix string as an argument.)

  1. Write pseudocode for the add(String string) method described above. Think about your base case carefully, and check your pseudocode on some small examples to be sure it works correctly. This method should be less than, say, 10 lines of pseudocode.







  2. Write pseudocode for the contains(String string) method. Again, think about your base case (it will most likely be the same as for the add method).







  3. Run your contains method on the trie from question 1, and the strings "hello" (contains should return true), "h" (contains should return false), "hellos" (contains should return true), and "helloz" (contains should return false). Just show the major steps, but really think about what your method is doing, and whether or not it works on these examples.









Part 2 - Square Class

As discussed, a Boggle board consists of a four-by-four grid of dice. You will be given a Square class that represents a square on the boggle board; that is, the "showing" side of one of the dice.


Part 3 - Boggle Class

Your next task will be to write the class Boggle that represents a Boggle board.

Data Members

Each Boggle board will contain the following data members:

    Trie lex;		// The dictionary, stored in a Trie
    Square[][] board;	// The 4x4 board
    Trie foundWords;	// The dictionary words appearing on the current board
    Trie guesses;	// The valid guesses made so far by our one player
    String[] dice;	// An array of dice -- explained later!

Constructors

You will write a Boggle constructor that takes a single String parameter that represents the file name of your lexicon (i.e. dictionary), and creates a new Trie out of the words in that lexicon. You will also also "initialize" the dice (see the private fillDice method).

Public methods

Square[][] getBoard()
Return the boggle board.

int numGuesses()
Return the number of guesses in guesses.

String toString()
Return the squares of the board, one row per line of the string.

boolean contains(String word)
Return true if the board contains the word word and false otherwise.
Use your Trie's contains method for this.

boolean addGuess(String guess)
Add guess to the list of guesses, if it is in the dictionary.
Return true if it was a valid guess and false otherwise.

void newGame()
Roll the dice and fill the board with new squares accordingly.
Construct a trie for the dictionary words found in the board.
Use the private methods below to do each of these steps.

ArrayList<Square> squaresForWord(String w)
Returns a list of valid Squares on the board that form the word w
It should try to find w starting from each square sq in the board, and return any list it may find.
This should use the private helper method squaresForWord(sq,w) described below.

Private Methods

void fillLexicon(String fname)
Construct a trie out of the dictionary in the file fname.

void fillDice()
Construct the dice from the file dice.txt.
Each line in the file contains the contents of the 6 sides of the die.
That is, die 0 has the sides L,R,Y,T,T, and E, and so on.
This method should store each line of the file into a different entry of the length-16 array dice.

fillBoardFromDice()
Construct a new board randomly out of the 16 die.
For each die i, choose a random face from dice[i]
Place the dice at random locations on the board.

Trie search(Square sq, String prefix)
Return all words in the dictionary that start with prefix and can be completed in a valid way on the board starting at square sq
You will use this when you have already produced prefix along some sequence of squares ending at sq, and want to continue from this point to create more words with prefix prefix.
Some specific implementation notes will be provided on the lab, although you are asked to come up with them in question 7

void fillFoundWords()
Construct the class member foundWords to contain all words on the board that are in the dictionary.
This should loop through all squares sq in the board, and call search(sq,"") from each one.

ArrayList<Square> squaresForWord(Square sq, String w)
Return a valid list of Squares onthe board starting with sq that form the word w.
This will use similar logic to the search method.
  1. Write pseudocode for the addGuess method. This should take under 6 lines of pseudocode (and you should use both your dictionary lex and your list of guesses guesses).









  2. Write pseudocode for the search(Square sq, String prefix) method. This should use recursion, and should take some thought. It is conceptually similar to one of breadth-first search or depth-first search (which?) (rhetorical question), where you are doing a search out from sq, having already visited the squares forming prefix. You may want to "mark" and "unmark" squares as you go along, to keep track of which squares are being used in the current word. This should take at most, I dunno, 12 lines of pseudocode.









  3. Run your algorithm on the first sample board, for the prefix "wa", starting at the square located in the third row, second column (i.e. the "i" square). You may assume that the "w" and "a" square are marked. Just show the major steps---enough to know that your algorithm is working. (Hint: "waif" and "wait" are words...)









Implementation Notes:

For this lab, whenever you see a "q" on the board, you should treat is as "qu".

  1. Give a suggestion (or two) of how you might handle the fact that the Boggle board will only have a single "Q"character but the wordlist includes "qu".