Csci 210 Lab: Boggle Csci 210 Lab: Boggle (Laura Toma adapted from Eric Chown) Overview In this assignment, you will implement the back-end logic for a graphical version of the board game Boggle. Specifically, you will design and implement the algorithmes necessary for a computer player to play against a human opponent. If you get everything right, the computer player should almost always "win" the game by a large margin, but it will be fun to play anyway. The main focus of this assignment is designing operations that will efficiently find words that appear in a Boggle board using recursion. Because your focus will be on the back-end logic, you are provided with a graphical display program, BoggleGUI. This program knows how to generate and render a random Boggle board, interact with a human player, and display results of the game. It will use your code to verify input from the human player, and to obtain the computer player's results. This lab has two parts. In the first part, you are to write a method to check whether or not words are on the board or not. In the second part, you are to write a function to find all possible words on the board. Getting started Get the Boggle files from here. A little background on Boggle The game of Boggle uses a board which is a 4-by-4 grid on which you randomly shake and distribute 16 dice. The 6-sided dice have letters rather than dots on the faces, creating a grid of letters in which you find words. In the original version, the players all start together and write down all of the words they can find by tracing paths through neighboring dice faces on the board. Two dice are neighbors if they are next to each other horizontally, vertically, or diagonally (So there are up to eight neighbors of a die; dice on the edges of the grid have fewer neighbors). A die can only be used once in a word, and a word must be at least 3 letters long. At the end of a three minute session, all players stop recording words. Words found by more than one player are then removed from the players' lists and the players receive points equal to the number of letters beyond the minimum word length found in their remaining words. In the computer version of the game, the human player gets to go first. (Don't worry, the computer player will still trounce you.) The player proceeds to enter in the text area at the bottom of the window, one at a time, each word that she finds in the game board. A valid word must meet four requirements: it must contain at least the minimum number of letters; the word must not already have been entered by the player; the word can be formed from the letters showing on the board following in order a path through neighboring dice without using the same die twice; and the word must exist in the official lexicon of words. All valid words entered by the player are highlighted graphically on the board display. The computer player determines the validity of the words, but we assume it can be trusted to do so fairly. Unlike the original version, there is no time limit here. Instead, the player indicates that she is through entering words by hitting a lone extra carriage return, as if entering an empty word. At this point, the computer gets to take its turn. The computer searches through the board looking for all the valid words it can find. The computer typically beats the human player mercilessly, but the player is free to try again by starting a new game. Scoring is as for the original version of the game, except that words are not removed from the players' lists (if they were, the human player would never have any words left!). The computer version that I have given you has been handicapped in a few ways (e.g. it doesn't bother with shorter words). Your final version shouldn't be. The structure of the implementation There are two java files: BoggleGUI.java---the front-end graphical interface, and BogglePlayer.java---used by BoggleGUI to execute the required back-end logic. BoggleGUI.java is complete and you do not need to modify it. You will only be editing BogglePlayer.java. In order that you might focus on the most interesting part of the assignment (involving recursion) some of the methods have been written for you, as follows. buildLexicon: this method takes as argument a Set containing the list of words specifying the official lexicon to be used for the game (a set is a collection of elements that does not allow duplicates; check out the Java doc on Sets). Each word in the Set will be a String consisting of lowercase letters a-z only. This function loads the words into an efficient data structure that can be used internally as needed by the BogglePlayer. setBoard: this method takes in an array of Strings of length N^2 specifying the layout of dice on the N-by-N board. (In official standard Boggle N is 4; but your BogglePlayer should work for any square board.) The elements of the array specify the letters found on dice going from left to right, and top to bottom of the board. (So the element indexed 0 is a String containing the letter(s) on the die at the upper left corner; on a 4x4 board, the element indexed 3 is the top right letter, and index 15 gives you the letter at the bottom right.) Each String may be upper or lower case, and may contain one or more letters; for purposes of finding words on the board, they should be considered lowercase. (In official Boggle, these Strings will contain one character only, except for the double letter die face "Qu"; but your BogglePlayer should work correctly for arbitrary strings.) isInLexicon: this function takes in a String and determines whether the String can be found in the lexicon specified by the most recent call to buildLexicon(). The function returns true if the word is in the lexicon and false if it is not in the lexicon. If buildLexicon() has not yet been called, the behavior of this method is undefined. What you need to implement Although you will probably implement other helper methods too, your BogglePlayer class must implement methods with these exact signatures:
public Vector isOnBoard(String wordToCheck)
public Vector getAllValidWords(int minimumWordLength)
isOnBoard: this function takes as argument a String and determines whether the String can be found in the board specified by the most recent call to setBoard(). The return type of this function is a Vector object containing Integer elements. If it is possible to find the word in the current board, the function returns a Vector of Integers representing the locations of the dice used to form the word, in order---where a location is a one-dimensional offset in the range 0..N2-1, obtained from (row,col) using the reverse transformation in setBoard(). If is is NOT possible to form the word, the function returns null. If setBoard() has not yet been called, the behavior of this method is undefined. getAllValidWords: this function returns, as a sorted Vector of Strings, all the words that are of at least the given minimum length; are in the lexicon specified by the most recent call to buildLexicon(); can be found by following a simple path on the board specified by the most recent call to setBoard(); and were not found by previous calls to isOnBoard. If either buildLexicon() or setBoard() has not yet been called, this method should return null. For debugging purposes, you may also find useful to change getCustomBoard. This is a function that will aid you immensely in debugging if you are wise enough to use it. This function will be called by the GUI application when a choice of "custom", non-randomly selected board layout is requested. This function should return an array of 16 Strings representing the desired dice layout based on the usual index from 0 to 15. The idea here is that you can work on repeatable boards. If you have a bug you can attack it relentlessly. Hints and Suggestions In a project of this complexity, it is important that you get an early start and work consistently toward your goal. To be sure that you are making progress, it also helps to divide up the work into manageable pieces, each of which has identifiable milestones. Here is a suggested plan of attack that breaks the problem down into phases: Compile and run the Boggle files, try playing a game with the computer, get familiar with what is happening, and understand where your code will fit in. Read BogglePlayer.java and understand the overall structure of the game and the methods that have already been implemented. Implement isOnBoard: Remember that a valid word must correspond to a simple path according to the neighborhood relation on the dice. You should search the board recursively, trying to find a legal formation of the user's word. This recursion can be made what you might call a "fail-fast" recursion: as soon as you realize you can't form the word along a path, you can immediately backtrack or move on to the next untried path start point. Reject any word that cannot be formed from the letters currently on the board. Because it is so important I'm going to make the last point over again. The key thing is that when you make your recursive calls you need to consider the results. For example, when looking for a word, if your recursive call succeeds then you are done. Pass the happy results along and stop making recursive calls. However, if it didn't work then you need to keep searching. Understanding this simple aspect of the assignment will save you countless hours of frustrating debugging. Think about your base cases. You should essentially have two -- one for the case when you hit a dead end, and another for the case that you have found the whole word. Think about your recursive case. Imagine you are checking if the word "boggle" is on the board. Your recursive call might target a square to check if it is the "b" and if that is true then it could call itself looking for the rest of the word. The call to itself would probably consist of 8 different calls representing the eight neighbors of the original square. Don't forget that an individual square cannot be used more than once in a boggle word. Therefore you must do something to mark that a square has already been used. Your experience with blobCount and Maze should help. A good way to write this is by having the isOnBoard call another method which is recursive. Your isOnBoard method can help by only calling the recursive method when it has identified a good candidate for the word. For example, if you are looking for "computer" it would only call the recursive method in the locations where there are "c"s. Implement getAllValidWords: Now it's time to implement the world-class computer player. The computer should easily trounce a human player; it can very quickly find more words than any human knows, and it can very quickly and perfectly check them against the board. The trick is to program the machine to do that. Your program must also do this recursively. Part of the problem is to have a good word list to start with (the file "dict.txt" contains such a list and is provided in the same folder as the Java files, though you may want to test on a shorter list to start). The hard part is implementing algorithms to make the computer's search work efficiently. It is easy to get lost in the recursive decomposition and you should think carefully about how to proceed. Bear in mind that you can significantly speed up this algorithm using "pruning" by recognizing when you are going down a dead end, abandoning it, and backtracking to where there are still live alternatives. For example, if you have built a path starting with "zx", you could use an isPrefix function implemented on your lexicon to determine that there are no words in English beginning with that prefix. If you miss this optimization, you may find yourself taking long coffee breaks while the computer is busy checking out non-existent words like "zxgub" and "zxaep", etc. Alternatively, if you have traversed your lexicon starting with "ab" and your game board representation is able to tell you there are no paths on the board starting with those letters, you know you don't have to even consider any lexicon words that have "ab" as prefix. Please note: one implementation of getAllValidWords is to simply run through the dictionary and repeatedly call isOnBoard. This is not a valid solution for this lab (you will get 0 points). Your solution should follow the strategy outlined above. The file BoggleGUI.java is a GUI application that you can use as a nice interface when testing some things about your Boggle player. Note however that your BogglePlayer class must have the required functionality without the BoggleGUI code present. Also keep in mind that BoggleGUI only knows about 4x4 Boggle boards, and official Boggle dice, while your BogglePlayer needs to be able to handle any square board, and any Strings on the die faces. The file BoggleGUI.jar is a partially correct solution to the assignment. (It has bugs, and as a player, it it violates the spec by never finding words shorter than 5 or longer than 8 letters, but it lets you find them -- just to give you a fighting chance.) The BoggleGUI program comes with a main method that will set up the game. You can also pass this method parameters when you run it to set a word file and various game parameters such as minimum word length. Be careful to keep your word file in the same folder as your project because that is where BlueJ will look for it. Speed matters! Don't forget that the BoggleGUI is only one possible implementation of a Boggle-style game. Your code must be general. E.g. a "B" program would be able to handle standard boggle dice, but an "A" program should be able to handle any dice. Along those lines, I will use my own program to test your program, so if you alter the BoggleGUI it won't be used when I'm testing your program. Be aware that where your program runs will make a significant difference in the speed of the program. If you run off the network, for example, your program might take several minutes to run where it might only take a second or two if run off the desktop. If you test your program in the early stages without using a small lexicon (e.g. 10 words) and a custom board then you are in for trouble. This is also a good time to use BlueJ's ability to run methods individually effectively. It is virtually essential that you master the debugger on this project. The second part in particular can get very complicated. Once you have your program up and running I will expect that anytime you ask me a question you'll already have the debugger up and will just want me to clarify something or provide a suggestion. Handing in: As always this is due at the start of the next lab and you should hand in a hard copy in addition to emailing me the java file. Email me only BogglePlayer.java, as an attachment; do not zip or tar. On the hard-copy sign that you followed the honor code for the class. Last modified: Mon Nov 2 15:04:01 EST 2009