Building the Program’s Main Logic The main logic for the program begins by retrieving the word list and puzzle parameters from the user’s form.. Then it tries to convert the list into an
Trang 1Building the Program’s Main Logic
The main logic for the program begins by retrieving the word list and puzzle
parameters from the user’s form Then it tries to convert the list into an array
This type of text analysis is sometimes called parsing
The program then repeatedly tries to build the board until it succeeds Once the
program has successfully created the board, it creates an answer key and adds
the random letters with the addFoils()function Finally, the program prints the
completed puzzle
} else {
//get puzzle data from HTML form
$boardData = array(
width => $width,
height => $height,
name => $name
);
//try to get a word list from user input
if (parseList() = = TRUE){
$legalBoard = FALSE;
//keep trying to build a board until you get a legal result
while ($legalBoard = = FALSE){
clearBoard();
$legalBoard = fillBoard();
} // end while
//make the answer key
$key = $board;
$keyPuzzle = makeBoard($key);
//make the final puzzle
addFoils();
$puzzle = makeBoard($board);
//print out the result page
printPuzzle();
} // end parsed list if
} // end word list exists if
163
l i
Trang 2You should be able to tell the general program flow even if you don’t understand exactly how things happen The main section of a well-defined program should give you a bird’s eye view of the action Most of the details are delegated to functions Most of the remaining chapter is devoted to explaining how these functions work Try to make sure you’ve got the basic gist of the program’s flow; then you see how all of it is done
Parsing the Word List
One important early task involves analyzing the word list that comes from the user The word list comes as one long string separated by newline (\n) characters
other important functions too, including converting each word to uppercase, checking for words that do not fit in the designated puzzle size, and removing unneeded carriage returns
function parseList(){
//gets word list, creates array of words from it
//or return false if impossible
global $word, $wordList, $boardData;
$itWorked = TRUE;
//convert word list entirely to upper case
$wordList = strtoupper($wordList);
//split word list into array
$word = split(“\n”, $wordList);
foreach ($word as $currentWord){
//take out trailing newline characters
$currentWord = rtrim($currentWord);
//stop if any words are too long to fit in puzzle
if ((strLen($currentWord) > $boardData[“width”]) &&
(strLen($currentWord) > $boardData[“height”])){
print “$currentWord is too long for puzzle”;
$itWorked = FALSE;
} // end if } // end foreach
return $itWorked;
} // end parseList
164
g r
s o
l u
g in
e r
Trang 3l i
The first thing I did was use the strtoupper()function to convert the entire word
list into uppercase letters Word search puzzles always seem to use capital letters,
so I decided to convert everything to that format
The long string of characters with newlines is not a useful format here, so I
con-verted the long string into an array called $word The split()function works
per-fectly for this task I split on the string “\n” This is the newline character, so it
should convert each line of the text area into an element of the new $wordarray
The next task was to analyze each word in the array with a foreachloop When I
tested this part of the program, it became clear that sometimes the trailing
new-line character was still there, so I used the rtrim() function to trim off any
unnecessary trailing whitespace
It is impossible to create the puzzle if the user enters a word larger than the
height or width of the puzzle board, so I check for this situation by comparing
the length of each word to the board’s height and width Note that if the word is
too long, I simply set the value of the $itWorkedvariable to FALSE
Earlier in this function, I initialized the value of $itWorkedto TRUE By the time
the function is finished, $itWorked still contains the value TRUEif all the words
were small enough to fit in the puzzle If any of the words were too large, the
value of $itWorkedis FALSEand the program stops
Clearing the Board
(boards which contain all the words in the list) It creates random boards
repeat-edly until it finds one that is legal While this may seem like a wasteful approach,
it is much easier to program than many more sophisticated methods and
pro-duces remarkably good results for simple problems
I N THE R EAL W ORLD Although this program does use a brute force approach to find a good solution,
you see a number of ways the code is optimized to make a good solution more
likely One example of this is the way the program stops if one of the words is
too long to fit in the puzzle This prevents a long processing time while the
pro-gram tries to fit a word in the puzzle when it cannot be done A number of other
places in the code do some work to steer the algorithm toward good solutions
and away from pitfalls Because of these efforts, you find that the program is
actually pretty good at finding word search puzzles unless there are too many
words or the game board is too small.
Trang 4The game board is often re-created several times during one program execution.
I needed a function that could initialize the game board or reset it easily The game board is stored in a two-dimensional array called $board When the board
is “empty,” each cell contains the period (.) character I chose this convention because it gives me something visible in each cell and provides a character that represents an empty cell The clearBoard() function sets or resets the $board array so that every cell contains a period
function clearBoard(){
//initialize board with a in each cell
global $board, $boardData;
for ($row = 0; $row < $boardData[“height”]; $row++){
for ($col = 0; $col < $boardData[“width”]; $col++){
$board[$row][$col] = “.”;
} // end col for loop } // end row for loop
} // end clearBoard
This code is the classic nested forloop so common to two-dimensional arrays Note that I used forloops rather than foreachloops because I was interested in the loop indices The outer forloop steps through the rows Inside each row loop, another loop steps through each column I assigned the value “.”to the $boardarray at the current $rowand $collocations Eventually, “.”is in every cell in the array
I determined the size of the for loops by referring to the $boardData associative array Although I could have done this a number of ways, I chose the associative array for several reasons The most important is clarity It’s easy for me to see by this structure that I’m working with the height and width related to board data Another advantage in this context is convenience Since the height, width, and board name are stored in the $boardData array, I could make a global reference
to the $boardData variable and all its values would come along It’s like having three variables for the price of one.
Filling the Board
Of course, the purpose of clearing the board is to fill it in with the words from the word list This happens in two stages: filling the board, and adding the words
board, but the details of adding each word to the board are relegated to the
T R I C K
166
g r
s o
l u
g in
e r
Trang 5The board is only complete if each word is added correctly Each word is added only
if each of its letters is added without problems The program calls fillBoard()as
often as necessary to get a correct solution Each time fillBoard()runs, it may
call addWord()as many times as necessary until each word is added The addWord()
function in turn keeps track of whether it is able to successfully add each
char-acter to the board
The general fillBoard()function plan is to generate a random direction for each
word and then tell the addWord()function to place the specified word in the
spec-ified direction on the board
The looping structure for the fillBoard()function is a little unique, because the
loop could exit two ways If any of the words cannot be placed in the requested
manner, the puzzle generation stops immediately and the function returns the
value FALSE However, if the entire word list is successfully placed on the game
board, the function should stop looping, but report the value TRUE
You can achieve this effect a number of ways, but I prefer often to use a special
Boolean variable for this purpose Boolean variables are variables meant to contain
only the values TRUEand FALSE Of course, PHP is pretty easygoing about variable
types, but you can make a variable act like a Boolean simply by assigning it only
the values TRUEor FALSE In the fillBoard()function, look at how the $keepGoing
variable is used It is initialized to TRUE, and the function’s main loop keeps
run-ning as long as this is the case
However, the two conditions that can cause the loop to exit—the addWord()
func-tion failed to place a word correctly, or the entire word list has been exhausted—
cause the $keepGoingvariable to become FALSE When this happens, the loop stops
and the function shortly exits
function fillBoard(){
//fill board with list by calling addWord() for each word
//or return false if failed
global $word;
$direction = array(“N”, “S”, “E”, “W”);
$itWorked = TRUE;
$counter = 0;
$keepGoing = TRUE;
while($keepGoing){
$dir = rand(0, 3);
$result = addWord($word[$counter], $direction[$dir]);
if ($result = = FALSE){
167
l i