// Start time - for testing runtime. $starttime = time()+microtime(); // DEFINITIONS/GLOBAL VARIABLES // Testing, yes or no? if (isset($_GET['testing'])) { define ("TESTING", 1); } // Worksafe, yes or no? if (isset($_GET['worksafe'])) { define ("WORKSAFE", 1); } // Generates random seed of this board $seed = rand(10000, 99999); if (isset($_GET['seed'])) { $seed = $_GET['seed']; if (!is_numeric($seed)) die("Seed must be a number"); } srand($seed); // Which tile distribution to use? // OPTIONS: 1 = 4x4 1980s; 2 = 4x4 1990s; 3 = 5x5 if (isset($_GET['distro'])) { $distro = $_GET['distro']; $okvalues = array(1, 2, 3); if (!in_array($distro, $okvalues)) die("Error, unknown distribution type"); } else { $distro = 1; // Default } // Size of board (numeric value, used in various parts of the program) depending on user's distribution preference if (($distro == 1) || ($distro == 2)) { $size = 4; } elseif ($distro == 3) { $size = 5; } // What letter minimum to use? if (isset($_GET['lettermin'])) { $minimum = $_GET['lettermin']; if (!is_numeric($minimum)) die("Minimum must be a number"); } else { $minimum = 3; // Default } // How long to make the timer? if (isset($_GET['timerl'])) { $timerl = $_GET['timerl']; if (!is_numeric($timerl)) die("Error, timer length must be a number"); } else { $timerl = 180; } // Friend's score, for comparison (ie if someone else sent you the link) if (isset($_GET['fscore'])) { $fscore = $_GET['fscore']; if (!is_numeric($fscore)) die("Error, friend's score must be a number"); } // Make the random board $grid = Board_Maker(); // A one-d version of the random board for use by support/crawl functions $flat_grid = Grid_Flattener(); // A coordinate value version of the grid used in the board crawler. Based on size. $coord_grid = MakeCoordGrid(); // SEND USER TO APPROPRIATE PLACE if (isset($_GET['prefs'])) { // If the prefs have been submitted, post the gameplay pace DisplayGame(); } elseif ((isset($_GET['distro'])) && (!isset($_GET['prefs']))) { // If the distro but not prefs have been submitted, post the 'your friend wants you to play' page WelcomeFriend(); } else { DisplayChoices(); // If nothing has been submitted, display options. } // CHOICES PAGE // DisplayChoices // Prints out the choices you will get if you have not set them yet. // IN: nothing; GLOBAL: all settings; OUT: nothing (performs action, setting distro and minimum letters) function DisplayChoices() { global $seed, $distro, $lettermin, $minimum, $timerl; echo "
"; } // WELCOME FRIEND PAGE // WelcomeFriend // // IN: nothing; GLOBAL: all preferences; OUT: nothing (gives user chance to click ok to play)) function WelcomeFriend() { global $seed, $distro, $lettermin, $minimum, $timerl, $fscore; Page_Format(); Fake_Board(); echo ""; } // GAMEPLAY PAGE // DisplayGame // Displays everything you need to play the game: the board, the input box. // IN: nothing; GLOBAL: all preferences; OUT: nothing (displays stuff on screen) function DisplayGame() { global $seed, $grid, $distro, $lettermin, $minimum, $timerl; Page_Format(); // Set up the timer ?> echo ""; echo "Randomly Shaken Board: "; Board_Printer($grid); if (!isset($_POST['done'])) { echo "
"; } else { // Deal with user input $input = trim($_POST['user_list']); // Get raw input from form (trim trims any opening or trailing whitespace) $pattern = '/[^a-zA-Z\\s,\.;]/'; // Matches anything that's not upper or lowercase letters, space, comma, period, semicolon $input = preg_replace($pattern, "", $input); // Filters input replacing illegal characters with nothing $delimiter = '/[,\.\\s;]+/'; // Delimiter could be any number of comma OR period OR space OR semicolon $input_array = preg_split($delimiter, $input); // Splits filtered input by delimiter Wordlist_Scorer($input_array); } } // PAGE FORMATTING // Page Format // Formats page with CSS // IN: nothing; OUT: nothing function Page_Format() { // HTML stuff echo "\n\nWork safe? (Check this is you'd rather see the game as an unformatted HTML page, plaintext on a white background.)
";
}
// MAKING THE BOARD
// Board_Printer
// Prints each letter in an HTML grid
// IN: 2-d Grid (with rows); OUT: Action (prints board)
function Board_Printer($grid){
echo "
| ".$value." | "; } echo "
| ".$fakeletters[$counter]." | "; $counter++; } echo "
"; echo $item; echo " | "; $decisionarray = Word_Decider($item, $checked_words); $checked_words = $decisionarray[1]; $runningtotal = $runningtotal + $decisionarray[0]; echo " |
Your Score: ".$runningtotal.""; if (isset($fscore)) { echo "
Score to Beat: ".$fscore.""; if ($runningtotal > $fscore) { echo "
Congratulations! Go rub it in your friend's face."; } elseif ($runningtotal == $fscore) { echo "
Tie! You guys are evenly matched."; } else { echo "
Better luck next time."; } } $thisurl = "http://".$_SERVER['HTTP_HOST']."/laura/wordfind/?seed=".$seed."&distro=".$distro."&lettermin=".$minimum."&timerl=".$timerl."&fscore=".$runningtotal; echo "
Want to play this board against a friend? Send them to: ".$thisurl.""; echo "
Alternately, you can play again.";
}
// Word_Decider
// Given a word, outputs customized error messages and stops if any of the letters are not on the board (from an Array_Onboard call), or if the word is not in the dictionary (from a Dictionary_Checker call), or if the letters in the word are nonadjacent (from an Array_Adjacent_Huh call). Outputs a word score from a Score_Up call if the word is on the board.
// IN: string, array of checked words; GLOBAL: minimum; OUT: Nothing or an array of (score, checked words array)
function Word_Decider($input_string, $checked_words){
global $minimum;
$num_letters = strlen($input_string);
// Word Deciding!
if ($num_letters < $minimum) {
echo "The word is not long enough. Words must be at least ".$minimum." letters long.";
}
elseif (in_array($input_string, $checked_words)) {
echo "The word has already been counted.";
}
else {
$input_array = str_split(preg_replace('/QU+/', "Q", strtoupper($input_string))); // A version of the input string which is (1) all upper case, (2) replaced all QU with Q, (3) split into an array.
$wordscore = 0; // Initialize score for word
if (Array_Onboard($input_array) != $input_array) {
echo "The word contains letters not on the board, so the word is illegal.";
}
else {
$stringlower = strtolower($input_string); // Creates a lower case version of input string for use by dictionary
if (Dictionary_Checker($stringlower) == false) {
echo "The word is not in the dictionary, so the word is illegal.";
}
else {
if (Array_Adjacent_Huh($input_array)) {
$checked_words[] = $input_string;
$wordscore = Score_Up($num_letters);
echo "
Your score for this word is ".$wordscore.".";
}
else {
echo "
The letters are not adjacent on the board, so the word is illegal.";
}
}
}
}
return array($wordscore, $checked_words);
}
// Score_Up
// Given a number of letters, returns a score. NOTE: THIS SHOULD EVENTUALLY SCALE UP WITH MINIMUM NUMBER OF LETTERS? MAYBE?
// IN: integer (number of letters in a given word); OUT: integer (score)
function Score_Up($num_letters) {
if ($num_letters <=4) {
$wordscore = 1;
}
elseif ($num_letters == 5) {
$wordscore = 2;
}
elseif ($num_letters == 6) {
$wordscore = 3;
}
elseif ($num_letters == 7) {
$wordscore = 5;
}
elseif ($num_letters == 8) {
$wordscore = 8;
}
elseif ($num_letters >= 9) {
$wordscore = 11;
}
return $wordscore;
}
// CHECK IF LETTER IS ON BOARD
// Array_Onboard
// Returns array if every letter in the array is on the grid. Otherwise, returns the first letter that was not on the grid. Calls Letter_Onboard_Huh.
// IN: array; OUT: array
function Array_Onboard($array){
$new_old_array = null;
foreach ($array as $letter) {
if (Letter_Onboard_Huh($letter)) {
$new_old_array[] = $letter;
}
else {
break;
}
}
return $new_old_array;
}
// Letter_Onboard_Huh
// Tests if a given letter is on the board.
// IN: letter; GLOBAL: flat grid; OUT: boolean
function Letter_Onboard_Huh($letter){
global $flat_grid;
if (in_array ($letter, $flat_grid)){
return true;
}
else {
return false;
}
}
// CHECK IF WORD IS IN DICTIONARY
// Dictionary_Checker
// Returns true if string is in dictionary file.
// IN: string; OUT: true or false
function Dictionary_Checker($string) {
// Open dictionary file
$dictionary_file = "twl.txt";
$fh = fopen($dictionary_file, 'r');
// Starting values of high, mid, low point of dictionary file
$filesize = filesize($dictionary_file);
$lowpoint = 0;
$highpoint = $filesize;
$midpoint = intval(($lowpoint+$highpoint)/2); // Typecast avg as integer
// Zero in on approximate area by halving dictionary 20 times
for ($i = 0; $i < 20; $i++) {
list($position, $result) = Word_Finder($midpoint, $string, $fh); // Get results of Word_Finder and assign them to variables $position, $result
if ($result == 3) { // Just right
if (TESTING == 1) {
echo "Check it out, I randomly flipped to the right word in the dictionary.
";
}
return true;
}
elseif ($result == 2) { // Too high
if (TESTING == 1) {
echo "Too far. Let me flip back a bit.";
}
$highpoint = $midpoint;
}
else { // Too low
if (TESTING == 1) {
echo "Not far enough. Let me flip ahead a bit.";
}
$lowpoint = $midpoint;
}
$midpoint = intval(($lowpoint+$highpoint)/2); // Get new midpoint
}
// If you haven't found the word yet, go back far enough to catch previous word
$lowpoint = $lowpoint - 30;
if ($lowpoint < 0) {
$lowpoint = 0;
}
fseek($fh, $lowpoint); // Go to the place to start looking
if ($lowpoint != 0) {
fgets($fh); // (Except at beginning of dictionary,) get a line so next fgets starts looking at beginning of a line.
}
while(!feof($fh)) { // While not at end of file,
$current_word = trim(fgets($fh));
if (TESTING == 1) {
echo "
Currently checking ".$string." against ".$current_word."... ";
}
if ($current_word == $string) {
if (TESTING == 1) {
echo "I found it in the dictionary!
";
}
return true; // Return true if you hit the word
}
elseif ($current_word > $string) { // Return false if you pass where the word should be
if (TESTING == 1) {
echo "I did not find it in the dictionary.
";
}
return false;
}
}
// Close dictionary file
fclose($fh);
}
// Word_Finder
// Checks a position in a file and determines whether the word is there, higher, or lower.
// IN: position, word, file; OUT: array of position, integer representing too high (1), too low (2), or just right (3)
function Word_Finder($position, $word, $file) {
fseek($file, $position); // Find line at position
fgets($file); // Get the line at the position
$current_line = trim(fgets($file)); // Current line removes extranous whitespace
if (TESTING == 1) {
echo "
Checking ".$word." against ".$current_line.". ";
}
if ($current_line < $word) {
$result = 1;
}
elseif ($current_line > $word) {
$result = 2;
}
else {
$result = 3;
}
return array(ftell($file), $result); // Return current position of file pointer, and result of higher-or-lower test
}
// CHECK IF ALL LETTERS ARE ADJACENT
// Array_Adjacent_Huh
// Looks for a path to the end of the word from each possible starting point.
// (Bypasses the rest of the options as soon as one good path is found.)
// In: Word array; OUT: true or false
function Array_Adjacent_Huh($array) {
$startingletter = array_shift($array); // Pop off the first letter
$startingpoints = Letter_to_Coord($startingletter); // Find all possible coordinates for the first letter
foreach ($startingpoints as $startingpoint) {
$used_letters[] = $startingpoint; // Put starting point in used_letters array.
$used_letters = Pathfinder($startingpoint, $array, $used_letters);
if (!in_array(-1, $used_letters)) { // If used-letters returns with no -1:
Tiny_Gridder($used_letters, "#0000ff");
if (TESTING == 1) {
echo "
Found a complete path. Returning true.";
}
return true;
}
else {
if (TESTING == 1) {
$minusone = array_pop($used_letters);
Tiny_Gridder($used_letters, "#ff0000");
echo "
Could not find a path from that starting point. Trying next... ";
}
unset($used_letters); // Clear used_letters for next iteration.
continue;
}
}
if (TESTING == 1) {
echo "
Nope, could not find a path.";
}
return false;
}
// Pathfinder
// Given a starting coordinate, the rest of a word, and a used-letters array,
// uses Pair_Adjacent to test if the first of the rest of the word is next to the starting coordinate.
// If Pair Adjacent returns -1, adds -1 to the used_letters array and returns it to Array_Adjacent_Huh.
// If Pair Adjacent returns a coordinate, however, adds that coordinate to used_letters,
// then calls itself using that coordinate and the rest of the array
// until it runs out of array, in which case it returns the complete used-letters array.
// In: coordinates of starting point, array of rest of word, array of used letters; OUT: array of used letters
function Pathfinder($firstcoord, $restarray, $used_letters) {
if (count($restarray) < 1) {
if (TESTING == 1) {
echo "
No more pairs to check.";
}
return $used_letters;
}
else {
$firstletter = array_shift($restarray);
$next_coord = Pair_Adjacent($firstcoord, $firstletter, $used_letters);
$used_letters[] = $next_coord;
if ($next_coord == -1) {
if (TESTING == 1) {
echo "
Found a dead end.";
}
return $used_letters;
}
else {
if (TESTING == 1) {
Tiny_Gridder($used_letters, "#00ff00");
echo "
Found a valid pair. Continuing... ";
}
return Pathfinder($next_coord, $restarray, $used_letters);
}
}
}
// Pair_Adjacent
// Given a starting coordinate, a letter, and a used_letters array,
// find the neighbors of the starting coordinate,
// removes any neighbors which have already been used in used_letters,
// and checks the remaining array for a match with the target letter.
// If it finds a match, returns the coordinate of the matching letter.
// Else, returns false.
function Pair_Adjacent($coord, $letter, $used_letters) {
$Neighbors = Neighbor_Lister($coord); // Find the neighbors of the coordinate pair
if (TESTING == 1) {
echo "
Starting Pair_Adjacent. Starting used letters: ";
print_r($used_letters);
$letter_of_coord = Coord_to_Letter($coord);
echo "
Starting centerletter is ".$letter_of_coord." and the neighbors of that are ";
print_r($Neighbors);
}
foreach ($Neighbors as $neighbor) {
$neighborletter = Coord_to_Letter($neighbor);
if (TESTING == 1) {
echo "
Is ".$neighborletter." the same as ".$letter."? ";
}
if ($neighborletter == $letter) {
if (TESTING == 1) {
echo "Yes! Has it been used? ";
}
if (in_array($neighbor, $used_letters)) {
if (TESTING == 1) {
echo "Oh, has already been used. Trying next neighbor...";
}
continue;
}
else {
if (TESTING == 1) {
echo "Nope! Great! Returning the coordinates of this ".$neighborletter.". ";
}
return $neighbor; // Return the coordinate
}
}
else {
if (TESTING == 1) {
echo "No. Trying next neighbor... ";
}
continue;
}
}
if (TESTING == 1) {
echo "Found no neighbor of ".Coord_to_Letter($coord)." which was ".$letter.". ";
}
return -1;
}
// Tiny_Gridder
// Makes a small version of the board with all the letters in a given array printed in a given color.
// IN: array of coordinate pairs, color; GLOBAL: gird; OUT: no value/action (prints board)
function Tiny_Gridder($used_coords, $color) {
global $coord_grid;
global $size;
// Create flat array of either colored or plain html letters associated with the given coords.
foreach ($coord_grid as $coord) {
$assoc_letter = Coord_to_Letter($coord);
$assoc_letter = Qize($assoc_letter);
if (in_array($coord, $used_coords)) {
$all_letters[] = "".$assoc_letter."";
}
else {
$all_letters[] = $assoc_letter;
}
}
$count = 0; // Initiatlize loop counter
// Create table.
echo "
| ".$all_letters[$count]." | "; $count++; } echo "
Run time: ".$totaltime; ?>