Location: PHPKode > scripts > floCrossword > flocrossword/floCrossword.php
<?
/***************************************************************************
 *  Original floCrossword copyright (C) 2006 by Joshua Hatfield.           *
 *                                                                         *
 *  In order to use any part of this floCrossword Class, you must comply   *
 *  with the license in 'license.doc'.  In particular, you may not remove  *
 *  this copyright notice.                                                 *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************

 - Product name: floCrossword
 - Author: Joshua Hatfield (hide@address.com)
 - Release Version: 0.1
 - Release Date: 2005-07-22
Documentation
THIS PUZZLE IS FOR INTERNET EXPLORER ONLY.  SORRY.  I haven't had time to edit
it for multiple browsers.

The main functions used in this class are:
$this->add_word($x, $y, $word, $isAcross = false, $validate = false)
* This adds a word to the crossword puzzle.  $x and $y specify the start of the
* word.  $isAcross is a boolean value where false designates this word as down
* and true designates it as across.  $validate is an experimental feature which
* tries to make sure your word does not conflict with any other words already
* placed.
$this->create_map($solved = false, $min_x = NULL, $max_x = NULL, $min_y = NULL, $max_y = NULL);
* This creates the javascript operated puzzle into your page.  Setting $solved
* to true will cause the puzzle to display the solved puzzle in the output.
* $min_x, $max_x, $min_y and $max_y specify the outsize boundry when creating
* the puzzle.  These values are relative to the values used in $x and $y in the
* function above.
$this->set_map($new_map)
* This takes a map generated by $this->get_map() and uses it as the current map.
$this->get_map()
* This generates a map of the current puzzle in a serialize array.
$this->clear_map()
* This sets the current puzzle map to empty.
$this->reverse_axes()
* This reverses the axis of the map.  Essentially, it switches the X and Y
* coordinates of all the entries and toggles the $isAcross value.
*/
class floCrossword {
	var $puzzle_map = array();
	var $error = NULL;
	function set_map($new_map) {
		$this->puzzle_map = unserialize($new_map);
	}
	function get_map() {
		return serialize($this->puzzle_map);
	}
	function clear_map() {
		$this->puzzle_map = array();
		return true;
	}
	function add_word($x, $y, $word, $isAcross = false, $validate = true) {
		$word_new = array(
			"x" => $x,
			"y" => $y,
			"word" => strtoupper($word),
			"across" => $isAcross
		);
		if ($validate) {
			// This check for proximity and will not let any words get too close to each other.
			// You can turn $validate off (false) and it will put the new word without validation.
			// Validation is pretty strict so if you have a close knit puzzle you might have to
			// turn it off for some words to go in.
			foreach ($this->puzzle_map as $word) {
				if ($word["across"] !== $word_new["across"]) {
					if ($word_new["across"]) {
						// Capture intersect letters
						if ($word["y"] - $word_new["y"] >= 0 && $word_new["x"] - $word["x"] >= 0) {
							$word_intersect = substr($word["word"], $word_new["x"] - $word["x"], 1);
							$word_new_intersect = substr($word_new["word"], $word["y"] - $word_new["y"], 1);
						}
//						echo("down: $word_intersect => $word_new_intersect?<br>");
						// Check for proyimitx
						if ($word_new["y"] - $word["y"] + strlen($word_new["word"]) > 0 &&
							$word_new["y"] <= $word["y"] &&
							(
								// End of word
								$word_new["x"] == $word["x"] + strlen($word["word"]) ||
								// Beginning of word
								$word_new["x"] == $word["x"] - 1
							)
						) {
							$this->error = "Proyimitx error: New down word too close to across word.";
							return false;
						}
						if ($word["x"] - $word_new["x"] + strlen($word["word"]) > 0 &&
							$word["x"] <= $word_new["x"] &&
							(
								// End of word
								$word["y"] == $word_new["y"] + strlen($word_new["word"]) ||
								// Beginning of word
								$word["y"] == $word_new["y"] - 1
							)
						) {
							$this->error = "Proyimitx error: New down word too close to across word.";
							return false;
						}
					} else {
						// Capture intersect letters
						if ($word["x"] - $word_new["x"] >= 0 && $word_new["y"] - $word["y"] >= 0) {
							$word_intersect = substr($word["word"], $word_new["y"] - $word["y"], 1);
							$word_new_intersect = substr($word_new["word"], $word["x"] - $word_new["x"], 1);
						}
//						echo("down: $word_intersect => $word_new_intersect?<br>");
						// Check for proximity
						if ($word_new["x"] - $word["x"] + strlen($word_new["word"]) > 0 &&
							$word_new["x"] <= $word["x"] &&
							(
								// End of word
								$word_new["y"] == $word["y"] + strlen($word["word"]) ||
								// Beginning of word
								$word_new["y"] == $word["y"] - 1
							)
						) {
							$this->error = "Proximity error: New down word too close to across word.";
							return false;
						}
						if ($word["y"] - $word_new["y"] + strlen($word["word"]) > 0 &&
							$word["y"] <= $word_new["y"] &&
							(
								// End of word
								$word["x"] == $word_new["x"] + strlen($word_new["word"]) ||
								// Beginning of word
								$word["x"] == $word_new["x"] - 1
							)
						) {
							$this->error = "Proximity error: New down word too close to across word.";
							return false;
						}
					}
					if ($word_intersect && $word_new_intersect && $word_intersect != $word_new_intersect) {
						$this->error = "Intersection must occur on same character.";
						return false;
					}
				} else {
					// Just some old debugging info.
/*					echo("compare: ".$word["word"]." => ".$word_new["word"]."<br>");
					echo("ow - X: ".$word["x"]." Y: ".$word["y"]." L: ".strlen($word["word"])."".($word["y"] - $word_new["y"] + strlen($word_new["word"]))."<br>");
					echo("nw - X: ".$word_new["x"]." Y: ".$word_new["y"]." L: ".strlen($word_new["word"])."<br>");*/
					if ($word["x"] == $word_new["x"] && $word["y"] == $word_new["y"]) {
						$this->error = "Two words cannot start at the same square in the same direction.";
						return false;
					}
					if ($word_new["across"]) {
						if (abs($word["x"] - $word_new["x"]) < 2 &&
							$word_new["y"] - $word["y"] + strlen($word_new["word"]) > 0 &&
							$word["y"] - $word_new["y"] + strlen($word["word"]) > 0
						) {
							$this->error = "Proximity error: Parallel words too close or overlapping.";
							return false;
						}
						if ($word["x"] - $word_new["x"] == 0 &&
							$word_new["y"] - $word["y"] + strlen($word_new["word"]) > -1 &&
							$word["y"] - $word_new["y"] + strlen($word["word"]) > -1
						) {
							$this->error = "Proximity error: Parallel words too close to each others end.";
							return false;
						}
					} else {
					}
				}
			}
		}
		$this->puzzle_map[] = $word_new;
		return true;
	}
	function create_map($solved = false, $min_x = NULL, $max_x = NULL, $min_y = NULL, $max_y = NULL) {
		foreach ($this->puzzle_map as $word) {
			if ($min_x === NULL || $word["x"] < $min_x) {
				$min_x = $word["x"];
			}
			if ($min_y === NULL || $word["y"] < $min_y) {
				$min_y = $word["y"];
			}
			$x_end = $word["across"]?$word["x"]:($word["x"] + strlen($word["word"]) - 1);
			if ($max_x === NULL || $x_end > $max_x) {
				$max_x = $x_end;
			}
			$y_end = $word["across"]?($word["y"] + strlen($word["word"]) - 1):$word["y"];
			if ($max_y === NULL || $y_end > $max_y) {
				$max_y = $y_end;
			}
		}
//		echo("x => $min_x - $max_x<br>");
//		echo("y => $min_y - $max_y<br>");
		$grid = array();
		for ($x = $min_x; $x <= $max_x; $x++) {
			$grid[$x] = array();
			for ($y = $min_y; $y <= $max_y; $y++) {
				$grid[$x][$y] = ".";
			}
		}
		$grid_letters = $grid;
		$grid_numbers = $grid;
		$answers = "";
		$position = "";
		foreach ($this->puzzle_map as $word) {
			for ($pos = 0; $pos < strlen($word["word"]); $pos++ ) {
				if ($word["across"]) {
					$x = $word["x"];
					$y = $word["y"] + $pos;
				} else {
					$x = $word["x"] + $pos;
					$y = $word["y"];
				}
				$grid_letters[$x][$y] = substr($word["word"], $pos, 1);
				$grid_numbers[$x][$y] = "-";
			}
		}
		foreach ($this->puzzle_map as $word) {
			$grid_numbers[$word["x"]][$word["y"]] = "#";
		}
		$answers = "";
		$position = "";
		for ($x = $min_x; $x <= $max_x; $x++) {
			for ($y = $min_y; $y <= $max_y; $y++) {
				$answers .= $grid_letters[$x][$y];
				$position .= $grid_numbers[$x][$y];
			}
			if ($x < $max_x) {
				$answers .= "|";
				$position .= "|";
			}
		}
		?>
		<form name="crossword">
		<script>
		<!--
		strAnswers  = "<?=$answers?>";
		strPosition = "<?=$position?>";
		document.write('<table style="width: <?=($max_y - $min_y + 1) * 25 + ($max_y - $min_y) + 2?>px; height: <?=($max_x - $min_x + 1) * 25 + ($max_x - $min_x) + 2?>px; border-collapse: collapse" border=1 cellpadding=0 cellspacing=0 bordercolor="#000000"><tr>');
		s = 0;
		x = 0;
		y = 1;
		currentx = 0;
		currenty = 0;
		for (t = 0; t < strAnswers.length; t++) {
			x = x + 1;
			if (strAnswers.charAt(t) == ".") {
				document.write('<td style="width: 25px; height: 25px" bgcolor="#000000"></td>')
			} else if (strAnswers.charAt(t) == "|") {
				y = y + 1;
				x = 0;
				document.write('</tr><tr>');
			} else {
				if (currentx == 0 || currenty == 0) {
					currentx = x;
					currenty = y;
				}
				if (strPosition.charAt(t) != "." && strPosition.charAt(t) != "-") {
					s = s + 1;
					document.write('<td onClick="movecursor(' + x + ',' + y + ');" style="width: 25px; height: 25px" bgcolor="#ffffff" valign="top"><div style="position: absolute"><span style="font-family: Arial, Helvetica, sans-serif; font-size: 6pt">' + s + '</font></div><div id="cell_' + y + '_' + x + '" width="100%" height="100%"><table border=0 cellpadding=0 cellspacing=0 width=100% height=100% bgcolor=white><tr><td width=25 height=25 align=center><font face="Arial, Helvetica, sans-serif" size="+1"><b><?=$solved?"'+strAnswers.charAt(t)+'":"&nbsp;&nbsp;&nbsp;"?></b></font></td></tr></table></div></td>');
				} else {
					document.write('<td onClick="movecursor(' + x + ',' + y + ');" id="cell_' + y + '_' + x + '" style="width: 25px; height: 25px"><table border=0 cellpadding=0 cellspacing=0 width=100% height=100% bgcolor=white><tr><td width=25 height=25 align=center><font face="Arial, Helvetica, sans-serif" size="+1"><b><?=$solved?"'+strAnswers.charAt(t)+'":"&nbsp;&nbsp;&nbsp;"?></b></font></td></tr></table></td>');
				}
				document.write('<input type="hidden" name="val_' + y + '_' + x + '" value="<?=$solved?"'+strAnswers.charAt(t)+'":"&nbsp;&nbsp;&nbsp;"?>">');
			}
		}
		totalx = x;
		totaly = y;
		strAnswers = strAnswers.replace(/\|/g, "")
		lastdir = "-";
		document.write('</tr></table>');
		function findvalue(x, y) {
			if (document.crossword["val_" + y + "_" + x].value == "") {
				return "&nbsp;&nbsp;&nbsp;"
			} else {
				return document.crossword["val_" + y + "_" + x].value;
			}
		}
		function findanswer(x, y) {
			return strAnswers.charAt(((y - 1) * totalx) + x - 1);
		}
		function movecursor(x,y) {
			if (!checkvalue(currentx, currenty)) {
				fontcolor = " color=red"
			} else {
				fontcolor = " color=black"
			}
			eval("cell_" + currenty + "_" + currentx).innerHTML = "<table border=0 cellpadding=0 cellspacing=0 width=100% height=100% bgcolor=white><tr><td width=25 height=25 align=center><font face='Arial, Helvetica, sans-serif' size=+1 " + fontcolor + "><b>" + findvalue(currentx, currenty) + "</b></font></td></tr></table>";
			currentx = x;
			currenty = y;
			if (!checkvalue(currentx, currenty)) {
				fontcolor = " color=red"
			} else {
				fontcolor = " color=black"
			}
			eval("cell_" + currenty + "_" + currentx).innerHTML = "<table border=0 cellpadding=0 cellspacing=0 width=100% height=100% bgcolor=pink><tr><td width=25 height=25 align=center><font face='Arial, Helvetica, sans-serif' size=+1 " + fontcolor + "><b><u>" + findvalue(currentx, currenty) + "</u></b></font></td></tr></table>";
		}
		function onKeyDown () {
			var keycode;
			if (window.event) keycode = window.event.keyCode;
			else if (e) keycode = e.which;
			else return true;
			if (keycode >= 97 && keycode <= 122) { // CAPITALIZE
				keycode = keycode - 32;
			}
			if (keycode >= 65 && keycode <= 90) {
				keycode = String.fromCharCode(keycode)
				document.crossword["val_" + currenty + "_" + currentx].value = keycode;
				movecursor(currentx,currenty);
				if (lastdir == "-") {
					if (currentx + 1 <= totalx && findanswer(currentx + 1, currenty) != ".") {
						keycode = 39;
					} else if (currenty + 1 <= totaly && findanswer(currentx, currenty + 1) != ".") {
						keycode = 40;
					}
				} else {
					if (currenty + 1 <= totaly && findanswer(currentx, currenty + 1) != ".") {
						keycode = 40;
					} else if (currentx + 1 <= totalx && findanswer(currentx + 1, currenty) != ".") {
						keycode = 39;
					}
				}
			}
			if (keycode == 8) { // Backspace
				document.crossword["val_" + currenty + "_" + currentx].value = "";
				if (lastdir == "-") {
					if (currentx - 1 > 0 && findanswer(currentx - 1, currenty) != ".") {
						keycode = 37;
					} else {
						keycode = 46;
					}
				} else {
					if (currenty - 1 > 0 && findanswer(currentx, currenty - 1) != ".") {
						keycode = 38;
					} else {
						keycode = 46;
					}
				}
			}
			if (keycode == 46) { // Delete is 46??? OOOOkkaaaayyy
				document.crossword["val_" + currenty + "_" + currentx].value = "";
				movecursor(currentx,currenty);
			}
			if (keycode == 38) { // UP
				x = currentx;
				y = currenty;
				y = y - 1;
				if (y < 1)	{
					y = y + totaly;
				}
				while (findanswer(x, y) == ".") {
					y = y - 1;
					if (y < 1)	{
						y = y + totaly;
					}
				}
				movecursor(x,y);
				lastdir = "|";
			} else if (keycode == 40) { // DOWN
				x = currentx;
				y = currenty;
				y = y + 1;
				if (y > totaly)	{
					y = y - totaly;
				}
				while (findanswer(x, y) == ".") {
					y = y + 1;
					if (y > totaly)	{
						y = y - totaly;
					}
				}
				movecursor(x,y);
				lastdir = "|";
			} else if (keycode == 37) { // LEFT
				x = currentx;
				y = currenty;
				x = x - 1;
				if (x < 1)	{
					x = x + totalx;
				}
				while (findanswer(x, y) == ".") {
					x = x - 1;
					if (x < 1)	{
						x = x + totalx;
					}
				}
				movecursor(x,y);
				lastdir = "-";
			} else if (keycode == 39) { // RIGHT
				x = currentx;
				y = currenty;
				x = x + 1;
				if (x > totalx)	{
					x = x - totalx;
				}
				while (findanswer(x, y) == ".") {
					x = x + 1;
					if (x > totalx)	{
						x = x - totalx;
					}
				}
				movecursor(x,y);
				lastdir = "-";
			}
			return false
		}
		function checkvalue(x, y) {
			if (findvalue(x, y) == findanswer(x, y) || findvalue(x, y) == "") {
				return true;
			} else {
				return false
			}
		}
		document.onkeydown = onKeyDown;
		movecursor(currentx, currenty);
		// -->
		</script>
		<?
	}
	function reverse_axes() {
		// Ha ha, this is funny.  I wrote the blasted thing with the wrong x and y axis...so I had to write a function to switch them.
		$new_map = array();
		foreach ($this->puzzle_map as $map_word) {
			$tmp_word = array();
			$tmp_word["word"] = $map_word["word"];
			$tmp_word["x"] = $map_word["y"];
			$tmp_word["y"] = $map_word["x"];
			$tmp_word["across"] = $map_word["across"]?false:true;
			$new_map[] = $tmp_word;
		}
		$this->puzzle_map = $new_map;
	}
}
?>
Return current item: floCrossword