<?
/***************************************************************************
* 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)+'":" "?></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)+'":" "?></b></font></td></tr></table></td>');
}
document.write('<input type="hidden" name="val_' + y + '_' + x + '" value="<?=$solved?"'+strAnswers.charAt(t)+'":" "?>">');
}
}
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 " "
} 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;
}
}
?>