mambax7 /
chess
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||
| 2 | |||
| 3 | use XoopsModules\Chess; |
||
| 4 | |||
| 5 | // ------------------------------------------------------------------------ // |
||
| 6 | // XOOPS - PHP Content Management System // |
||
| 7 | // Copyright (c) 2000 XOOPS.org // |
||
| 8 | // <https://xoops.org> // |
||
| 9 | // ------------------------------------------------------------------------- // |
||
| 10 | // This program is free software; you can redistribute it and/or modify // |
||
| 11 | // it under the terms of the GNU General Public License as published by // |
||
| 12 | // the Free Software Foundation; either version 2 of the License, or // |
||
| 13 | // (at your option) any later version. // |
||
| 14 | // // |
||
| 15 | // You may not change or alter any portion of this comment or credits // |
||
| 16 | // of supporting developers from this source code or any supporting // |
||
| 17 | // source code which is considered copyrighted (c) material of the // |
||
| 18 | // original comment or credit authors. // |
||
| 19 | // // |
||
| 20 | // This program is distributed in the hope that it will be useful, // |
||
| 21 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // |
||
| 22 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // |
||
| 23 | // GNU General Public License for more details. // |
||
| 24 | // // |
||
| 25 | // You should have received a copy of the GNU General Public License // |
||
| 26 | // along with this program; if not, write to the Free Software // |
||
| 27 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // |
||
| 28 | // ------------------------------------------------------------------------ // |
||
| 29 | |||
| 30 | /** |
||
| 31 | * Handle an individual chess game. |
||
| 32 | * |
||
| 33 | * The array $gamedata used throughout this file contains the columns of the |
||
| 34 | * database row for the current game. |
||
| 35 | * @see ChessGame |
||
| 36 | * |
||
| 37 | * @package chess |
||
| 38 | * @subpackage game |
||
| 39 | */ |
||
| 40 | |||
| 41 | /**#@+ |
||
| 42 | */ |
||
| 43 | |||
| 44 | $GLOBALS['xoopsOption']['template_main'] = 'chess_game_main.tpl'; |
||
| 45 | require __DIR__ . '/header.php'; |
||
| 46 | |||
| 47 | require_once XOOPS_ROOT_PATH . '/class/xoopsformloader.php'; |
||
| 48 | $xoopsConfig['module_cache'][$xoopsModule->getVar('mid')] = 0; // disable caching |
||
| 49 | require_once XOOPS_ROOT_PATH . '/modules/chess/include/constants.inc.php'; |
||
| 50 | require_once XOOPS_ROOT_PATH . '/modules/chess/include/functions.php'; |
||
| 51 | |||
| 52 | chess_game(); |
||
| 53 | |||
| 54 | require_once XOOPS_ROOT_PATH . '/include/comment_view.php'; |
||
| 55 | require_once XOOPS_ROOT_PATH . '/footer.php'; |
||
| 56 | /**#@-*/ |
||
| 57 | |||
| 58 | /** |
||
| 59 | * Handle an individual chess game. |
||
| 60 | * |
||
| 61 | * processing: |
||
| 62 | * - fetch game data from database |
||
| 63 | * - handle user request |
||
| 64 | * - update game data in database |
||
| 65 | * - display board |
||
| 66 | */ |
||
| 67 | function chess_game() |
||
| 68 | { |
||
| 69 | // user input |
||
| 70 | |||
| 71 | $game_id = (int)@$_GET['game_id']; |
||
| 72 | |||
| 73 | $submit_move = isset($_POST['submit_move']); |
||
| 74 | |||
| 75 | $submit_refresh = isset($_POST['submit_refresh']); |
||
| 76 | |||
| 77 | $move = chess_sanitize(trim(@$_POST['chessmove']), 'A-Za-z0-9=-'); |
||
| 78 | |||
| 79 | $movetype = chess_sanitize(@$_POST['movetype']); |
||
| 80 | |||
| 81 | $confirm = isset($_POST['confirm']) ? 1 : 0; |
||
| 82 | |||
| 83 | $move_explain = chess_sanitize(trim(@$_POST['move_explain']), 'A-Za-z0-9 .,;:=()[]*?!-'); |
||
| 84 | |||
| 85 | $arbiter_explain = chess_sanitize(trim(@$_POST['arbiter_explain']), 'A-Za-z0-9 .,;:=()[]*?!-'); |
||
| 86 | |||
| 87 | $orientation = chess_sanitize(@$_POST['orientation']); |
||
| 88 | |||
| 89 | $show_arbiter_ctrl = isset($_POST['show_arbiter_ctrl']); |
||
| 90 | |||
| 91 | $submit_arbitrate = isset($_POST['submit_arbitrate']); |
||
| 92 | |||
| 93 | $arbiter_action = chess_sanitize(@$_POST['arbiter_action']); |
||
| 94 | |||
| 95 | // If form-submit, check security token. |
||
| 96 | |||
| 97 | if (($submit_move || $submit_refresh || $submit_arbitrate || $show_arbiter_ctrl) && is_object($GLOBALS['xoopsSecurity']) && !$GLOBALS['xoopsSecurity']->check()) { |
||
| 98 | redirect_header( |
||
| 99 | XOOPS_URL . '/modules/chess/', |
||
| 100 | _CHESS_REDIRECT_DELAY_FAILURE, |
||
| 101 | _MD_CHESS_TOKEN_ERROR . '<br>' . implode('<br>', $GLOBALS['xoopsSecurity']->getErrors()) |
||
| 102 | ); |
||
| 103 | } |
||
| 104 | |||
| 105 | // Fetch the game data from the database. |
||
| 106 | |||
| 107 | $gamedata = chess_get_game($game_id); |
||
| 108 | |||
| 109 | if (false === $gamedata) { |
||
| 110 | redirect_header(XOOPS_URL . '/modules/chess/', _CHESS_REDIRECT_DELAY_FAILURE, _MD_CHESS_GAME_NOT_FOUND); |
||
| 111 | } |
||
| 112 | |||
| 113 | $gamedata_updated = false; |
||
| 114 | |||
| 115 | $move_performed = false; // status result from move-handler |
||
| 116 | $move_result_text = ''; // text result from move-handler |
||
| 117 | $draw_claim_error_text = ''; // text describing invalid draw-claim |
||
| 118 | $notify = ''; // text description of action for notification |
||
| 119 | $delete_game = false; |
||
| 120 | |||
| 121 | global $xoopsUser; |
||
| 122 | |||
| 123 | $uid = $xoopsUser ? $xoopsUser->getVar('uid') : 0; |
||
| 124 | |||
| 125 | $selfplay = $gamedata['white_uid'] == $gamedata['black_uid']; |
||
| 126 | |||
| 127 | if ($selfplay) { |
||
| 128 | if ($uid == $gamedata['white_uid']) { |
||
| 129 | $user_color = $gamedata['fen_active_color']; // current user is either player, so consider him active player |
||
| 130 | } else { |
||
| 131 | $user_color = ''; // current user is not a player |
||
| 132 | } |
||
| 133 | // not self-play |
||
| 134 | } else { |
||
| 135 | if ($uid == $gamedata['white_uid']) { |
||
| 136 | $user_color = 'w'; // current user is white |
||
| 137 | } elseif ($uid == $gamedata['black_uid']) { |
||
| 138 | $user_color = 'b'; // current user is black |
||
| 139 | } else { |
||
| 140 | $user_color = ''; // current user is not a player |
||
| 141 | } |
||
| 142 | } |
||
| 143 | |||
| 144 | // Determine whether current user is a player in the current game, and whether it's his move. |
||
| 145 | |||
| 146 | $user_is_player = 'w' == $user_color || 'b' == $user_color; |
||
| 147 | |||
| 148 | $user_is_player_to_move = $user_color == $gamedata['fen_active_color']; |
||
| 149 | |||
| 150 | $user_color_name = 'w' == $user_color ? _MD_CHESS_WHITE : _MD_CHESS_BLACK; |
||
| 151 | |||
| 152 | if ($submit_move && !$gamedata['suspended']) { |
||
| 153 | // If the player has changed his confirm-move preference, it needs to be updated in the database. |
||
| 154 | |||
| 155 | // For a self-play game, the white and black confirm-move preferences are kept the same, to avoid confusion. |
||
| 156 | |||
| 157 | if ($user_is_player) { |
||
| 158 | if ('w' == $user_color && $gamedata['white_confirm'] != $confirm) { |
||
| 159 | $gamedata['white_confirm'] = $confirm; |
||
| 160 | |||
| 161 | if ($selfplay) { |
||
| 162 | $gamedata['black_confirm'] = $confirm; |
||
| 163 | } |
||
| 164 | |||
| 165 | $gamedata_updated = true; |
||
| 166 | } elseif ('b' == $user_color && $gamedata['black_confirm'] != $confirm) { |
||
| 167 | $gamedata['black_confirm'] = $confirm; |
||
| 168 | |||
| 169 | if ($selfplay) { |
||
| 170 | $gamedata['white_confirm'] = $confirm; |
||
| 171 | } |
||
| 172 | |||
| 173 | $gamedata_updated = true; |
||
| 174 | } |
||
| 175 | } |
||
| 176 | |||
| 177 | switch ($movetype) { |
||
| 178 | default: |
||
| 179 | case _CHESS_MOVETYPE_NORMAL: |
||
| 180 | if ($user_is_player_to_move && $gamedata['offer_draw'] != $user_color) { |
||
| 181 | [$move_performed, $move_result_text] = chess_move($gamedata, $move); |
||
| 182 | |||
| 183 | if ($move_performed) { |
||
| 184 | if ($selfplay) { // If self-play, the current user's color switches. |
||
| 185 | $user_color = $gamedata['fen_active_color']; |
||
| 186 | } |
||
| 187 | |||
| 188 | $gamedata['offer_draw'] = ''; |
||
| 189 | |||
| 190 | $gamedata_updated = true; |
||
| 191 | |||
| 192 | $notify = "$user_color_name: $move"; |
||
| 193 | } |
||
| 194 | } |
||
| 195 | break; |
||
| 196 | case _CHESS_MOVETYPE_CLAIM_DRAW_3: |
||
| 197 | case _CHESS_MOVETYPE_CLAIM_DRAW_50: |
||
| 198 | if ($user_is_player_to_move && $gamedata['offer_draw'] != $user_color) { |
||
| 199 | if (!empty($move)) { |
||
| 200 | [$move_performed, $move_result_text] = chess_move($gamedata, $move); |
||
| 201 | |||
| 202 | #var_dump('chess_game', $move_performed, $move_result_text);#*#DEBUG# |
||
| 203 | |||
| 204 | if ($move_performed) { |
||
| 205 | if ($selfplay) { // If self-play, the current user's color switches. |
||
| 206 | $user_color = $gamedata['fen_active_color']; |
||
| 207 | } |
||
| 208 | |||
| 209 | $gamedata['offer_draw'] = ''; |
||
| 210 | |||
| 211 | $gamedata_updated = true; |
||
| 212 | |||
| 213 | $notify = "$user_color_name: $move"; |
||
| 214 | } else { |
||
| 215 | break; // move invalid, so don't bother checking draw-claim |
||
| 216 | } |
||
| 217 | } |
||
| 218 | |||
| 219 | [$draw_claim_valid, $draw_claim_text] = _CHESS_MOVETYPE_CLAIM_DRAW_3 == $movetype ? chess_is_draw_threefold_repetition($gamedata) : chess_is_draw_50_move_rule($gamedata); |
||
| 220 | |||
| 221 | #var_dump('chess_game', $draw_claim_valid, $draw_claim_text);#*#DEBUG# |
||
| 222 | |||
| 223 | if ($draw_claim_valid) { |
||
| 224 | $gamedata['offer_draw'] = ''; |
||
| 225 | |||
| 226 | $gamedata['pgn_result'] = '1/2-1/2'; |
||
| 227 | |||
| 228 | $comment = '{' . $draw_claim_text . '}'; |
||
| 229 | |||
| 230 | $gamedata['pgn_movetext'] = str_replace('*', "{$gamedata['pgn_result']} $comment", $gamedata['pgn_movetext']); |
||
| 231 | |||
| 232 | $gamedata_updated = true; |
||
| 233 | |||
| 234 | $notify = !empty($move) ? "$user_color_name: $move: $draw_claim_text" : "$user_color_name: $draw_claim_text"; |
||
| 235 | } else { |
||
| 236 | $draw_claim_error_text = $draw_claim_text; |
||
| 237 | } |
||
| 238 | } |
||
| 239 | break; |
||
| 240 | case _CHESS_MOVETYPE_RESIGN: |
||
| 241 | if ($user_is_player) { |
||
| 242 | $gamedata['offer_draw'] = ''; |
||
| 243 | |||
| 244 | $gamedata['pgn_result'] = 'w' == $user_color ? '0-1' : '1-0'; |
||
| 245 | |||
| 246 | $comment = '{' . $user_color_name . ' ' . _MD_CHESS_RESIGNED . '}'; |
||
| 247 | |||
| 248 | $gamedata['pgn_movetext'] = str_replace('*', "{$gamedata['pgn_result']} $comment", $gamedata['pgn_movetext']); |
||
| 249 | |||
| 250 | $gamedata_updated = true; |
||
| 251 | |||
| 252 | $notify = "$user_color_name " . _MD_CHESS_RESIGNED; |
||
| 253 | } |
||
| 254 | break; |
||
| 255 | case _CHESS_MOVETYPE_OFFER_DRAW: |
||
| 256 | if ($user_is_player && empty($gamedata['offer_draw']) && !$selfplay) { |
||
| 257 | $gamedata['offer_draw'] = $user_color; |
||
| 258 | |||
| 259 | $gamedata_updated = true; |
||
| 260 | |||
| 261 | $notify = "$user_color_name " . _MD_CHESS_OFFERED_DRAW; |
||
| 262 | } |
||
| 263 | break; |
||
| 264 | case _CHESS_MOVETYPE_ACCEPT_DRAW: |
||
| 265 | if ($user_is_player && !empty($gamedata['offer_draw']) && $gamedata['offer_draw'] != $user_color && !$selfplay) { |
||
| 266 | $gamedata['offer_draw'] = ''; |
||
| 267 | |||
| 268 | $gamedata['pgn_result'] = '1/2-1/2'; |
||
| 269 | |||
| 270 | $comment = '{' . _MD_CHESS_DRAW_BY_AGREEMENT . '}'; |
||
| 271 | |||
| 272 | $gamedata['pgn_movetext'] = str_replace('*', "{$gamedata['pgn_result']} $comment", $gamedata['pgn_movetext']); |
||
| 273 | |||
| 274 | $gamedata_updated = true; |
||
| 275 | |||
| 276 | $notify = "$user_color_name " . _MD_CHESS_ACCEPTED_DRAW; |
||
| 277 | } |
||
| 278 | |||
| 279 | // no break |
||
| 280 | case _CHESS_MOVETYPE_REJECT_DRAW: |
||
| 281 | if ($user_is_player && !empty($gamedata['offer_draw']) && $gamedata['offer_draw'] != $user_color && !$selfplay) { |
||
| 282 | $gamedata['offer_draw'] = ''; |
||
| 283 | |||
| 284 | $gamedata_updated = true; |
||
| 285 | |||
| 286 | $notify = "$user_color_name " . _MD_CHESS_REJECTED_DRAW; |
||
| 287 | } |
||
| 288 | break; |
||
| 289 | case _CHESS_MOVETYPE_RESTART: |
||
| 290 | if ($user_is_player && $selfplay) { |
||
| 291 | // instantiate a ChessGame to get a "fresh" gamestate |
||
| 292 | |||
| 293 | $chessgame = new Chess\ChessGame($gamedata['pgn_fen']); |
||
| 294 | |||
| 295 | $new_gamestate = $chessgame->gamestate(); |
||
| 296 | |||
| 297 | // update the game data |
||
| 298 | |||
| 299 | $gamedata['fen_piece_placement'] = $new_gamestate['fen_piece_placement']; |
||
| 300 | |||
| 301 | $gamedata['fen_active_color'] = $new_gamestate['fen_active_color']; |
||
| 302 | |||
| 303 | $gamedata['fen_castling_availability'] = $new_gamestate['fen_castling_availability']; |
||
| 304 | |||
| 305 | $gamedata['fen_en_passant_target_square'] = $new_gamestate['fen_en_passant_target_square']; |
||
| 306 | |||
| 307 | $gamedata['fen_halfmove_clock'] = $new_gamestate['fen_halfmove_clock']; |
||
| 308 | |||
| 309 | $gamedata['fen_fullmove_number'] = $new_gamestate['fen_fullmove_number']; |
||
| 310 | |||
| 311 | $gamedata['pgn_fen'] = $new_gamestate['pgn_fen']; |
||
| 312 | |||
| 313 | $gamedata['pgn_result'] = $new_gamestate['pgn_result']; |
||
| 314 | |||
| 315 | $gamedata['pgn_movetext'] = $new_gamestate['pgn_movetext']; |
||
| 316 | |||
| 317 | // update user color |
||
| 318 | |||
| 319 | $user_color = $gamedata['fen_active_color']; |
||
| 320 | |||
| 321 | $gamedata_updated = true; |
||
| 322 | } |
||
| 323 | break; |
||
| 324 | case _CHESS_MOVETYPE_DELETE: |
||
| 325 | if ($user_is_player && ($selfplay || chess_can_delete())) { |
||
| 326 | $delete_game = true; // must defer actual deletion until after notifications are sent |
||
| 327 | $notify = eval('return \'' . _MD_CHESS_DELETED_GAME . '\';'); // eval references $username |
||
| 328 | } |
||
| 329 | break; |
||
| 330 | case _CHESS_MOVETYPE_WANT_ARBITRATION: |
||
| 331 | if ($user_is_player) { |
||
| 332 | // Ensure that $move_explain does not contain separator $sep. |
||
| 333 | |||
| 334 | $sep = '|'; |
||
| 335 | |||
| 336 | $move_explain = str_replace($sep, '_', $move_explain); |
||
| 337 | |||
| 338 | $gamedata['suspended'] = implode($sep, [date('Y-m-d H:i:s'), $uid, _CHESS_MOVETYPE_WANT_ARBITRATION, $move_explain]); |
||
| 339 | |||
| 340 | $gamedata_updated = true; |
||
| 341 | |||
| 342 | $notify = "$user_color_name " . _MD_CHESS_RQSTED_ARBT . ' ' . _MD_CHESS_BEEN_SUSPENDED; |
||
| 343 | } |
||
| 344 | break; |
||
| 345 | } |
||
| 346 | } |
||
| 347 | |||
| 348 | // If board orientation setting uninitialized or invalid, set it to the appropriate default. |
||
| 349 | |||
| 350 | if (!in_array($orientation, [_CHESS_ORIENTATION_ACTIVE, _CHESS_ORIENTATION_WHITE, _CHESS_ORIENTATION_BLACK])) { |
||
| 351 | if ($user_is_player && 'b' == $user_color) { |
||
| 352 | $orientation = _CHESS_ORIENTATION_BLACK; |
||
| 353 | } else { |
||
| 354 | $orientation = _CHESS_ORIENTATION_WHITE; |
||
| 355 | } |
||
| 356 | } |
||
| 357 | |||
| 358 | // Determine if user is a valid arbiter. |
||
| 359 | |||
| 360 | global $xoopsModule; |
||
| 361 | |||
| 362 | $is_arbiter = is_object($xoopsUser) && $xoopsUser->isAdmin($xoopsModule->getVar('mid')); |
||
| 363 | |||
| 364 | // If arbiter action was submitted, and user is a valid arbiter, process the action. |
||
| 365 | |||
| 366 | if ($submit_arbitrate && $is_arbiter) { |
||
| 367 | $username = $xoopsUser ? $xoopsUser->getVar('uname') : '*unknown*'; |
||
| 368 | |||
| 369 | switch ($arbiter_action) { |
||
| 370 | case _CHESS_ARBITER_RESUME: |
||
| 371 | if ($gamedata['suspended'] && '*' == $gamedata['pgn_result']) { |
||
| 372 | $gamedata['suspended'] = ''; |
||
| 373 | |||
| 374 | $gamedata_updated = true; |
||
| 375 | |||
| 376 | $notify = eval('return \'' . _MD_CHESS_RESUMED_PLAY . '\';'); // eval references $username |
||
| 377 | } |
||
| 378 | break; |
||
| 379 | case _CHESS_ARBITER_DRAW: |
||
| 380 | if ($gamedata['suspended'] && '*' == $gamedata['pgn_result']) { |
||
| 381 | $gamedata['offer_draw'] = ''; |
||
| 382 | |||
| 383 | $gamedata['pgn_result'] = '1/2-1/2'; |
||
| 384 | |||
| 385 | $arbiter_explain = str_replace('{', '(', $arbiter_explain); |
||
| 386 | |||
| 387 | $arbiter_explain = str_replace('}', ')', $arbiter_explain); |
||
| 388 | |||
| 389 | $comment = '{' . sprintf(_MD_CHESS_DRAW_DECLARED, $arbiter_explain) . '}'; |
||
| 390 | |||
| 391 | $gamedata['pgn_movetext'] = str_replace('*', "{$gamedata['pgn_result']} $comment", $gamedata['pgn_movetext']); |
||
| 392 | |||
| 393 | $gamedata['suspended'] = ''; |
||
| 394 | |||
| 395 | $gamedata_updated = true; |
||
| 396 | |||
| 397 | $notify = eval('return \'' . _MD_CHESS_DECLARED_DRAW . '\';'); // eval references $username |
||
| 398 | } |
||
| 399 | break; |
||
| 400 | case _CHESS_ARBITER_DELETE: |
||
| 401 | if ($gamedata['suspended']) { |
||
| 402 | $delete_game = true; // must defer actual deletion until after notifications are sent |
||
| 403 | $notify = eval('return \'' . _MD_CHESS_DELETED_GAME . '\';'); // eval references $username |
||
| 404 | } |
||
| 405 | break; |
||
| 406 | case _CHESS_ARBITER_SUSPEND: |
||
| 407 | if (!$gamedata['suspended'] && '*' == $gamedata['pgn_result']) { |
||
| 408 | // Ensure that $arbiter_explain does not contain separator $sep. |
||
| 409 | |||
| 410 | $sep = '|'; |
||
| 411 | |||
| 412 | $arbiter_explain = str_replace($sep, '_', $arbiter_explain); |
||
| 413 | |||
| 414 | $gamedata['suspended'] = implode('|', [date('Y-m-d H:i:s'), $uid, _CHESS_MOVETYPE_ARBITER_SUSPEND, $arbiter_explain]); |
||
| 415 | |||
| 416 | $gamedata_updated = true; |
||
| 417 | |||
| 418 | $notify = eval('return \'' . _MD_CHESS_SUSPENDED_PLAY . '\';'); // eval references $username |
||
| 419 | } |
||
| 420 | break; |
||
| 421 | default: |
||
| 422 | break; |
||
| 423 | } |
||
| 424 | } |
||
| 425 | |||
| 426 | // Store the updated game data in the database. |
||
| 427 | |||
| 428 | if ($gamedata_updated) { |
||
| 429 | chess_put_game($game_id, $gamedata); |
||
| 430 | } |
||
| 431 | |||
| 432 | // Display the (possibly updated) board. |
||
| 433 | |||
| 434 | if (!$delete_game) { |
||
| 435 | chess_show_board($gamedata, $orientation, $user_color, $move_performed, $move_result_text, $draw_claim_error_text, $show_arbiter_ctrl && $is_arbiter); |
||
| 436 | } |
||
| 437 | |||
| 438 | // If a move (or other action) was made, notify any subscribers. |
||
| 439 | |||
| 440 | if (!empty($notify)) { |
||
| 441 | $notificationHandler = xoops_getHandler('notification'); |
||
| 442 | |||
| 443 | $notificationHandler->triggerEvent('game', $game_id, 'notify_game_move', ['CHESS_ACTION' => $notify]); |
||
| 444 | |||
| 445 | // admin notifications |
||
| 446 | |||
| 447 | if (_CHESS_MOVETYPE_WANT_ARBITRATION == $movetype) { |
||
| 448 | $event = 'notify_request_arbitration'; |
||
| 449 | |||
| 450 | $username = $xoopsUser ? $xoopsUser->getVar('uname') : '(' . _MD_CHESS_UNKNOWN . ')'; |
||
| 451 | |||
| 452 | $extra_tags = ['CHESS_REQUESTOR' => $username, 'CHESS_GAME_ID' => $game_id, 'CHESS_EXPLAIN' => $move_explain]; |
||
| 453 | |||
| 454 | $notificationHandler->triggerEvent('global', 0, $event, $extra_tags); |
||
| 455 | } |
||
| 456 | } |
||
| 457 | |||
| 458 | // Handle delete-game action. |
||
| 459 | |||
| 460 | if ($delete_game) { |
||
| 461 | chess_delete_game($game_id); |
||
| 462 | |||
| 463 | redirect_header(XOOPS_URL . '/modules/chess/', _CHESS_REDIRECT_DELAY_SUCCESS, _MD_CHESS_GAME_DELETED); |
||
| 464 | } |
||
| 465 | } |
||
| 466 | |||
| 467 | /** |
||
| 468 | * Fetch game data from database. |
||
| 469 | * |
||
| 470 | * @param int $game_id Game ID |
||
| 471 | * @return array Game data |
||
| 472 | */ |
||
| 473 | function chess_get_game($game_id) |
||
| 474 | { |
||
| 475 | global $xoopsDB; |
||
| 476 | |||
| 477 | $games_table = $xoopsDB->prefix('chess_games'); |
||
| 478 | |||
| 479 | $result = $xoopsDB->query("SELECT * FROM $games_table WHERE game_id = '$game_id'"); |
||
| 480 | |||
| 481 | $gamedata = $xoopsDB->fetchArray($result); |
||
| 482 | |||
| 483 | #var_dump('chess_get_game, gamedata', $gamedata);#*#DEBUG# |
||
| 484 | |||
| 485 | $xoopsDB->freeRecordSet($result); |
||
| 486 | |||
| 487 | return $gamedata; |
||
| 488 | } |
||
| 489 | |||
| 490 | /** |
||
| 491 | * Store game data in database. |
||
| 492 | * |
||
| 493 | * @param int $game_id Game ID |
||
| 494 | * @param array $gamedata Game data |
||
| 495 | */ |
||
| 496 | function chess_put_game($game_id, $gamedata) |
||
| 497 | { |
||
| 498 | global $xoopsDB; |
||
| 499 | |||
| 500 | $myts = MyTextSanitizer::getInstance(); |
||
| 501 | |||
| 502 | $suspended_q = $myts->addSlashes($gamedata['suspended']); |
||
| 503 | |||
| 504 | $movetext_q = $myts->addSlashes($gamedata['pgn_movetext']); |
||
| 505 | |||
| 506 | $table = $xoopsDB->prefix('chess_games'); |
||
| 507 | |||
| 508 | #echo "updating database table $table<br>\n";#*#DEBUG# |
||
| 509 | |||
| 510 | $xoopsDB->query( |
||
| 511 | trim( |
||
| 512 | " |
||
| 513 | UPDATE $table |
||
| 514 | SET |
||
| 515 | start_date = '{$gamedata['start_date']}', |
||
| 516 | last_date = '{$gamedata['last_date']}', |
||
| 517 | fen_piece_placement = '{$gamedata['fen_piece_placement']}', |
||
| 518 | fen_active_color = '{$gamedata['fen_active_color']}', |
||
| 519 | fen_castling_availability = '{$gamedata['fen_castling_availability']}', |
||
| 520 | fen_en_passant_target_square = '{$gamedata['fen_en_passant_target_square']}', |
||
| 521 | fen_halfmove_clock = '{$gamedata['fen_halfmove_clock']}', |
||
| 522 | fen_fullmove_number = '{$gamedata['fen_fullmove_number']}', |
||
| 523 | pgn_result = '{$gamedata['pgn_result']}', |
||
| 524 | pgn_movetext = '$movetext_q', |
||
| 525 | offer_draw = '{$gamedata['offer_draw']}', |
||
| 526 | suspended = '$suspended_q', |
||
| 527 | white_confirm = '{$gamedata['white_confirm']}', |
||
| 528 | black_confirm = '{$gamedata['black_confirm']}' |
||
| 529 | WHERE game_id = '$game_id' |
||
| 530 | " |
||
| 531 | ) |
||
| 532 | ); |
||
| 533 | |||
| 534 | if ($xoopsDB->errno()) { |
||
| 535 | trigger_error($xoopsDB->errno() . ':' . $xoopsDB->error(), E_USER_ERROR); |
||
| 536 | } |
||
| 537 | |||
| 538 | if ('*' != $gamedata['pgn_result'] && '1' == $gamedata['is_rated']) { |
||
| 539 | require_once XOOPS_ROOT_PATH . '/modules/chess/include/ratings.php'; |
||
| 540 | |||
| 541 | chess_ratings_adj($game_id); |
||
| 542 | } |
||
| 543 | } |
||
| 544 | |||
| 545 | /** |
||
| 546 | * Delete game from database. |
||
| 547 | * |
||
| 548 | * @param int $game_id Game ID |
||
| 549 | */ |
||
| 550 | function chess_delete_game($game_id) |
||
| 551 | { |
||
| 552 | global $xoopsModule, $xoopsDB; |
||
| 553 | |||
| 554 | // delete notifications associated with this game |
||
| 555 | |||
| 556 | xoops_notification_deletebyitem($xoopsModule->getVar('mid'), 'game', $game_id); |
||
| 557 | |||
| 558 | $table = $xoopsDB->prefix('chess_games'); |
||
| 559 | |||
| 560 | $xoopsDB->query("DELETE FROM $table WHERE game_id='$game_id'"); |
||
| 561 | |||
| 562 | if ($xoopsDB->errno()) { |
||
| 563 | trigger_error($xoopsDB->errno() . ':' . $xoopsDB->error(), E_USER_ERROR); |
||
| 564 | } |
||
| 565 | } |
||
| 566 | |||
| 567 | /** |
||
| 568 | * Handle a move. |
||
| 569 | * |
||
| 570 | * @param array $gamedata Game data (input/output) |
||
| 571 | * @param string $move The move |
||
| 572 | * @return array A two-element array: |
||
| 573 | * - $move_performed: true if the move was performed and the game state has been updated, false otherwise |
||
| 574 | * - $move_result_text: text message |
||
| 575 | */ |
||
| 576 | function chess_move(&$gamedata, $move) |
||
| 577 | { |
||
| 578 | $gamestate = [ |
||
| 579 | 'fen_piece_placement' => $gamedata['fen_piece_placement'], |
||
| 580 | 'fen_active_color' => $gamedata['fen_active_color'], |
||
| 581 | 'fen_castling_availability' => $gamedata['fen_castling_availability'], |
||
| 582 | 'fen_en_passant_target_square' => $gamedata['fen_en_passant_target_square'], |
||
| 583 | 'fen_halfmove_clock' => $gamedata['fen_halfmove_clock'], |
||
| 584 | 'fen_fullmove_number' => $gamedata['fen_fullmove_number'], |
||
| 585 | 'pgn_fen' => $gamedata['pgn_fen'], |
||
| 586 | 'pgn_result' => $gamedata['pgn_result'], |
||
| 587 | 'pgn_movetext' => $gamedata['pgn_movetext'], |
||
| 588 | ]; |
||
| 589 | |||
| 590 | $chessgame = new Chess\ChessGame($gamestate); |
||
| 591 | |||
| 592 | #echo "Performing move: '$move'<br>\n";#*#DEBUG# |
||
| 593 | |||
| 594 | [$move_performed, $move_result_text] = $chessgame->move($move); |
||
| 595 | |||
| 596 | #echo "Move result: '$move_result_text'<br>\n";#*#DEBUG# |
||
| 597 | |||
| 598 | if ($move_performed) { |
||
| 599 | // The move was valid - update the game data. |
||
| 600 | |||
| 601 | $new_gamestate = $chessgame->gamestate(); |
||
| 602 | |||
| 603 | #var_dump('new_gamestate', $new_gamestate);#*#DEBUG# |
||
| 604 | |||
| 605 | #*#DEBUG# - start |
||
| 606 | |||
| 607 | #if ($new_gamestate['fen_castling_availability'] != $gamedata['fen_castling_availability']) { |
||
| 608 | |||
| 609 | # echo "*** castling_availability changed from '{$gamedata['fen_castling_availability']}' to '{$new_gamestate['fen_castling_availability']}' ***<br>\n"; |
||
| 610 | |||
| 611 | #} |
||
| 612 | |||
| 613 | #*#DEBUG# - end |
||
| 614 | |||
| 615 | $gamedata['fen_piece_placement'] = $new_gamestate['fen_piece_placement']; |
||
| 616 | |||
| 617 | $gamedata['fen_active_color'] = $new_gamestate['fen_active_color']; |
||
| 618 | |||
| 619 | $gamedata['fen_castling_availability'] = $new_gamestate['fen_castling_availability']; |
||
| 620 | |||
| 621 | $gamedata['fen_en_passant_target_square'] = $new_gamestate['fen_en_passant_target_square']; |
||
| 622 | |||
| 623 | $gamedata['fen_halfmove_clock'] = $new_gamestate['fen_halfmove_clock']; |
||
| 624 | |||
| 625 | $gamedata['fen_fullmove_number'] = $new_gamestate['fen_fullmove_number']; |
||
| 626 | |||
| 627 | $gamedata['pgn_fen'] = $new_gamestate['pgn_fen']; |
||
| 628 | |||
| 629 | $gamedata['pgn_result'] = $new_gamestate['pgn_result']; |
||
| 630 | |||
| 631 | $gamedata['pgn_movetext'] = $new_gamestate['pgn_movetext']; |
||
| 632 | |||
| 633 | $gamedata['last_date'] = date('Y-m-d H:i:s'); |
||
| 634 | |||
| 635 | // if start_date undefined (first move), initialize it |
||
| 636 | |||
| 637 | if ('0000-00-00 00:00:00' == $gamedata['start_date']) { |
||
| 638 | $gamedata['start_date'] = $gamedata['last_date']; |
||
| 639 | } |
||
| 640 | } |
||
| 641 | |||
| 642 | return [$move_performed, $move_result_text]; |
||
| 643 | } |
||
| 644 | |||
| 645 | /** |
||
| 646 | * Verify a draw-claim under the 50-move rule. |
||
| 647 | * |
||
| 648 | * @param array $gamedata Game data |
||
| 649 | * @return array A two-element array: |
||
| 650 | * - $draw_claim_valid: True if draw-claim is valid, otherwise false |
||
| 651 | * - $draw_claim_text: Describes draw-claim result |
||
| 652 | */ |
||
| 653 | function chess_is_draw_50_move_rule($gamedata) |
||
| 654 | { |
||
| 655 | #var_dump('gamedata', $gamedata);#*#DEBUG# |
||
| 656 | |||
| 657 | if ($gamedata['fen_halfmove_clock'] >= 100) { |
||
| 658 | $draw_claim_valid = true; |
||
| 659 | |||
| 660 | $draw_claim_text = _MD_CHESS_DRAW_50; |
||
| 661 | } else { |
||
| 662 | $draw_claim_valid = false; |
||
| 663 | |||
| 664 | $draw_claim_text = _MD_CHESS_NO_DRAW_50; |
||
| 665 | } |
||
| 666 | |||
| 667 | return [$draw_claim_valid, $draw_claim_text]; |
||
| 668 | } |
||
| 669 | |||
| 670 | /** |
||
| 671 | * Verify a draw-claim under the threefold-repetition rule. |
||
| 672 | * |
||
| 673 | * Board positions are compared using the first four fields of the FEN data: |
||
| 674 | * fen_piece_placement, fen_active_color, fen_castling_availability and fen_en_passant_target_square. |
||
| 675 | * |
||
| 676 | * @param array $gamedata Game data |
||
| 677 | * @return array A two-element array: |
||
| 678 | * - $draw_claim_valid: True if draw-claim is valid, otherwise false |
||
| 679 | * - $draw_claim_text: Describes draw-claim result |
||
| 680 | */ |
||
| 681 | function chess_is_draw_threefold_repetition($gamedata) |
||
| 682 | { |
||
| 683 | #var_dump('gamedata', $gamedata);#*#DEBUG# |
||
| 684 | |||
| 685 | // Define this constant as true to output a log file containing a move analysis. |
||
| 686 | |||
| 687 | define('CHESS_LOG_3FOLD', false); |
||
| 688 | |||
| 689 | if (CHESS_LOG_3FOLD) { |
||
| 690 | $log = []; |
||
| 691 | } |
||
| 692 | |||
| 693 | $chessgame = new Chess\ChessGame($gamedata); |
||
| 694 | |||
| 695 | // board position against which to check for repetitions |
||
| 696 | |||
| 697 | $last_board_state = "{$gamedata['fen_piece_placement']} {$gamedata['fen_active_color']} {$gamedata['fen_castling_availability']} {$gamedata['fen_en_passant_target_square']}"; |
||
| 698 | |||
| 699 | $last_move_number = $gamedata['fen_fullmove_number']; |
||
| 700 | |||
| 701 | #echo "last_board_state='$last_board_state'<br>\n";#*#DEBUG# |
||
| 702 | |||
| 703 | if (CHESS_LOG_3FOLD) { |
||
| 704 | $log[] = sprintf('%08x %03d%1s %s', crc32($last_board_state), $gamedata['fen_fullmove_number'], $gamedata['fen_active_color'], $last_board_state); |
||
| 705 | } |
||
| 706 | |||
| 707 | $chessgame = new Chess\ChessGame($gamedata['pgn_fen']); |
||
| 708 | |||
| 709 | empty($chessgame->error) or trigger_error('chessgame invalid', E_USER_ERROR); |
||
| 710 | |||
| 711 | $tmp_gamedata = $chessgame->gamestate(); |
||
| 712 | |||
| 713 | is_array($tmp_gamedata) or trigger_error('gamestate invalid', E_USER_ERROR); |
||
| 714 | |||
| 715 | #*#DEBUG# - start |
||
| 716 | |||
| 717 | /*** |
||
| 718 | * if (function_exists('posix_times')) { |
||
| 719 | * $posix_times = posix_times(); |
||
| 720 | * var_dump('posix_times', $posix_times); |
||
| 721 | * } |
||
| 722 | * if (function_exists('getrusage')) { |
||
| 723 | * $rusage = getrusage(); |
||
| 724 | * var_dump('rusage', $rusage); |
||
| 725 | * } |
||
| 726 | ***/ |
||
| 727 | |||
| 728 | #*#DEBUG# - end |
||
| 729 | |||
| 730 | // $repeats is the list of moves for which the board positions are identical. |
||
| 731 | |||
| 732 | // For example, '6w' would represent the board position immediately before white's sixth move. |
||
| 733 | |||
| 734 | // The current position is included, since that's the position against which the other positions will be compared. |
||
| 735 | |||
| 736 | $repeats[] = $gamedata['fen_fullmove_number'] . $gamedata['fen_active_color']; |
||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
| 737 | |||
| 738 | // Compare initial board position with last board position, unless the move number is the same, meaning that there haven't been any moves. |
||
| 739 | |||
| 740 | #echo "FEN: '{$tmp_gamedata['fen_piece_placement']} {$tmp_gamedata['fen_active_color']} {$tmp_gamedata['fen_castling_availability']} {$tmp_gamedata['fen_en_passant_target_square']} {$tmp_gamedata['fen_halfmove_clock']} {$tmp_gamedata['fen_fullmove_number']}'<br>\n";#*#DEBUG# |
||
| 741 | |||
| 742 | $board_state = "{$tmp_gamedata['fen_piece_placement']} {$tmp_gamedata['fen_active_color']} {$tmp_gamedata['fen_castling_availability']} {$tmp_gamedata['fen_en_passant_target_square']}"; |
||
| 743 | |||
| 744 | if ($tmp_gamedata['fen_fullmove_number'] != $last_move_number && $board_state == $last_board_state) { |
||
| 745 | $repeats[] = $tmp_gamedata['fen_fullmove_number'] . $tmp_gamedata['fen_active_color']; |
||
| 746 | |||
| 747 | if (CHESS_LOG_3FOLD) { |
||
| 748 | $log[] = sprintf('%08x %03d%1s %s', crc32($board_state), $tmp_gamedata['fen_fullmove_number'], $tmp_gamedata['fen_active_color'], $board_state); |
||
| 749 | } |
||
| 750 | |||
| 751 | #*#DEBUG# - start |
||
| 752 | /*** |
||
| 753 | * if (count($repeats) >= 3) { |
||
| 754 | * echo "*** Three repetitions! {$repeats[1]},{$repeats[2]},{$repeats[0]} ***<br>\n"; |
||
| 755 | * } elseif (count($repeats) >= 2) { |
||
| 756 | * echo "*** Two repetitions! {$repeats[1]},{$repeats[0]} ***<br>\n"; |
||
| 757 | * } |
||
| 758 | ***/ |
||
| 759 | #*#DEBUG# - end |
||
| 760 | } |
||
| 761 | |||
| 762 | // Convert pgn_movetext into Nx3 array $movelist. |
||
| 763 | |||
| 764 | $movelist = chess_make_movelist($gamedata['pgn_movetext']); |
||
| 765 | |||
| 766 | #var_dump('movelist', $movelist);#*#DEBUG# |
||
| 767 | |||
| 768 | // Compare board positions after each move with last board position. |
||
| 769 | |||
| 770 | foreach ($movelist as $fullmove) { |
||
| 771 | #echo "'{$fullmove[0]}' '{$fullmove[1]}' '{$fullmove[2]}'<br>\n";#*#DEBUG# |
||
| 772 | |||
| 773 | if (CHESS_LOG_3FOLD) { |
||
| 774 | #$log[] = "'{$fullmove[0]}' '{$fullmove[1]}' '{$fullmove[2]}'";#*#LOG_3FOLD# #*#DEBUG# |
||
| 775 | } |
||
| 776 | |||
| 777 | for ($i = 1; $i <= 2; ++$i) { |
||
| 778 | if (!empty($fullmove[$i])) { |
||
| 779 | $move = $fullmove[$i]; |
||
| 780 | } else { |
||
| 781 | continue; // $fullmove[$i] can be empty if last move was white's, or if game was setup with black to move first. |
||
| 782 | } |
||
| 783 | |||
| 784 | // Remove check/checkmate annotation, if present. |
||
| 785 | |||
| 786 | $move = str_replace('+', '', $move); |
||
| 787 | |||
| 788 | $move = str_replace('#', '', $move); |
||
| 789 | |||
| 790 | #echo "Performing move: '$move'<br>\n";#*#DEBUG# |
||
| 791 | |||
| 792 | [$tmp_move_performed, $tmp_move_result_text] = $chessgame->move($move); |
||
| 793 | |||
| 794 | #echo "Move result: '$tmp_move_result_text'<br>\n";#*#DEBUG# |
||
| 795 | |||
| 796 | $tmp_move_performed or trigger_error("Failed to perform move $move: $tmp_move_result_text", E_USER_ERROR); |
||
| 797 | |||
| 798 | $tmp_gamedata = $chessgame->gamestate(); |
||
| 799 | |||
| 800 | #echo "FEN: '{$tmp_gamedata['fen_piece_placement']} {$tmp_gamedata['fen_active_color']} {$tmp_gamedata['fen_castling_availability']} {$tmp_gamedata['fen_en_passant_target_square']} {$tmp_gamedata['fen_halfmove_clock']} {$tmp_gamedata['fen_fullmove_number']}'<br>\n";#*#DEBUG# |
||
| 801 | |||
| 802 | $board_state = "{$tmp_gamedata['fen_piece_placement']} {$tmp_gamedata['fen_active_color']} {$tmp_gamedata['fen_castling_availability']} {$tmp_gamedata['fen_en_passant_target_square']}"; |
||
| 803 | |||
| 804 | if (CHESS_LOG_3FOLD) { |
||
| 805 | $log[] = sprintf('%08x %03d%1s %s', crc32($board_state), $tmp_gamedata['fen_fullmove_number'], $tmp_gamedata['fen_active_color'], $board_state); |
||
| 806 | } |
||
| 807 | |||
| 808 | if ($tmp_gamedata['fen_fullmove_number'] != $last_move_number && $board_state == $last_board_state) { |
||
| 809 | $repeats[] = $tmp_gamedata['fen_fullmove_number'] . $tmp_gamedata['fen_active_color']; |
||
| 810 | |||
| 811 | #*#DEBUG# - start |
||
| 812 | |||
| 813 | /*** |
||
| 814 | * if (count($repeats) >= 3) { |
||
| 815 | * echo "*** Three repetitions! {$repeats[1]},{$repeats[2]},{$repeats[0]} ***<br>\n"; |
||
| 816 | * } elseif (count($repeats) >= 2) { |
||
| 817 | * echo "*** Two repetitions! {$repeats[1]},{$repeats[0]} ***<br>\n"; |
||
| 818 | * } |
||
| 819 | ***/ |
||
| 820 | |||
| 821 | #*#DEBUG# - end |
||
| 822 | |||
| 823 | if (count($repeats) >= 3) { |
||
| 824 | break 2; |
||
| 825 | } |
||
| 826 | } |
||
| 827 | } |
||
| 828 | } |
||
| 829 | |||
| 830 | #*#DEBUG# - start |
||
| 831 | |||
| 832 | /*** |
||
| 833 | * if (function_exists('posix_times')) { |
||
| 834 | * $posix_times = posix_times(); |
||
| 835 | * var_dump('posix_times', $posix_times); |
||
| 836 | * } |
||
| 837 | * if (function_exists('getrusage')) { |
||
| 838 | * $rusage = getrusage(); |
||
| 839 | * var_dump('rusage', $rusage); |
||
| 840 | * } |
||
| 841 | ***/ |
||
| 842 | |||
| 843 | #*#DEBUG# - end |
||
| 844 | |||
| 845 | if (count($repeats) >= 3) { |
||
| 846 | $draw_claim_valid = true; |
||
| 847 | |||
| 848 | $draw_claim_text = sprintf(_MD_CHESS_DRAW_3, "{$repeats[1]},{$repeats[2]},{$repeats[0]}"); |
||
| 849 | } else { |
||
| 850 | $draw_claim_valid = false; |
||
| 851 | |||
| 852 | $draw_claim_text = _MD_CHESS_NO_DRAW_3; |
||
| 853 | } |
||
| 854 | |||
| 855 | if (CHESS_LOG_3FOLD) { |
||
| 856 | $logfile = XOOPS_ROOT_PATH . '/cache/' . date('Ymd_His') . '_3fold.log'; |
||
| 857 | |||
| 858 | sort($log); |
||
| 859 | |||
| 860 | error_log(implode("\n", $log), 3, $logfile); |
||
| 861 | } |
||
| 862 | |||
| 863 | return [$draw_claim_valid, $draw_claim_text]; |
||
| 864 | } |
||
| 865 | |||
| 866 | /** |
||
| 867 | * Convert pgn_movetext into Nx3 array. |
||
| 868 | * |
||
| 869 | * @param array $movetext pgn_movetext |
||
| 870 | * @return array Nx3 array |
||
| 871 | */ |
||
| 872 | function chess_make_movelist($movetext) |
||
| 873 | { |
||
| 874 | $movelist = []; |
||
| 875 | |||
| 876 | $move_tokens = explode(' ', preg_replace('/\{.*\}/', '', $movetext)); |
||
| 877 | |||
| 878 | $index = -1; |
||
| 879 | |||
| 880 | while ($move_tokens) { |
||
| 881 | $move_token = array_shift($move_tokens); |
||
| 882 | |||
| 883 | if (in_array($move_token, ['1-0', '0-1', '1/2-1/2', '*'])) { |
||
| 884 | break; |
||
| 885 | } elseif (preg_match('/^\d+(\.|\.\.\.)$/', $move_token, $matches)) { |
||
| 886 | ++$index; |
||
| 887 | |||
| 888 | $movelist[$index][] = $move_token; |
||
| 889 | |||
| 890 | if ('...' == $matches[1]) { // setup-game with initial black move - add padding for white's move |
||
| 891 | $movelist[$index][] = ''; |
||
| 892 | } |
||
| 893 | } else { |
||
| 894 | $movelist[$index][] = $move_token; |
||
| 895 | } |
||
| 896 | } |
||
| 897 | |||
| 898 | return $movelist; |
||
| 899 | } |
||
| 900 | |||
| 901 | /** |
||
| 902 | * Display chess board. |
||
| 903 | * |
||
| 904 | * @param array $gamedata Game data |
||
| 905 | * @param string $orientation _CHESS_ORIENTATION_ACTIVE, _CHESS_ORIENTATION_WHITE or _CHESS_ORIENTATION_BLACK |
||
| 906 | * @param string $user_color 'w', 'b' or '' (empty string indicates that current user is not a player in this game) |
||
| 907 | * @param bool $move_performed True if move was performed |
||
| 908 | * @param string $move_result_text Text describing move |
||
| 909 | * @param string $draw_claim_error_text Non-empty if draw claim invalid |
||
| 910 | * @param bool $show_arbiter_ctrl True if arbiter controls are to be displayed |
||
| 911 | */ |
||
| 912 | function chess_show_board($gamedata, $orientation, $user_color, $move_performed, $move_result_text = '', $draw_claim_error_text = '', $show_arbiter_ctrl = false) |
||
| 913 | { |
||
| 914 | global $xoopsTpl; |
||
| 915 | |||
| 916 | $xoopsTpl->assign( |
||
| 917 | 'xoops_module_header', |
||
| 918 | ' |
||
| 919 | <link rel="stylesheet" type="text/css" media="screen" href="' . XOOPS_URL . '/modules/chess/assets/css/style.css"> |
||
| 920 | ' |
||
| 921 | ); |
||
| 922 | |||
| 923 | $memberHandler = xoops_getHandler('member'); |
||
| 924 | |||
| 925 | $white_user = $memberHandler->getUser($gamedata['white_uid']); |
||
| 926 | |||
| 927 | $white_username = is_object($white_user) ? $white_user->getVar('uname') : '?'; |
||
| 928 | |||
| 929 | $black_user = $memberHandler->getUser($gamedata['black_uid']); |
||
| 930 | |||
| 931 | $black_username = is_object($black_user) ? $black_user->getVar('uname') : '?'; |
||
| 932 | |||
| 933 | // Determine whether board is flipped (black at bottom) or "normal" (white at bottom). |
||
| 934 | |||
| 935 | switch ($orientation) { |
||
| 936 | default: |
||
| 937 | case _CHESS_ORIENTATION_ACTIVE: |
||
| 938 | $flip = 'b' == $gamedata['fen_active_color']; |
||
| 939 | break; |
||
| 940 | case _CHESS_ORIENTATION_WHITE: |
||
| 941 | $flip = false; |
||
| 942 | break; |
||
| 943 | case _CHESS_ORIENTATION_BLACK: |
||
| 944 | $flip = true; |
||
| 945 | break; |
||
| 946 | } |
||
| 947 | |||
| 948 | // Convert fen_piece_placement string into 8x8-array $tiles. |
||
| 949 | |||
| 950 | $tiles = []; |
||
| 951 | |||
| 952 | $ranks = explode('/', $gamedata['fen_piece_placement']); |
||
| 953 | |||
| 954 | if ($flip) { |
||
| 955 | $ranks = array_reverse($ranks); |
||
| 956 | } |
||
| 957 | |||
| 958 | foreach ($ranks as $rank) { |
||
| 959 | $expanded_row = preg_replace_callback( |
||
| 960 | '/(\d)/', |
||
| 961 | |||
| 962 | static function ($matches) { |
||
| 963 | return str_repeat('x', $matches[1]); |
||
| 964 | }, |
||
| 965 | $rank |
||
| 966 | ); |
||
| 967 | |||
| 968 | $rank_tiles = preg_split('//', $expanded_row, -1, PREG_SPLIT_NO_EMPTY); |
||
| 969 | |||
| 970 | $tiles[] = $flip ? array_reverse($rank_tiles) : $rank_tiles; |
||
| 971 | } |
||
| 972 | |||
| 973 | #var_dump('tiles', $tiles);#*#DEBUG# |
||
| 974 | |||
| 975 | // Convert pgn_movetext into Nx3 array $movelist. |
||
| 976 | |||
| 977 | $movelist = chess_make_movelist($gamedata['pgn_movetext']); |
||
| 978 | |||
| 979 | static $file_labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; |
||
| 980 | |||
| 981 | if ($flip) { |
||
| 982 | $file_labels = array_reverse($file_labels); |
||
| 983 | } |
||
| 984 | |||
| 985 | $xoopsTpl->assign('chess_gamedata', $gamedata); |
||
| 986 | |||
| 987 | // comment at end of movetext |
||
| 988 | |||
| 989 | if (preg_match('/\{(.*?)\}\s*$/', $gamedata['pgn_movetext'], $matches)) { |
||
| 990 | $xoopsTpl->assign('chess_result_comment', $matches[1]); |
||
| 991 | } else { |
||
| 992 | $xoopsTpl->assign('chess_result_comment', ''); |
||
| 993 | } |
||
| 994 | |||
| 995 | $xoopsTpl->assign('chess_create_date', '0000-00-00 00:00:00' != $gamedata['create_date'] ? strtotime($gamedata['create_date']) : 0); |
||
| 996 | |||
| 997 | $xoopsTpl->assign('chess_start_date', '0000-00-00 00:00:00' != $gamedata['start_date'] ? strtotime($gamedata['start_date']) : 0); |
||
| 998 | |||
| 999 | $xoopsTpl->assign('chess_last_date', '0000-00-00 00:00:00' != $gamedata['last_date'] ? strtotime($gamedata['last_date']) : 0); |
||
| 1000 | |||
| 1001 | $xoopsTpl->assign('chess_white_user', $white_username); |
||
| 1002 | |||
| 1003 | $xoopsTpl->assign('chess_black_user', $black_username); |
||
| 1004 | |||
| 1005 | $xoopsTpl->assign('chess_tiles', $tiles); |
||
| 1006 | |||
| 1007 | $xoopsTpl->assign('chess_file_labels', $file_labels); |
||
| 1008 | |||
| 1009 | $xoopsTpl->assign( |
||
| 1010 | 'chess_pgn_string', |
||
| 1011 | chess_to_pgn_string( |
||
| 1012 | [ |
||
| 1013 | 'event' => '?', |
||
| 1014 | 'site' => $_SERVER['SERVER_NAME'], |
||
| 1015 | 'datetime' => $gamedata['start_date'], |
||
| 1016 | 'round' => '?', |
||
| 1017 | 'white' => $white_username, |
||
| 1018 | 'black' => $black_username, |
||
| 1019 | 'result' => $gamedata['pgn_result'], |
||
| 1020 | 'setup' => !empty($gamedata['pgn_fen']) ? 1 : 0, |
||
| 1021 | 'fen' => $gamedata['pgn_fen'], |
||
| 1022 | 'movetext' => $gamedata['pgn_movetext'], |
||
| 1023 | ] |
||
| 1024 | ) |
||
| 1025 | ); |
||
| 1026 | |||
| 1027 | $xoopsTpl->assign('chess_movelist', $movelist); |
||
| 1028 | |||
| 1029 | $xoopsTpl->assign('chess_date_format', _MEDIUMDATESTRING); |
||
| 1030 | |||
| 1031 | #if (empty($move_result_text)) {$move_result_text = 'test';}#*#DEBUG# |
||
| 1032 | |||
| 1033 | $xoopsTpl->assign('chess_move_performed', $move_performed); |
||
| 1034 | |||
| 1035 | $xoopsTpl->assign('chess_move_result', $move_result_text); |
||
| 1036 | |||
| 1037 | $xoopsTpl->assign('chess_draw_claim_error_text', $draw_claim_error_text); |
||
| 1038 | |||
| 1039 | $xoopsTpl->assign('chess_user_color', $user_color); |
||
| 1040 | |||
| 1041 | $xoopsTpl->assign('chess_confirm', $gamedata['w' == $user_color ? 'white_confirm' : 'black_confirm']); |
||
| 1042 | |||
| 1043 | $xoopsTpl->assign('chess_orientation', $orientation); |
||
| 1044 | |||
| 1045 | $xoopsTpl->assign('chess_rank_start', $flip ? 1 : 8); |
||
| 1046 | |||
| 1047 | $xoopsTpl->assign('chess_rank_direction', $flip ? 'up' : 'down'); |
||
| 1048 | |||
| 1049 | $xoopsTpl->assign('chess_file_start', $flip ? 8 : 1); |
||
| 1050 | |||
| 1051 | $xoopsTpl->assign('chess_file_direction', $flip ? 'down' : 'up'); |
||
| 1052 | |||
| 1053 | static $pieces = [ |
||
| 1054 | 'K' => ['color' => 'w', 'name' => 'wking', 'alt' => _MD_CHESS_ALT_WKING], |
||
| 1055 | 'Q' => ['color' => 'w', 'name' => 'wqueen', 'alt' => _MD_CHESS_ALT_WQUEEN], |
||
| 1056 | 'R' => ['color' => 'w', 'name' => 'wrook', 'alt' => _MD_CHESS_ALT_WROOK], |
||
| 1057 | 'B' => ['color' => 'w', 'name' => 'wbishop', 'alt' => _MD_CHESS_ALT_WBISHOP], |
||
| 1058 | 'N' => ['color' => 'w', 'name' => 'wknight', 'alt' => _MD_CHESS_ALT_WKNIGHT], |
||
| 1059 | 'P' => ['color' => 'w', 'name' => 'wpawn', 'alt' => _MD_CHESS_ALT_WPAWN], |
||
| 1060 | 'k' => ['color' => 'b', 'name' => 'bking', 'alt' => _MD_CHESS_ALT_BKING], |
||
| 1061 | 'q' => ['color' => 'b', 'name' => 'bqueen', 'alt' => _MD_CHESS_ALT_BQUEEN], |
||
| 1062 | 'r' => ['color' => 'b', 'name' => 'brook', 'alt' => _MD_CHESS_ALT_BROOK], |
||
| 1063 | 'b' => ['color' => 'b', 'name' => 'bbishop', 'alt' => _MD_CHESS_ALT_BBISHOP], |
||
| 1064 | 'n' => ['color' => 'b', 'name' => 'bknight', 'alt' => _MD_CHESS_ALT_BKNIGHT], |
||
| 1065 | 'p' => ['color' => 'b', 'name' => 'bpawn', 'alt' => _MD_CHESS_ALT_BPAWN], |
||
| 1066 | 'x' => ['color' => 'x', 'name' => 'empty', 'alt' => _MD_CHESS_ALT_EMPTY], |
||
| 1067 | ]; |
||
| 1068 | |||
| 1069 | $xoopsTpl->assign('chess_pieces', $pieces); |
||
| 1070 | |||
| 1071 | $xoopsTpl->assign('chess_show_arbitration_controls', $show_arbiter_ctrl); |
||
| 1072 | |||
| 1073 | if ($show_arbiter_ctrl && $gamedata['suspended']) { |
||
| 1074 | [$susp_date, $susp_uid, $susp_type, $susp_explain] = explode('|', $gamedata['suspended']); |
||
| 1075 | |||
| 1076 | switch ($susp_type) { |
||
| 1077 | case 'arbiter_suspend': |
||
| 1078 | $susp_type_display = _MD_CHESS_SUSP_TYPE_ARBITER; |
||
| 1079 | break; |
||
| 1080 | case 'want_arbitration': |
||
| 1081 | $susp_type_display = _MD_CHESS_SUSP_TYPE_PLAYER; |
||
| 1082 | break; |
||
| 1083 | default: |
||
| 1084 | $susp_type_display = _MD_CHESS_LABEL_ERROR; |
||
| 1085 | break; |
||
| 1086 | } |
||
| 1087 | |||
| 1088 | $susp_user = $memberHandler->getUser($susp_uid); |
||
| 1089 | |||
| 1090 | $susp_username = is_object($susp_user) ? $susp_user->getVar('uname') : _MD_CHESS_UNKNOWN; |
||
| 1091 | |||
| 1092 | $suspend_info = [ |
||
| 1093 | 'date' => strtotime($susp_date), |
||
| 1094 | 'user' => $susp_username, |
||
| 1095 | 'type' => $susp_type_display, |
||
| 1096 | 'reason' => $susp_explain, |
||
| 1097 | ]; |
||
| 1098 | |||
| 1099 | $xoopsTpl->assign('chess_suspend_info', $suspend_info); |
||
| 1100 | } |
||
| 1101 | |||
| 1102 | // Initialize $captured_pieces_all to indicate all pieces captured, then subtract off the pieces remaining on the board. |
||
| 1103 | |||
| 1104 | $captured_pieces_all = [ |
||
| 1105 | 'Q' => 1, |
||
| 1106 | 'R' => 2, |
||
| 1107 | 'B' => 2, |
||
| 1108 | 'N' => 2, |
||
| 1109 | 'P' => 8, |
||
| 1110 | 'q' => 1, |
||
| 1111 | 'r' => 2, |
||
| 1112 | 'b' => 2, |
||
| 1113 | 'n' => 2, |
||
| 1114 | 'p' => 8, |
||
| 1115 | ]; |
||
| 1116 | |||
| 1117 | for ($i = 0, $iMax = mb_strlen($gamedata['fen_piece_placement']); $i < $iMax; ++$i) { |
||
| 1118 | $piece = $gamedata['fen_piece_placement'][$i]; |
||
| 1119 | |||
| 1120 | if (!empty($captured_pieces_all[$piece])) { |
||
| 1121 | --$captured_pieces_all[$piece]; |
||
| 1122 | } |
||
| 1123 | } |
||
| 1124 | |||
| 1125 | // Construct lists of white's and black's captured pieces. |
||
| 1126 | |||
| 1127 | $captured_pieces = ['white' => [], 'black' => []]; |
||
| 1128 | |||
| 1129 | foreach ($captured_pieces_all as $piece => $count) { |
||
| 1130 | if ($count > 0) { |
||
| 1131 | if (ctype_upper($piece)) { |
||
| 1132 | $captured_pieces['white'] = array_merge($captured_pieces['white'], array_pad([], $count, $piece)); |
||
| 1133 | } elseif (ctype_lower($piece)) { |
||
| 1134 | $captured_pieces['black'] = array_merge($captured_pieces['black'], array_pad([], $count, $piece)); |
||
| 1135 | } |
||
| 1136 | } |
||
| 1137 | } |
||
| 1138 | |||
| 1139 | #var_dump('captured_pieces_all', $captured_pieces_all);#*#DEBUG# |
||
| 1140 | |||
| 1141 | #var_dump('captured_pieces', $captured_pieces);#*#DEBUG# |
||
| 1142 | |||
| 1143 | $xoopsTpl->assign('chess_captured_pieces', $captured_pieces); |
||
| 1144 | |||
| 1145 | $xoopsTpl->assign('chess_pawn_promote_choices', 'w' == $gamedata['fen_active_color'] ? ['Q', 'R', 'B', 'N'] : ['q', 'r', 'b', 'n']); |
||
| 1146 | |||
| 1147 | $xoopsTpl->assign('chess_allow_delete', chess_can_delete()); |
||
| 1148 | |||
| 1149 | // popup window contents for selecting piece to which pawn is promoted |
||
| 1150 | |||
| 1151 | // (Note that template is compiled here by fetch(), so any template variables it uses must already be defined.) |
||
| 1152 | |||
| 1153 | $xoopsTpl->assign('chess_pawn_promote_popup', $user_color ? $xoopsTpl->fetch('db:chess_game_promote_popup.tpl') : ''); |
||
| 1154 | |||
| 1155 | $xoopsTpl->assign('chess_ratings_enabled', 'none' != chess_moduleConfig('rating_system')); |
||
| 1156 | |||
| 1157 | // security token |
||
| 1158 | |||
| 1159 | $xoopsTpl->assign('chess_xoops_request_token', is_object($GLOBALS['xoopsSecurity']) ? $GLOBALS['xoopsSecurity']->getTokenHTML() : ''); |
||
| 1160 | } |
||
| 1161 | |||
| 1162 | ?> |
||
| 1163 |