488 lines
15 KiB
PHP
488 lines
15 KiB
PHP
<?php
|
|
|
|
/*
|
|
* pgn4web javascript chessboard
|
|
* copyright (C) 2009-2013 Paolo Casaschi
|
|
* see README file and http://pgn4web.casaschi.net
|
|
* for credits, license and more details
|
|
*/
|
|
|
|
error_reporting(E_ALL | E_STRICT);
|
|
|
|
// add temporarily blocked sites here
|
|
$blockedReferrers = array();
|
|
|
|
|
|
$referrerHost = isset($_SERVER['HTTP_REFERER']) ? parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) : "";
|
|
if ($referrerHost) {
|
|
foreach ($blockedReferrers as $blockedReferrer) {
|
|
if (strstr($referrerHost, $blockedReferrer)) {
|
|
$thisPage = curPageURL();
|
|
$thisPage .= ((strstr($thisPage, "?") ? "&" : "?") . "selfReferred=true");
|
|
print <<<END
|
|
<!DOCTYPE HTML>
|
|
<html>
|
|
<head>
|
|
</head>
|
|
<body style="padding:10px; font-size:x-small; font-family:sans-serif;">
|
|
<p style="font-weight:bold;">pgn4web chess puzzler: warning</p><p>Your site generates a substantial load on the pgn4web chess puzzler server. Please install the pgn4web chess puzzler on your own server following these <a href="http://code.google.com/p/pgn4web/wiki/ServiceAvailability#Use_on_a_professional_site" target="_blank">instructions</a>. Sorry for any inconvenience, previous attempts contacting your site's administrators were unsuccessful.</p><p>Click <a href="$thisPage">here</a> to view the pgn4web chess puzzler.</p>
|
|
</body>
|
|
</html>
|
|
END;
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
$debugInfo = "\n";
|
|
|
|
function get_param($param, $shortParam, $default) {
|
|
if (isset($_REQUEST[$param])) { return $_REQUEST[$param]; }
|
|
if (isset($_REQUEST[$shortParam])) { return $_REQUEST[$shortParam]; }
|
|
return $default;
|
|
}
|
|
|
|
|
|
$pgnData = get_param("pgnData", "pd", "tactics.pgn");
|
|
|
|
function get_pgnText($pgnUrl) {
|
|
if (strpos($pgnUrl, ":") || (strpos($pgnUrl, "%3A"))) { return "[Event \"error: invalid pgnData parameter\"]\n"; }
|
|
$fileLimitBytes = 10000000; // 10Mb
|
|
$pgnText = file_get_contents($pgnUrl, NULL, NULL, 0, $fileLimitBytes + 1);
|
|
if (!$pgnText) { return "[Event \"error: failed to get pgnData content\"]\n"; }
|
|
$pgnText = str_replace(array("&", "<", ">"), array("&", "<", ">"), $pgnText);
|
|
return $pgnText;
|
|
}
|
|
|
|
$pgnText = get_pgnText($pgnData);
|
|
|
|
// for simplicity, remove all comments from the game text
|
|
// to avoid spurious [ in comments breaking the regular expression
|
|
// splitting the PGN data into games
|
|
$pgnText = preg_replace("/{[^}]*}/", "", $pgnText);
|
|
$pgnText = preg_replace("/;[^\n$]*/", "", $pgnText);
|
|
$pgnText = preg_replace("/(\n|^)%[^\n$]*/", "", $pgnText);
|
|
|
|
$numGames = preg_match_all("/(\s*\[\s*(\w+)\s*\"([^\"]*)\"\s*\]\s*)+[^\[]*/", $pgnText, $games );
|
|
|
|
|
|
$gameNum = get_param("gameNum", "gn", "");
|
|
|
|
$expiresDate = "";
|
|
if ($gameNum == "random") { $gameNum = rand(1, $numGames); }
|
|
else if (!preg_match("/^\d+$/", $gameNum)) {
|
|
$timeNow = time();
|
|
$expiresDate = gmdate("D, d M Y H:i:s", (floor($timeNow / (60 * 60 * 24)) + 1) * (60 * 60 * 24)) . " GMT";
|
|
if (!preg_match("/^[ +-]\d+$/", $gameNum)) { $gameNum = 0; } // space is needed since + is urldecoded as space
|
|
$gameNum = floor(($gameNum + ($timeNow / (60 * 60 * 24))) % $numGames) + 1;
|
|
}
|
|
else if ($gameNum < 1) { $gameNum = 1; }
|
|
else if ($gameNum > $numGames) { $gameNum = $numGames; }
|
|
$debugInfo .= "#" . ($gameNum ^ $numGames) . "." . $numGames . "\n";
|
|
$gameNum -= 1;
|
|
|
|
$pgnGame = $games[0][$gameNum];
|
|
|
|
|
|
$lightColorHex = get_param("lightColorHex", "lch", "EFF4EC"); // FFCC99
|
|
$lightColorHexCss = "#" . $lightColorHex;
|
|
$darkColorHex = get_param("darkColorHex", "dch", "C6CEC3"); // CC9966
|
|
$darkColorHexCss = "#" . $darkColorHex;
|
|
|
|
$controlBackgroundColorHex = get_param("controlBackgroundColorHex", "cbch", "EFF4EC"); // FFCC99
|
|
$controlBackgroundColorHexCss = "#" . $controlBackgroundColorHex;
|
|
$controlTextColorHex = get_param("controlTextColorHex", "ctch", "888888"); // 663300
|
|
$controlTextColorHexCss = "#" . $controlTextColorHex;
|
|
|
|
|
|
$squareSize = get_param("squareSize", "ss", "30");
|
|
$squareSizeCss = $squareSize . "px";
|
|
if ($squareSize < 20) { $squareSize = 20; }
|
|
if ($squareSize < 30) {
|
|
$borderStyleCss = "none";
|
|
$highlightBorderStyleCss = "none";
|
|
$borderSize = 0;
|
|
$borderSizeCss = $borderSize;
|
|
} else {
|
|
$borderStyleCss = "solid";
|
|
$highlightBorderStyleCss = "inset";
|
|
$borderSize = ceil($squareSize / 50);
|
|
$borderSizeCss = $borderSize . "px";
|
|
}
|
|
$bareSquareSize = $squareSize - 2 * $borderSize;
|
|
$bareSquareSizeCss = $bareSquareSize . "px";
|
|
|
|
function defaultPieceSize($ss) {
|
|
$pieceSizeOptions = array(20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 52, 56, 60, 64, 72, 80, 88, 96, 112, 128, 144, 300);
|
|
$targetPieceSize = floor(0.8 * $ss);
|
|
for ($ii=count($pieceSizeOptions)-1; $ii>=0; $ii--) {
|
|
if ($pieceSizeOptions[$ii] <= $targetPieceSize) { return $pieceSizeOptions[$ii]; }
|
|
}
|
|
return $pieceSizeOptions[0];
|
|
}
|
|
$pieceSize = defaultPieceSize($squareSize - 2 * $borderSize);
|
|
$pieceSizeCss = $pieceSize . "px";
|
|
|
|
|
|
$pieceFont = get_param("pieceFont", "pf", "default");
|
|
if ($pieceFont == "a") { $pieceFont = "alpha"; }
|
|
if ($pieceFont == "m") { $pieceFont = "merida"; }
|
|
if ($pieceFont == "u") { $pieceFont = "uscf"; }
|
|
if (($pieceFont == "random") || ($pieceFont == "r")) {
|
|
$randomPiece = rand(0, 2);
|
|
switch ($randomPiece) {
|
|
case 1: $pieceFont = "alpha"; break;
|
|
case 2: $pieceFont = "merida"; break;
|
|
default: $pieceFont = "uscf"; break;
|
|
}
|
|
}
|
|
if (($pieceFont == "hash") || ($pieceFont == "h")) {
|
|
// $hashPiece = strlen($pgnGame) % 3;
|
|
$hashPiece = $gameNum % 3;
|
|
switch ($hashPiece) {
|
|
case 1: $pieceFont = "alpha"; break;
|
|
case 2: $pieceFont = "merida"; break;
|
|
default: $pieceFont = "uscf"; break;
|
|
}
|
|
}
|
|
if (($pieceFont == "default") || ($pieceFont == "d")) {
|
|
if ($pieceSize < 28) { $pieceFont = "uscf"; }
|
|
else {
|
|
if ($pieceSize > 39) { $pieceFont = "merida"; }
|
|
else { $pieceFont = "alpha"; }
|
|
}
|
|
}
|
|
|
|
|
|
$boardSize = $squareSize * 8;
|
|
$boardSizeCss = $boardSize . "px";
|
|
|
|
|
|
$buttonHeight = $squareSize;
|
|
$buttonHeightCss = $buttonHeight . "px";
|
|
$buttonWidth = $squareSize * 4;
|
|
$buttonWidthCss = $buttonWidth . "px";
|
|
$buttonFontSize = floor($squareSize / 2.5);
|
|
if ($buttonFontSize < 10) { $buttonFontSize = 10; }
|
|
$buttonFontSizeCss = $buttonFontSize . "px";
|
|
$buttonPadding = floor($squareSize / 10);
|
|
|
|
|
|
$sidetomoveBorder = floor($buttonFontSize / 18) + 1;
|
|
$sidetomoveBorderCss = $sidetomoveBorder . "px";
|
|
$sidetomoveHeight = floor(0.8 * $buttonFontSize - 2 * $sidetomoveBorder);
|
|
$sidetomoveHeightCss = $sidetomoveHeight . "px";
|
|
$sidetomoveWidth = $sidetomoveHeight;
|
|
$sidetomoveWidthCss = $sidetomoveWidth . "px";
|
|
|
|
|
|
$frameBorderColorHex = get_param("frameBorderColorHex", "fbch", "C6CEC3");
|
|
if ($frameBorderColorHex == "none") {
|
|
$frameBorderStyleCss = "none";
|
|
$frameBorderWidth = 0;
|
|
$frameBorderWidthCss = "0";
|
|
$frameBorderColorHex = "000000";
|
|
} else {
|
|
$frameBorderStyleCss = "outset";
|
|
$frameBorderWidth = ceil($squareSize / 50);
|
|
$frameBorderWidthCss = $frameBorderWidth . "px";
|
|
}
|
|
$frameBorderColorHexCss = "#" . $frameBorderColorHex;
|
|
|
|
$frameWidth = $boardSize;
|
|
$frameWidthCss = $frameWidth . "px";
|
|
$frameHeight = $boardSize + $buttonHeight;
|
|
$frameHeightCss = $frameHeight . "px";
|
|
|
|
|
|
// undocumented features
|
|
|
|
$backgroundColorHex = get_param("backgroundColorHex", "bch", "transparent");
|
|
if (preg_match("/^[0123456789ABCDEF]{6}$/i", $backgroundColorHex)) {
|
|
$backgroundColorHexCss = "#" . $backgroundColorHex;
|
|
} else {
|
|
$backgroundColorHexCss = $backgroundColorHex;
|
|
}
|
|
|
|
$framePadding = get_param("framePadding", "fp", 0);
|
|
if ($framePadding != 0) {
|
|
$framePaddingCss = $framePadding . "px";
|
|
} else {
|
|
$framePaddingCss = $framePadding;
|
|
}
|
|
|
|
$rawGame = "";
|
|
|
|
$fenString = get_param("fenString", "fs", "");
|
|
if (($fenString == "true") || ($fenString == "t")) {
|
|
if (preg_match('/\[\s*FEN\s*"([^"]*)"\s*\]/', $pgnGame, $matches)) { $rawGame = $matches[1]; }
|
|
}
|
|
|
|
$pgnOnly = get_param("pgnOnly", "po", "");
|
|
if (($pgnOnly == "true") || ($pgnOnly == "t")) {
|
|
$rawGame = $pgnGame;
|
|
}
|
|
|
|
$pgnOnlyMini = get_param("pgnOnlyMini", "pom", "");
|
|
if (($pgnOnlyMini == "true") || ($pgnOnlyMini == "t")) {
|
|
if (preg_match('/\[\s*FEN\s*"[^"]*"\s*\]/', $pgnGame, $matches)) { $rawGame = "[SetUp \"1\"]\n" . $matches[0] . "\n\n"; }
|
|
$rawGame = $rawGame . preg_replace('/\[\s*\w+\s*"[^"]*"\s*\]\s*/', "", $pgnGame);
|
|
}
|
|
|
|
// end of undocumented features
|
|
|
|
|
|
$outerFrameWidth = $frameWidth + 2 * $frameBorderWidth + 2 * $framePadding;
|
|
$outerFrameHeight = $frameHeight + 2 * $frameBorderWidth + 2 * $framePadding;
|
|
|
|
|
|
function curPageURL() {
|
|
if ((isset($_SERVER["SERVER_NAME"])) && (isset($_SERVER["REQUEST_URI"]))) {
|
|
$pageURL = 'http';
|
|
if ((isset($_SERVER["HTTPS"])) && ($_SERVER["HTTPS"] == "on")) { $pageURL .= "s"; }
|
|
$pageURL .= "://";
|
|
if ((isset($_SERVER["SERVER_PORT"])) && ($_SERVER["SERVER_PORT"] != "80")) {
|
|
$pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
|
|
} else {
|
|
$pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
|
|
}
|
|
} else {
|
|
$pageURL = "";
|
|
}
|
|
return $pageURL;
|
|
}
|
|
$thisPage = curPageURL();
|
|
|
|
|
|
$expiresMeta = "";
|
|
if ($expiresDate) {
|
|
header("expires: " . $expiresDate);
|
|
$expiresMeta = "<meta http-equiv=\"expires\" content=\"" . $expiresDate . "\">";
|
|
}
|
|
|
|
if ($rawGame) {
|
|
header("content-type: application/x-chess-pgn");
|
|
header("content-disposition: inline; filename=puzzler.pgn");
|
|
print $rawGame;
|
|
exit;
|
|
}
|
|
|
|
header("content-type: text/html; charset=utf-8");
|
|
|
|
print <<<END
|
|
<!DOCTYPE HTML>
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
$expiresMeta
|
|
|
|
<title>chess puzzler</title>
|
|
|
|
<!-- debug info
|
|
$debugInfo
|
|
end of debug info -->
|
|
|
|
<style type="text/css">
|
|
|
|
html,
|
|
body {
|
|
margin: 0px;
|
|
padding: 0px;
|
|
}
|
|
|
|
body {
|
|
padding: $framePaddingCss;
|
|
background: $backgroundColorHexCss;
|
|
}
|
|
|
|
.container {
|
|
width: $frameWidthCss;
|
|
height: $frameHeightCss;
|
|
border-style: $frameBorderStyleCss;
|
|
border-width: $frameBorderWidthCss;
|
|
border-color: $frameBorderColorHexCss;
|
|
}
|
|
|
|
.boardTable {
|
|
width: $boardSizeCss;
|
|
height: $boardSizeCss;
|
|
border-width: 0px;
|
|
}
|
|
|
|
.pieceImage {
|
|
width: $pieceSizeCss;
|
|
height: $pieceSizeCss;
|
|
}
|
|
|
|
.whiteSquare,
|
|
.blackSquare,
|
|
.highlightWhiteSquare,
|
|
.highlightBlackSquare {
|
|
width: $bareSquareSizeCss;
|
|
height: $bareSquareSizeCss;
|
|
border-style: $borderStyleCss;
|
|
border-width: $borderSizeCss;
|
|
}
|
|
|
|
.whiteSquare,
|
|
.highlightWhiteSquare {
|
|
border-color: $lightColorHexCss;
|
|
background: $lightColorHexCss;
|
|
}
|
|
|
|
.blackSquare,
|
|
.highlightBlackSquare {
|
|
border-color: $darkColorHexCss;
|
|
background: $darkColorHexCss;
|
|
}
|
|
|
|
.highlightWhiteSquare,
|
|
.highlightBlackSquare {
|
|
border-style: $highlightBorderStyleCss;
|
|
}
|
|
|
|
.buttonTable {
|
|
width: $boardSizeCss;
|
|
height: $buttonHeightCss;
|
|
background-color: $controlBackgroundColorHexCss;
|
|
}
|
|
|
|
.buttonCell {
|
|
width: $buttonWidthCss;
|
|
height: $buttonHeightCss;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.buttonCellLink {
|
|
font-family: sans-serif;
|
|
font-size: $buttonFontSizeCss;
|
|
font-weight: 900;
|
|
color: $controlTextColorHexCss;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.sidetomoveBox {
|
|
width: $sidetomoveWidthCss;
|
|
height: $sidetomoveHeightCss;
|
|
border-style: solid;
|
|
border-width: $sidetomoveBorderCss;
|
|
border-color: $controlTextColorHexCss;
|
|
}
|
|
|
|
</style>
|
|
|
|
<link rel="shortcut icon" href="pawn.ico" />
|
|
|
|
<script src="pgn4web.js" type="text/javascript"></script>
|
|
|
|
<script type="text/javascript">
|
|
"use strict";
|
|
|
|
SetImagePath("images/$pieceFont/$pieceSize");
|
|
SetShortcutKeysEnabled(false);
|
|
|
|
function displayPuzzlerHelp() {
|
|
var puzzlerHelp = "pgn4web chess puzzler" + "\\n\\n";
|
|
puzzlerHelp += "- the white or black small square below the chessboard's left side indicates the side to move" + "\\n\\n";
|
|
puzzlerHelp += "- show the puzzler's solution step by step on the chessboard by clicking the > button below the chessboard's right side" + "\\n\\n";
|
|
puzzlerHelp += "- step backwards one move by clicking the < button below the chessboard's left side" + "\\n\\n";
|
|
puzzlerHelp += "click OK for more information, including how to add the chess puzzler to a website or blog";
|
|
if (confirm(puzzlerHelp)) { window.open("http://code.google.com/p/pgn4web/wiki/Example_Puzzler", "_blank"); }
|
|
}
|
|
|
|
boardShortcut("F8", "chess puzzler help", function(t,e){ displayPuzzlerHelp(); });
|
|
clearShortcutSquares("E", "8");
|
|
clearShortcutSquares("BCDEFGH", "7");
|
|
clearShortcutSquares("ABCDEFGH", "23456");
|
|
clearShortcutSquares("BCFG", "1");
|
|
|
|
function solutionSoFar() {
|
|
var sol = "";
|
|
for (var thisPly = StartPly; thisPly < CurrentPly; thisPly++) {
|
|
var moveCount = Math.floor(thisPly/2)+1;
|
|
if ((thisPly % 2 === 0) || (thisPly === StartPly)) {
|
|
sol += (Math.floor(thisPly/2)+1) + ".";
|
|
if (thisPly % 2) { sol += ".."; }
|
|
sol += " ";
|
|
}
|
|
sol += Moves[thisPly] + " ";
|
|
}
|
|
return sol;
|
|
}
|
|
|
|
function customFunctionOnMove() {
|
|
var res, outcome;
|
|
|
|
if (CurrentPly == StartPly) {
|
|
document.getElementById("leftButtonLink").innerHTML = "<img class='sidetomoveBox' style='background-color:" + (CurrentPly % 2 ? "black" : "white" ) + ";' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoEAYAAADcbmQuAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAMAAwADAGp0HVAAAAAlwSFlzAAAASAAAAEgARslrPgAAAAl2cEFnAAAAKAAAACgAo3CU+AAAACNJREFUaN7twQENAAAAwqD3T20PBxQAAAAAAAAAAAAAAAAPBjIoAAFxtd2pAAAAAElFTkSuQmCC'>";
|
|
document.getElementById("leftButton").title = ((CurrentPly % 2) ? "Black" : "White") + " to play: find the best move";
|
|
} else {
|
|
document.getElementById("leftButtonLink").innerHTML = " < ";
|
|
document.getElementById("leftButton").title = "click < to step backwards one move";
|
|
}
|
|
|
|
if (CurrentPly == StartPly+PlyNumber) {
|
|
switch (res = gameResult[currentGame]) {
|
|
case "1-0": outcome = "white wins"; break;
|
|
case "0-1": outcome = "black wins"; break;
|
|
case "1/2-1/2": outcome = "draw"; break;
|
|
default: outcome = "end"; res = "*"; break;
|
|
}
|
|
document.getElementById("rightButtonLink").innerHTML = " " + res + " ";
|
|
document.getElementById("rightButton").title = solutionSoFar() + " ..." + outcome;
|
|
} else {
|
|
document.getElementById("rightButtonLink").innerHTML = " > ";
|
|
if (CurrentPly == StartPly) {
|
|
document.getElementById("rightButton").title = "click > to show the puzzler's solution step by step on the chessboard";
|
|
} else {
|
|
document.getElementById("rightButton").title = solutionSoFar() + " ...click > to continue showing the puzzler's solution step by step on the chessboard";
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
<!-- DeploymentCheck: google analytics code -->
|
|
|
|
<!-- end DeploymentCheck -->
|
|
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<!-- paste your PGN below and make sure you dont specify an external source with SetPgnUrl() -->
|
|
<form style="display: none;"><textarea style="display: none;" id="pgnText">
|
|
|
|
$pgnGame
|
|
|
|
</textarea></form>
|
|
<!-- paste your PGN above and make sure you dont specify an external source with SetPgnUrl() -->
|
|
|
|
<center>
|
|
<div class="container">
|
|
<div id="GameBoard"></div>
|
|
<table class="buttonTable" border="0" cellspacing="0" cellpadding="0">
|
|
<tr>
|
|
<td id="leftButton" title="" class="buttonCell" onClick="javascript:GoToMove(CurrentPly - 1);" align="center" valign="middle">
|
|
<a id="leftButtonLink" class="buttonCellLink" href="javascript:void(0);" onfocus="blur();"></a>
|
|
</td>
|
|
<td id="rightButton" title="" class="buttonCell" onClick="javascript:GoToMove(CurrentPly + 1);" align="center" valign="middle">
|
|
<a id="rightButtonLink" class="buttonCellLink" href="javascript:void(0);" onfocus="blur();"></a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</center>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
END;
|
|
?>
|