Search algorithms are often used to solve puzzles, from 8-tiles to Rubik's Cube to cryptarithmetic.
In this project, we will be using search to “construct” puzzles, specifically, cross word or wordsearch puzzles. Recall, cross-word puzzles have a grid (typically rectangular) in which letters will fill the even-spaced cells, either horizontally (left to right) or vertically (downward). Whenever words intersect, the same letter must be consistent for both words. In cross-word puzzles, players are given semantic clues about each word and must guess among several possibilities for each, exploiting constraints on both length of available space for the word in the grid, and also consistency with words that are intersected. In word-search, every cell in the grid is filled in with some letter, and the words must be identified among what looks mostly like a random sea.
Imagine you are a puzzle maker (i.e. you work for some game company). Your job is to create instances of these types of puzzles. You are given a desired grid size (MxN) and an initial word list.
You do not have to use all the words; but the goal is to fill at least theta=30% of the grid with characters from words on the word list (and words may not be used more than once).
The inputs to the program (from the argument list) should be: M N theta WordListFileName (which will be read from a FILE). The output should be some sort of character-based print-out of the final solution, as illustrated below (or determine that no solution exists).
---------------------------
| c a c t u s |
| p n g |
| a s s u n n y |
| c a a |
| h e a t w a v e c |
| e i a b a |
| s o u |
| s e l f i s h b s |
| a i c t |
| i e a i |
| l d r a m a t i c |
---------------------------
This program must implement an interface Puzzle which contains something similar to the following parts: public interface Puzzle {
void setup(String filename); // set up the parameters for the game, given an input file
boolean makePuzzle(); // creates the puzzle and returns true if it was successfully created
void printPuzzle(); // prints the puzzle to standard output
String listParameters(); // List parameters of puzzle problem in readable format (including
contents of word list)
String toString(); // returns a string version of the Puzzle using \n between lines
}
I do NOT want to force you to get inside my brain – so if you want to set things up a bit differently, that’s fine.
We will do this problem in two parts:
th
Create the driver part of the program. Your program must have the following functionality: a. Read parameters from argument list and throw an exception if insufficient parameters are supplied. Use parameters 12 12 .30 wordlist.txt
b. Read the word list from the supplied file. The file of words is one long string with words separated by “:”. Use the String.split command to pull them apart. c. Implement all methods of the Puzzle interface EXCEPT makePuzzle d. Using standard output, print the parameters and a copy of the created puzzle. e. Write the function tryAt. Test it in any appropriate way. You are free to develop your own syntax, but it may look something like ResultStruct tryAt(String word, int xLoc, int yLoc, isHorizontal) which answers the question of whether word can be placed beginning at grid[xLoc,yLoc] in the direction specified by isHorizontal. It returns a structure (ResultStruct) containing:
a Boolean which is true if the word can be placed at that location in the direction specified
the number of letters in the grid that are REUSED by this word placement
the number of new letters this word placement adds to the grid (obviously the sum of the new and reused letters is the total length of the word)
the xLocation
the YLocation
isHorizontal (true, if the word is placed horizontally, false if it is placed vertically)
the word
This is trickier than it seems. Notice, “cat” can be added to the previous grid as shown below, but
“two” can not be added there because it creates letters pairs (wn and os) which are not part of the wordlist. Similarly “low” cannot be added because of what follows it.
---------------------------
| c a c t u s |
| p w n g |
| a o s s u n n y |
| c a a |
| h e a t w a v e c |
| e i a b a |
| s o u |
| s e l f i s h b s |
| a i c a t |
| i e a i |
| l o w d r a m a t i c |
---------------------------
I found it useful to create a function boolean unused(x,y) which returned true if grid[x][y] is blank or if x or y was off the grid.
Actually produce the puzzle. In order to automate this process, you decide to write a recursive program that considers the each word in your list. Your algorithm either uses the word or doesn’t use the word. Then it considers placing the next word in the list. It either uses the word or doesn’t use the word. This continues until it the desired theta density is reached or until you run out of choices. The first selected word is placed horizontally in the upper left corner, and then uses the depth first search algorithm (regular preorder traversal) to continue making choices. The placement of a word is made by intersections at common characters with words already in the current solution state, obeying constraints on grid boundaries and other intersections), until the criterion of theta reached.
Note: In order to simplify the search time, you are not required to consider all possible placements for all words (recursively). I only considered ANY intelligent placement for a word and not using the word. I did not consider placing the word at location (x1,y1), location (x2,y2), location (x3,y3) and not placing the word. I did try all locations, but only used the “best” location. This greatly reduces the possibilities.
Show the results for wordlist1.txt, wordlist2.txt, and wordlist3.txt. Assume a 12 by 12 grid in each case and a value of .30 for theta. Feel free to play around with other values of the grid size and other values for theta (the grader will).
One thing that I tried is sorting the words in decreasing order by size. This seemed to help (for larger theta values) as the smaller words are easier to place. This is not required, but you may want to try it. It is easy to do, as you already have a sort routine from the lab exercise. I just hardcoded the sort routine to sort Strings in decreasing order by length.
The puzzle maker at http://www.puzzledepot.com/java/czplayer.shtml
is fun to play with.
Hint: I created a routine removeAt(Results) which removed the given word from the location. Note, you do not want to remove a letter that is being used by someone else. As a test, I hardcoded calls to tryAt and removeAt using words (that I knew would succeed/fail) BEFORE I tried to integrate these routines inside the recursive search. Write one thing and test it before adding something else.
So for example (your code WILL BE DIFFERENT), I tested my routines with: public boolean makePuzzle1() {
Results s = tryAt("programs",0,0,true,true);
System.out.println(s);
s = tryAt("grow",0,3,false,true);
System.out.println(s);
s = tryAt("winter",3,3,true,true);
System.out.println(s);
s = tryAt("no",0,1,true,true);
System.out.println(s);
removeAt(new Results(true,"programs",0,0,true,0));
printPuzzle();
return true;
}