PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/puzzler.php

http://pgn4web.googlecode.com/
PHP | 491 lines | 467 code | 14 blank | 10 comment | 9 complexity | 408bd2243bf22f237e0a64d5fd926efc MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*
  3. * pgn4web javascript chessboard
  4. * copyright (C) 2009, 2012 Paolo Casaschi
  5. * see README file and http://pgn4web.casaschi.net
  6. * for credits, license and more details
  7. */
  8. error_reporting(E_ERROR | E_PARSE);
  9. // add temporarily blocked sites here
  10. $blockedReferrers = array();
  11. $referrerHost = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
  12. if ($referrerHost) {
  13. foreach ($blockedReferrers as $blockedReferrer) {
  14. if (strstr($referrerHost, $blockedReferrer)) {
  15. $thisPage = curPageURL();
  16. $thisPage .= ((strstr($thisPage, "?") ? "&" : "?") . "selfReferred=true");
  17. print <<<END
  18. <html>
  19. <head>
  20. </head>
  21. <body style="padding:10px; font-size:x-small; font-family:sans-serif;">
  22. <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>
  23. </body>
  24. </html>
  25. END;
  26. exit;
  27. }
  28. }
  29. }
  30. $debugInfo = "\n";
  31. function get_param($param, $shortParam, $default) {
  32. $out = $_REQUEST[$param];
  33. if ($out != "") { return $out; }
  34. $out = $_REQUEST[$shortParam];
  35. if ($out != "") { return $out; }
  36. return $default;
  37. }
  38. $pgnData = get_param("pgnData", "pd", "tactics.pgn");
  39. function get_pgnText($pgnUrl) {
  40. if (strpos($pgnUrl, ":") || (strpos($pgnUrl, "%3A"))) { return "[Event \"error: invalid pgnData parameter\"]\n"; }
  41. $fileLimitBytes = 10000000; // 10Mb
  42. $pgnText = file_get_contents($pgnUrl, NULL, NULL, 0, $fileLimitBytes + 1);
  43. if (!$pgnText) { return "[Event \"error: failed to get pgnData content\"]\n"; }
  44. return $pgnText;
  45. }
  46. $pgnText = get_pgnText($pgnData);
  47. // for simplicity, remove all comments from the game text
  48. // to avoid spurious [ in comments breaking the regular expression
  49. // splitting the PGN data into games
  50. $pgnText = preg_replace("/{[^}]*}/", "", $pgnText);
  51. $pgnText = preg_replace("/;[^\n$]*/", "", $pgnText);
  52. $pgnText = preg_replace("/(\n|^)%[^\n$]*/", "", $pgnText);
  53. $numGames = preg_match_all("/(\s*\[\s*(\w+)\s*\"([^\"]*)\"\s*\]\s*)+[^\[]*/", $pgnText, $games );
  54. $gameNum = get_param("gameNum", "gn", "");
  55. $expiresDate = "";
  56. if ($gameNum == "random") { $gameNum = rand(1, $numGames); }
  57. else if (!preg_match("/^\d+$/", $gameNum)) {
  58. $timeNow = time();
  59. $expiresDate = gmdate("D, d M Y H:i:s", (floor($timeNow / (60 * 60 * 24)) + 1) * (60 * 60 * 24)) . " GMT";
  60. if (!preg_match("/^[ +-]\d+$/", $gameNum)) { $gameNum = 0; } // space is needed since + is urldecoded as space
  61. $gameNum = floor(($gameNum + ($timeNow / (60 * 60 * 24))) % $numGames) + 1;
  62. }
  63. else if ($gameNum < 1) { $gameNum = 1; }
  64. else if ($gameNum > $numGames) { $gameNum = $numGames; }
  65. $debugInfo .= "#" . ($gameNum ^ $numGames) . "." . $numGames . "\n";
  66. $gameNum -= 1;
  67. $pgnGame = $games[0][$gameNum];
  68. $lightColorHex = get_param("lightColorHex", "lch", "EFF4EC"); // FFCC99
  69. $lightColorHexCss = "#" . $lightColorHex;
  70. $darkColorHex = get_param("darkColorHex", "dch", "C6CEC3"); // CC9966
  71. $darkColorHexCss = "#" . $darkColorHex;
  72. $controlBackgroundColorHex = get_param("controlBackgroundColorHex", "cbch", "EFF4EC"); // FFCC99
  73. $controlBackgroundColorHexCss = "#" . $controlBackgroundColorHex;
  74. $controlTextColorHex = get_param("controlTextColorHex", "ctch", "888888"); // 663300
  75. $controlTextColorHexCss = "#" . $controlTextColorHex;
  76. $squareSize = get_param("squareSize", "ss", "30");
  77. $squareSizeCss = $squareSize . "px";
  78. if ($squareSize < 20) { $squareSize = 20; }
  79. if ($squareSize < 30) {
  80. $borderStyleCss = "none";
  81. $highlightBorderStyleCss = "none";
  82. $borderSize = 0;
  83. $borderSizeCss = $borderSize;
  84. } else {
  85. $borderStyleCss = "solid";
  86. $highlightBorderStyleCss = "inset";
  87. $borderSize = ceil($squareSize / 50);
  88. $borderSizeCss = $borderSize . "px";
  89. }
  90. $bareSquareSize = $squareSize - 2 * $borderSize;
  91. $bareSquareSizeCss = $bareSquareSize . "px";
  92. function defaultPieceSize($ss) {
  93. $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);
  94. $targetPieceSize = floor(0.8 * $ss);
  95. for ($ii=count($pieceSizeOptions)-1; $ii>=0; $ii--) {
  96. if ($pieceSizeOptions[$ii] <= $targetPieceSize) { return $pieceSizeOptions[$ii]; }
  97. }
  98. return $pieceSizeOptions[0];
  99. }
  100. $pieceSize = defaultPieceSize($squareSize - 2 * $borderSize);
  101. $pieceSizeCss = $pieceSize . "px";
  102. $pieceFont = get_param("pieceFont", "pf", "default");
  103. if ($pieceFont == "a") { $pieceFont = "alpha"; }
  104. if ($pieceFont == "m") { $pieceFont = "merida"; }
  105. if ($pieceFont == "u") { $pieceFont = "uscf"; }
  106. if (($pieceFont == "random") || ($pieceFont == "r")) {
  107. $randomPiece = rand(0, 2);
  108. switch ($randomPiece) {
  109. case 1: $pieceFont = "alpha"; break;
  110. case 2: $pieceFont = "merida"; break;
  111. default: $pieceFont = "uscf"; break;
  112. }
  113. }
  114. if (($pieceFont == "hash") || ($pieceFont == "h")) {
  115. // $hashPiece = strlen($pgnGame) % 3;
  116. $hashPiece = $gameNum % 3;
  117. switch ($hashPiece) {
  118. case 1: $pieceFont = "alpha"; break;
  119. case 2: $pieceFont = "merida"; break;
  120. default: $pieceFont = "uscf"; break;
  121. }
  122. }
  123. if (($pieceFont == "default") || ($pieceFont == "d")) {
  124. if ($pieceSize < 28) { $pieceFont = "uscf"; }
  125. else {
  126. if ($pieceSize > 39) { $pieceFont = "merida"; }
  127. else { $pieceFont = "alpha"; }
  128. }
  129. }
  130. $boardSize = $squareSize * 8;
  131. $boardSizeCss = $boardSize . "px";
  132. $buttonHeight = $squareSize;
  133. $buttonHeightCss = $buttonHeight . "px";
  134. $buttonWidth = $squareSize * 4;
  135. $buttonWidthCss = $buttonWidth . "px";
  136. $buttonFontSize = floor($squareSize / 2.5);
  137. if ($buttonFontSize < 10) { $buttonFontSize = 10; }
  138. $buttonFontSizeCss = $buttonFontSize . "px";
  139. $buttonPadding = floor($squareSize / 10);
  140. $sidetomoveBorder = floor($buttonFontSize / 18) + 1;
  141. $sidetomoveBorderCss = $sidetomoveBorder . "px";
  142. $sidetomoveHeight = $buttonFontSize - 2 * $sidetomoveBorder;
  143. $sidetomoveHeightCss = $sidetomoveHeight . "px";
  144. $sidetomoveWidth = $sidetomoveHeight;
  145. $sidetomoveWidthCss = $sidetomoveWidth . "px";
  146. $frameBorderColorHex = get_param("frameBorderColorHex", "fbch", "C6CEC3");
  147. if ($frameBorderColorHex == "none") {
  148. $frameBorderStyleCss = "none";
  149. $frameBorderWidth = 0;
  150. $frameBorderWidthCss = "0";
  151. $frameBorderColorHex = "000000";
  152. } else {
  153. $frameBorderStyleCss = "outset";
  154. $frameBorderWidth = ceil($squareSize / 50);
  155. $frameBorderWidthCss = $frameBorderWidth . "px";
  156. }
  157. $frameBorderColorHexCss = "#" . $frameBorderColorHex;
  158. $frameWidth = $boardSize;
  159. $frameWidthCss = $frameWidth . "px";
  160. $frameHeight = $boardSize + $buttonHeight;
  161. $frameHeightCss = $frameHeight . "px";
  162. // undocumented features
  163. $backgroundColorHex = get_param("backgroundColorHex", "bch", "transparent");
  164. if (preg_match("/^[0123456789ABCDEF]{6}$/i", $backgroundColorHex)) {
  165. $backgroundColorHexCss = "#" . $backgroundColorHex;
  166. } else {
  167. $backgroundColorHexCss = $backgroundColorHex;
  168. }
  169. $framePadding = get_param("framePadding", "fp", 0);
  170. if ($framePadding != 0) {
  171. $framePaddingCss = $framePadding . "px";
  172. } else {
  173. $framePaddingCss = $framePadding;
  174. }
  175. $rawGame = "";
  176. $pgnMini = get_param("pgnMini", "pm", "");
  177. if (($pgnMini == "true") || ($pgnMini == "t")) {
  178. if (preg_match('/\[\s*FEN\s*"[^"]*"\s*\]/', $pgnGame, $matches)) { $rawGame = $rawGame . "[SetUp \"1\"]\n" . $matches[0] . "\n\n"; }
  179. $rawGame = $rawGame . preg_replace('/\[\s*\w+\s*"[^"]*"\s*\]\s*/', "", $pgnGame);
  180. }
  181. $pgnFull = get_param("pgnFull", "pf", "");
  182. if (($pgnFull == "true") || ($pgnFull == "t")) {
  183. $rawGame = $pgnGame;
  184. }
  185. // end of undocumented features
  186. $outerFrameWidth = $frameWidth + 2 * $frameBorderWidth + 2 * $framePadding;
  187. $outerFrameHeight = $frameHeight + 2 * $frameBorderWidth + 2 * $framePadding;
  188. function curPageURL() {
  189. $pageURL = 'http';
  190. if ($_SERVER["HTTPS"] == "on") { $pageURL .= "s"; }
  191. $pageURL .= "://";
  192. if ($_SERVER["SERVER_PORT"] != "80") {
  193. $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
  194. } else {
  195. $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
  196. }
  197. return $pageURL;
  198. }
  199. $thisPage = curPageURL();
  200. $expiresMeta = "";
  201. if ($expiresDate) {
  202. header("Expires: " . $expiresDate);
  203. $expiresMeta = "<meta http-equiv=\"Expires\" content=\"" . $expiresDate . "\">";
  204. }
  205. if ($rawGame) {
  206. print $rawGame;
  207. exit;
  208. }
  209. print <<<END
  210. <html>
  211. <head>
  212. <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  213. $expiresMeta
  214. <title>chess puzzler</title>
  215. <!-- debug info
  216. $debugInfo
  217. end of debug info -->
  218. <style type="text/css">
  219. html,
  220. body {
  221. margin: 0;
  222. padding: $framePaddingCss;
  223. background: $backgroundColorHexCss;
  224. }
  225. .container {
  226. width: $frameWidthCss;
  227. height: $frameHeightCss;
  228. border-style: $frameBorderStyleCss;
  229. border-width: $frameBorderWidthCss;
  230. border-color: $frameBorderColorHexCss;
  231. }
  232. .boardTable {
  233. width: $boardSizeCss;
  234. height: $boardSizeCss;
  235. border-width: 0;
  236. }
  237. .pieceImage {
  238. width: $pieceSizeCss;
  239. height: $pieceSizeCss;
  240. }
  241. .whiteSquare,
  242. .blackSquare,
  243. .highlightWhiteSquare,
  244. .highlightBlackSquare {
  245. width: $bareSquareSizeCss;
  246. height: $bareSquareSizeCss;
  247. border-style: $borderStyleCss;
  248. border-width: $borderSizeCss;
  249. }
  250. .whiteSquare,
  251. .highlightWhiteSquare {
  252. border-color: $lightColorHexCss;
  253. background: $lightColorHexCss;
  254. }
  255. .blackSquare,
  256. .highlightBlackSquare {
  257. border-color: $darkColorHexCss;
  258. background: $darkColorHexCss;
  259. }
  260. .highlightWhiteSquare,
  261. .highlightBlackSquare {
  262. border-style: $highlightBorderStyleCss;
  263. }
  264. .buttonTable {
  265. width: $boardSizeCss;
  266. height: $buttonHeightCss;
  267. background-color: $controlBackgroundColorHexCss;
  268. }
  269. .buttonCell {
  270. width: $buttonWidthCss;
  271. height: $buttonHeightCss;
  272. white-space: nowrap;
  273. overflow: hidden;
  274. }
  275. .buttonCellLink {
  276. font-family: sans-serif;
  277. font-size: $buttonFontSizeCss;
  278. font-weight: 900;
  279. color: $controlTextColorHexCss;
  280. text-decoration: none;
  281. }
  282. .sidetomoveBox {
  283. width: $sidetomoveWidthCss;
  284. height: $sidetomoveHeightCss;
  285. border-style: solid;
  286. border-width: $sidetomoveBorderCss;
  287. border-color: $controlTextColorHexCss;
  288. }
  289. </style>
  290. <link rel="shortcut icon" href="pawn.ico" />
  291. <script src="pgn4web.js" type="text/javascript"></script>
  292. <script type="text/javascript">
  293. SetImagePath("$pieceFont/$pieceSize");
  294. SetShortcutKeysEnabled(false);
  295. function setPuzzlerHelpShortcutSquares(cols, rows) {
  296. var puzzlerHelp = "chess puzzler" + "\\n\\n";
  297. puzzlerHelp += "- the white or black small square below the chessboard's left side indicates the side to move" + "\\n\\n";
  298. puzzlerHelp += "- show the puzzler's solution step by step on the chessboard by clicking the > button below the chessboard's right side" + "\\n\\n";
  299. puzzlerHelp += "- step backwards one move by clicking the < button below the chessboard's left side" + "\\n\\n";
  300. puzzlerHelp += "click OK to learn how to add the chess puzzler to your website, blog or iGoogle page";
  301. if ((typeof cols != "string") || (typeof rows != "string")) { return; }
  302. for (c=0; c<cols.length; c++) { for (r=0; r<rows.length; r++) {
  303. boardShortcut(cols.charAt(c).toUpperCase()+rows.charAt(r), "chess puzzler help", function(t,e){ if (confirm(puzzlerHelp)) { window.open("http://code.google.com/p/pgn4web/wiki/Example_Puzzler", "_blank"); } });
  304. } }
  305. }
  306. setPuzzlerHelpShortcutSquares("BCDEFGH", "7");
  307. setPuzzlerHelpShortcutSquares("ABCDEFGH", "23456");
  308. setPuzzlerHelpShortcutSquares("BCFG", "1");
  309. function solutionSoFar() {
  310. sol = "";
  311. for (thisPly = StartPly; thisPly < CurrentPly; thisPly++) {
  312. if (thisPly % 2 == 0) {
  313. sol += ((thisPly / 2) + 1) + ". ";
  314. } else if (thisPly == StartPly) {
  315. sol += ((thisPly + 1) / 2) + "... ";
  316. }
  317. sol += Moves[thisPly] + " ";
  318. }
  319. return sol;
  320. }
  321. function customFunctionOnMove() {
  322. if (CurrentPly == StartPly) {
  323. document.getElementById("leftButtonLink").innerHTML = "<table class='sidetomoveBox' style='background-color:" + (CurrentPly % 2 ? "black" : "white" ) + ";' cellspacing='0' cellpadding='0'><tr><td></td></tr></table>";
  324. document.getElementById("leftButton").title = ((CurrentPly % 2) ? "Black" : "White") + " to play: find the best move";
  325. } else {
  326. document.getElementById("leftButtonLink").innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&lt;&nbsp;&nbsp;&nbsp;&nbsp;";
  327. document.getElementById("leftButton").title = "click < to step backwards one move";
  328. }
  329. if (CurrentPly == StartPly+PlyNumber) {
  330. switch (res = gameResult[currentGame]) {
  331. case "1-0": outcome = "white wins"; break;
  332. case "0-1": outcome = "black wins"; break;
  333. case "1/2-1/2": outcome = "draw"; break;
  334. default: outcome = "end"; res = "*"; break;
  335. }
  336. document.getElementById("rightButtonLink").innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;" + res + "&nbsp;&nbsp;&nbsp;&nbsp;";
  337. document.getElementById("rightButton").title = solutionSoFar() + " ..." + outcome;
  338. } else {
  339. document.getElementById("rightButtonLink").innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&gt;&nbsp;&nbsp;&nbsp;&nbsp;";
  340. if (CurrentPly == StartPly) {
  341. document.getElementById("rightButton").title = "click > to show the puzzler's solution step by step on the chessboard";
  342. } else {
  343. document.getElementById("rightButton").title = solutionSoFar() + " ...click > to continue showing the puzzler's solution step by step on the chessboard";
  344. }
  345. }
  346. }
  347. </script>
  348. <!-- start of google analytics code -->
  349. <!-- end of google analytics code -->
  350. </head>
  351. <body>
  352. <!-- paste your PGN below and make sure you dont specify an external source with SetPgnUrl() -->
  353. <form style="display: none;"><textarea style="display: none;" id="pgnText">
  354. $pgnGame
  355. {
  356. pgn4web chess puzzler
  357. you can add the chess puzzler to your site with the following HTML code:
  358. <iframe height='$outerFrameHeight' width='$outerFrameWidth' frameborder='0' scrolling='no' marginheight='0' marginwidth='0' src='$thisPage'>
  359. iframe support required to display the chess puzzler
  360. </iframe>
  361. the following URL parameters allow customization of the chess puzzler:
  362. - pgnData=... selects the PGN file containing the puzzler, default: tactics.pgn
  363. - gameNum=... sets the game number for the puzzler to be shown, default: blank, showing the puzzler of the day updated at 00:00 GMT
  364. - squareSize=... sets the chessboard square size, default 30
  365. - lightColorHex=... sets the light squares color, in hexadecimal format, default: EFF4EC
  366. - darkColorHex=... sets the dark squares color, in hexadecimal format, default: C6CEC3
  367. - pieceFont=... sets the piece font type, either alpha, merida, uscf, random, hash or default, default: default
  368. - controlBackgroundColorHex=... sets the buttons background color, in hexadecimal format, default: EFF4EC
  369. - controlTextColorHex=... sets the buttons text color, in hexadecimal format, default: 888888
  370. - frameBorderColorHex=... sets the frame border color, in hexadecimal format, or none, default: A4A4A4
  371. }
  372. </textarea></form>
  373. <!-- paste your PGN above and make sure you dont specify an external source with SetPgnUrl() -->
  374. <center>
  375. <div class="container">
  376. <div style="display: inline" id="GameBoard"></div>
  377. <table class="buttonTable" border="0" cellspacing="0" cellpadding="0">
  378. <tr>
  379. <td id="leftButton" title="" class="buttonCell" onClick="javascript:GoToMove(CurrentPly - 1);" align="center" valign="middle">
  380. <a id="leftButtonLink" class="buttonCellLink" href="javascript:void(0);" onfocus="blur();"></a>
  381. </td>
  382. <td id="rightButton" title="" class="buttonCell" onClick="javascript:GoToMove(CurrentPly + 1);" align="center" valign="middle">
  383. <a id="rightButtonLink" class="buttonCellLink" href="javascript:void(0);" onfocus="blur();"></a>
  384. </td>
  385. </tr>
  386. </table>
  387. </div>
  388. </center>
  389. </body>
  390. </html>
  391. END;
  392. ?>