Passed
Push — master ( b6c62e...3fdbae )
by Michael
07:59
created

game.php (17 issues)

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
require_once dirname(__DIR__, 2) . '/mainfile.php';
44
require_once XOOPS_ROOT_PATH . '/class/xoopsformloader.php';
45
46
$GLOBALS['xoopsOption']['template_main']                  = 'chess_game_main.tpl';
47
$xoopsConfig['module_cache'][$xoopsModule->getVar('mid')] = 0; // disable caching
48
require_once XOOPS_ROOT_PATH . '/header.php';
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) {
0 ignored issues
show
The condition false === $gamedata is always false.
Loading history...
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']);
0 ignored issues
show
The type XoopsModules\Chess\ChessGame was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
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*';
0 ignored issues
show
$xoopsUser is of type object, thus it always evaluated to true.
Loading history...
The assignment to $username is dead and can be removed.
Loading history...
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
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
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
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
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
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
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
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
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]);
0 ignored issues
show
The method triggerEvent() does not exist on XoopsObjectHandler. It seems like you code against a sub-type of XoopsObjectHandler such as XoopsNotificationHandler or XoopsPersistableObjectHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

443
        $notificationHandler->/** @scrutinizer ignore-call */ 
444
                              triggerEvent('game', $game_id, 'notify_game_move', ['CHESS_ACTION' => $notify]);
Loading history...
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);
0 ignored issues
show
The assignment to $chessgame is dead and can be removed.
Loading history...
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
$repeats was never initialized. Although not strictly required by PHP, it is generally a good practice to add $repeats = array(); before regardless.
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
760
        #*#DEBUG# - end
761
    }
762
763
    // Convert pgn_movetext into Nx3 array $movelist.
764
765
    $movelist = chess_make_movelist($gamedata['pgn_movetext']);
766
767
    #var_dump('movelist', $movelist);#*#DEBUG#
768
769
    // Compare board positions after each move with last board position.
770
771
    foreach ($movelist as $fullmove) {
772
        #echo "'{$fullmove[0]}' '{$fullmove[1]}' '{$fullmove[2]}'<br>\n";#*#DEBUG#
773
774
        if (CHESS_LOG_3FOLD) {
775
            #$log[] = "'{$fullmove[0]}' '{$fullmove[1]}' '{$fullmove[2]}'";#*#LOG_3FOLD# #*#DEBUG#
776
        }
777
778
        for ($i = 1; $i <= 2; ++$i) {
779
            if (!empty($fullmove[$i])) {
780
                $move = $fullmove[$i];
781
            } else {
782
                continue; // $fullmove[$i] can be empty if last move was white's, or if game was setup with black to move first.
783
            }
784
785
            // Remove check/checkmate annotation, if present.
786
787
            $move = str_replace('+', '', $move);
788
789
            $move = str_replace('#', '', $move);
790
791
            #echo "Performing move: '$move'<br>\n";#*#DEBUG#
792
793
            [$tmp_move_performed, $tmp_move_result_text] = $chessgame->move($move);
794
795
            #echo "Move result: '$tmp_move_result_text'<br>\n";#*#DEBUG#
796
797
            $tmp_move_performed or trigger_error("Failed to perform move $move: $tmp_move_result_text", E_USER_ERROR);
798
799
            $tmp_gamedata = $chessgame->gamestate();
800
801
            #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#
802
803
            $board_state = "{$tmp_gamedata['fen_piece_placement']} {$tmp_gamedata['fen_active_color']} {$tmp_gamedata['fen_castling_availability']} {$tmp_gamedata['fen_en_passant_target_square']}";
804
805
            if (CHESS_LOG_3FOLD) {
806
                $log[] = sprintf('%08x %03d%1s %s', crc32($board_state), $tmp_gamedata['fen_fullmove_number'], $tmp_gamedata['fen_active_color'], $board_state);
807
            }
808
809
            if ($tmp_gamedata['fen_fullmove_number'] != $last_move_number && $board_state == $last_board_state) {
810
                $repeats[] = $tmp_gamedata['fen_fullmove_number'] . $tmp_gamedata['fen_active_color'];
811
812
                #*#DEBUG# - start
813
814
                /***
815
                 * if (count($repeats) >= 3) {
816
                 * echo "*** Three repetitions! {$repeats[1]},{$repeats[2]},{$repeats[0]} ***<br>\n";
817
                 * } elseif (count($repeats) >= 2) {
818
                 * echo "*** Two repetitions!  {$repeats[1]},{$repeats[0]} ***<br>\n";
819
                 * }
820
                 ***/
821
822
                #*#DEBUG# - end
823
824
                if (count($repeats) >= 3) {
825
                    break 2;
826
                }
827
            }
828
        }
829
    }
830
831
    #*#DEBUG# - start
832
833
    /***
834
     * if (function_exists('posix_times')) {
835
     * $posix_times = posix_times();
836
     * var_dump('posix_times', $posix_times);
837
     * }
838
     * if (function_exists('getrusage')) {
839
     * $rusage = getrusage();
840
     * var_dump('rusage', $rusage);
841
     * }
842
     ***/
843
844
    #*#DEBUG# - end
845
846
    if (count($repeats) >= 3) {
847
        $draw_claim_valid = true;
848
849
        $draw_claim_text = sprintf(_MD_CHESS_DRAW_3, "{$repeats[1]},{$repeats[2]},{$repeats[0]}");
850
    } else {
851
        $draw_claim_valid = false;
852
853
        $draw_claim_text = _MD_CHESS_NO_DRAW_3;
854
    }
855
856
    if (CHESS_LOG_3FOLD) {
857
        $logfile = XOOPS_ROOT_PATH . '/cache/' . date('Ymd_His') . '_3fold.log';
858
859
        sort($log);
860
861
        error_log(implode("\n", $log), 3, $logfile);
862
    }
863
864
    return [$draw_claim_valid, $draw_claim_text];
865
}
866
867
/**
868
 * Convert pgn_movetext into Nx3 array.
869
 *
870
 * @param array $movetext pgn_movetext
871
 * @return array Nx3 array
872
 */
873
function chess_make_movelist($movetext)
874
{
875
    $movelist = [];
876
877
    $move_tokens = explode(' ', preg_replace('/\{.*\}/', '', $movetext));
0 ignored issues
show
preg_replace('/\{.*\}/', '', $movetext) of type string[] is incompatible with the type string expected by parameter $string of explode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

877
    $move_tokens = explode(' ', /** @scrutinizer ignore-type */ preg_replace('/\{.*\}/', '', $movetext));
Loading history...
878
879
    $index = -1;
880
881
    while ($move_tokens) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $move_tokens of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
882
        $move_token = array_shift($move_tokens);
883
884
        if (in_array($move_token, ['1-0', '0-1', '1/2-1/2', '*'])) {
885
            break;
886
        } elseif (preg_match('/^\d+(\.|\.\.\.)$/', $move_token, $matches)) {
887
            ++$index;
888
889
            $movelist[$index][] = $move_token;
890
891
            if ('...' == $matches[1]) { // setup-game with initial black move - add padding for white's move
892
                $movelist[$index][] = '';
893
            }
894
        } else {
895
            $movelist[$index][] = $move_token;
896
        }
897
    }
898
899
    return $movelist;
900
}
901
902
/**
903
 * Display chess board.
904
 *
905
 * @param array  $gamedata              Game data
906
 * @param string $orientation           _CHESS_ORIENTATION_ACTIVE, _CHESS_ORIENTATION_WHITE or _CHESS_ORIENTATION_BLACK
907
 * @param string $user_color            'w', 'b' or '' (empty string indicates that current user is not a player in this game)
908
 * @param bool   $move_performed        True if move was performed
909
 * @param string $move_result_text      Text describing move
910
 * @param string $draw_claim_error_text Non-empty if draw claim invalid
911
 * @param bool   $show_arbiter_ctrl     True if arbiter controls are to be displayed
912
 */
913
function chess_show_board($gamedata, $orientation, $user_color, $move_performed, $move_result_text = '', $draw_claim_error_text = '', $show_arbiter_ctrl = false)
914
{
915
    global $xoopsTpl;
916
917
    $xoopsTpl->assign(
918
        'xoops_module_header',
919
        '
920
        <link rel="stylesheet" type="text/css" media="screen" href="' . XOOPS_URL . '/modules/chess/assets/css/style.css">
921
    '
922
    );
923
924
    $memberHandler = xoops_getHandler('member');
925
926
    $white_user = $memberHandler->getUser($gamedata['white_uid']);
0 ignored issues
show
The method getUser() does not exist on XoopsObjectHandler. It seems like you code against a sub-type of XoopsObjectHandler such as XoopsAvatarHandler or XoopsPersistableObjectHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

926
    /** @scrutinizer ignore-call */ 
927
    $white_user = $memberHandler->getUser($gamedata['white_uid']);
Loading history...
927
928
    $white_username = is_object($white_user) ? $white_user->getVar('uname') : '?';
929
930
    $black_user = $memberHandler->getUser($gamedata['black_uid']);
931
932
    $black_username = is_object($black_user) ? $black_user->getVar('uname') : '?';
933
934
    // Determine whether board is flipped (black at bottom) or "normal" (white at bottom).
935
936
    switch ($orientation) {
937
        default:
938
        case _CHESS_ORIENTATION_ACTIVE:
939
            $flip = 'b' == $gamedata['fen_active_color'];
940
            break;
941
        case _CHESS_ORIENTATION_WHITE:
942
            $flip = false;
943
            break;
944
        case _CHESS_ORIENTATION_BLACK:
945
            $flip = true;
946
            break;
947
    }
948
949
    // Convert fen_piece_placement string into 8x8-array $tiles.
950
951
    $tiles = [];
952
953
    $ranks = explode('/', $gamedata['fen_piece_placement']);
954
955
    if ($flip) {
956
        $ranks = array_reverse($ranks);
957
    }
958
959
    foreach ($ranks as $rank) {
960
        $expanded_row = preg_replace_callback(
961
            '/(\d)/',
962
963
            static function ($matches) {
964
                return str_repeat('x', $matches[1]);
965
            },
966
            $rank
967
        );
968
969
        $rank_tiles = preg_split('//', $expanded_row, -1, PREG_SPLIT_NO_EMPTY);
970
971
        $tiles[] = $flip ? array_reverse($rank_tiles) : $rank_tiles;
0 ignored issues
show
It seems like $rank_tiles can also be of type false; however, parameter $array of array_reverse() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

971
        $tiles[] = $flip ? array_reverse(/** @scrutinizer ignore-type */ $rank_tiles) : $rank_tiles;
Loading history...
972
    }
973
974
    #var_dump('tiles', $tiles);#*#DEBUG#
975
976
    // Convert pgn_movetext into Nx3 array $movelist.
977
978
    $movelist = chess_make_movelist($gamedata['pgn_movetext']);
979
980
    static $file_labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
981
982
    if ($flip) {
983
        $file_labels = array_reverse($file_labels);
984
    }
985
986
    $xoopsTpl->assign('chess_gamedata', $gamedata);
987
988
    // comment at end of movetext
989
990
    if (preg_match('/\{(.*?)\}\s*$/', $gamedata['pgn_movetext'], $matches)) {
991
        $xoopsTpl->assign('chess_result_comment', $matches[1]);
992
    } else {
993
        $xoopsTpl->assign('chess_result_comment', '');
994
    }
995
996
    $xoopsTpl->assign('chess_create_date', '0000-00-00 00:00:00' != $gamedata['create_date'] ? strtotime($gamedata['create_date']) : 0);
997
998
    $xoopsTpl->assign('chess_start_date', '0000-00-00 00:00:00' != $gamedata['start_date'] ? strtotime($gamedata['start_date']) : 0);
999
1000
    $xoopsTpl->assign('chess_last_date', '0000-00-00 00:00:00' != $gamedata['last_date'] ? strtotime($gamedata['last_date']) : 0);
1001
1002
    $xoopsTpl->assign('chess_white_user', $white_username);
1003
1004
    $xoopsTpl->assign('chess_black_user', $black_username);
1005
1006
    $xoopsTpl->assign('chess_tiles', $tiles);
1007
1008
    $xoopsTpl->assign('chess_file_labels', $file_labels);
1009
1010
    $xoopsTpl->assign(
1011
        'chess_pgn_string',
1012
        chess_to_pgn_string(
1013
            [
1014
                'event'    => '?',
1015
                'site'     => $_SERVER['SERVER_NAME'],
1016
                'datetime' => $gamedata['start_date'],
1017
                'round'    => '?',
1018
                'white'    => $white_username,
1019
                'black'    => $black_username,
1020
                'result'   => $gamedata['pgn_result'],
1021
                'setup'    => !empty($gamedata['pgn_fen']) ? 1 : 0,
1022
                'fen'      => $gamedata['pgn_fen'],
1023
                'movetext' => $gamedata['pgn_movetext'],
1024
            ]
1025
        )
1026
    );
1027
1028
    $xoopsTpl->assign('chess_movelist', $movelist);
1029
1030
    $xoopsTpl->assign('chess_date_format', _MEDIUMDATESTRING);
1031
1032
    #if (empty($move_result_text)) {$move_result_text = 'test';}#*#DEBUG#
1033
1034
    $xoopsTpl->assign('chess_move_performed', $move_performed);
1035
1036
    $xoopsTpl->assign('chess_move_result', $move_result_text);
1037
1038
    $xoopsTpl->assign('chess_draw_claim_error_text', $draw_claim_error_text);
1039
1040
    $xoopsTpl->assign('chess_user_color', $user_color);
1041
1042
    $xoopsTpl->assign('chess_confirm', $gamedata['w' == $user_color ? 'white_confirm' : 'black_confirm']);
1043
1044
    $xoopsTpl->assign('chess_orientation', $orientation);
1045
1046
    $xoopsTpl->assign('chess_rank_start', $flip ? 1 : 8);
1047
1048
    $xoopsTpl->assign('chess_rank_direction', $flip ? 'up' : 'down');
1049
1050
    $xoopsTpl->assign('chess_file_start', $flip ? 8 : 1);
1051
1052
    $xoopsTpl->assign('chess_file_direction', $flip ? 'down' : 'up');
1053
1054
    static $pieces = [
1055
        'K' => ['color' => 'w', 'name' => 'wking', 'alt' => _MD_CHESS_ALT_WKING],
1056
        'Q' => ['color' => 'w', 'name' => 'wqueen', 'alt' => _MD_CHESS_ALT_WQUEEN],
1057
        'R' => ['color' => 'w', 'name' => 'wrook', 'alt' => _MD_CHESS_ALT_WROOK],
1058
        'B' => ['color' => 'w', 'name' => 'wbishop', 'alt' => _MD_CHESS_ALT_WBISHOP],
1059
        'N' => ['color' => 'w', 'name' => 'wknight', 'alt' => _MD_CHESS_ALT_WKNIGHT],
1060
        'P' => ['color' => 'w', 'name' => 'wpawn', 'alt' => _MD_CHESS_ALT_WPAWN],
1061
        'k' => ['color' => 'b', 'name' => 'bking', 'alt' => _MD_CHESS_ALT_BKING],
1062
        'q' => ['color' => 'b', 'name' => 'bqueen', 'alt' => _MD_CHESS_ALT_BQUEEN],
1063
        'r' => ['color' => 'b', 'name' => 'brook', 'alt' => _MD_CHESS_ALT_BROOK],
1064
        'b' => ['color' => 'b', 'name' => 'bbishop', 'alt' => _MD_CHESS_ALT_BBISHOP],
1065
        'n' => ['color' => 'b', 'name' => 'bknight', 'alt' => _MD_CHESS_ALT_BKNIGHT],
1066
        'p' => ['color' => 'b', 'name' => 'bpawn', 'alt' => _MD_CHESS_ALT_BPAWN],
1067
        'x' => ['color' => 'x', 'name' => 'empty', 'alt' => _MD_CHESS_ALT_EMPTY],
1068
    ];
1069
1070
    $xoopsTpl->assign('chess_pieces', $pieces);
1071
1072
    $xoopsTpl->assign('chess_show_arbitration_controls', $show_arbiter_ctrl);
1073
1074
    if ($show_arbiter_ctrl && $gamedata['suspended']) {
1075
        [$susp_date, $susp_uid, $susp_type, $susp_explain] = explode('|', $gamedata['suspended']);
1076
1077
        switch ($susp_type) {
1078
            case 'arbiter_suspend':
1079
                $susp_type_display = _MD_CHESS_SUSP_TYPE_ARBITER;
1080
                break;
1081
            case 'want_arbitration':
1082
                $susp_type_display = _MD_CHESS_SUSP_TYPE_PLAYER;
1083
                break;
1084
            default:
1085
                $susp_type_display = _MD_CHESS_LABEL_ERROR;
1086
                break;
1087
        }
1088
1089
        $susp_user = $memberHandler->getUser($susp_uid);
1090
1091
        $susp_username = is_object($susp_user) ? $susp_user->getVar('uname') : _MD_CHESS_UNKNOWN;
1092
1093
        $suspend_info = [
1094
            'date'   => strtotime($susp_date),
1095
            'user'   => $susp_username,
1096
            'type'   => $susp_type_display,
1097
            'reason' => $susp_explain,
1098
        ];
1099
1100
        $xoopsTpl->assign('chess_suspend_info', $suspend_info);
1101
    }
1102
1103
    // Initialize $captured_pieces_all to indicate all pieces captured, then subtract off the pieces remaining on the board.
1104
1105
    $captured_pieces_all = [
1106
        'Q' => 1,
1107
        'R' => 2,
1108
        'B' => 2,
1109
        'N' => 2,
1110
        'P' => 8,
1111
        'q' => 1,
1112
        'r' => 2,
1113
        'b' => 2,
1114
        'n' => 2,
1115
        'p' => 8,
1116
    ];
1117
1118
    for ($i = 0, $iMax = mb_strlen($gamedata['fen_piece_placement']); $i < $iMax; ++$i) {
1119
        $piece = $gamedata['fen_piece_placement'][$i];
1120
1121
        if (!empty($captured_pieces_all[$piece])) {
1122
            --$captured_pieces_all[$piece];
1123
        }
1124
    }
1125
1126
    // Construct lists of white's and black's captured pieces.
1127
1128
    $captured_pieces = ['white' => [], 'black' => []];
1129
1130
    foreach ($captured_pieces_all as $piece => $count) {
1131
        if ($count > 0) {
1132
            if (ctype_upper($piece)) {
1133
                $captured_pieces['white'] = array_merge($captured_pieces['white'], array_pad([], $count, $piece));
1134
            } elseif (ctype_lower($piece)) {
1135
                $captured_pieces['black'] = array_merge($captured_pieces['black'], array_pad([], $count, $piece));
1136
            }
1137
        }
1138
    }
1139
1140
    #var_dump('captured_pieces_all', $captured_pieces_all);#*#DEBUG#
1141
1142
    #var_dump('captured_pieces', $captured_pieces);#*#DEBUG#
1143
1144
    $xoopsTpl->assign('chess_captured_pieces', $captured_pieces);
1145
1146
    $xoopsTpl->assign('chess_pawn_promote_choices', 'w' == $gamedata['fen_active_color'] ? ['Q', 'R', 'B', 'N'] : ['q', 'r', 'b', 'n']);
1147
1148
    $xoopsTpl->assign('chess_allow_delete', chess_can_delete());
1149
1150
    // popup window contents for selecting piece to which pawn is promoted
1151
1152
    // (Note that template is compiled here by fetch(), so any template variables it uses must already be defined.)
1153
1154
    $xoopsTpl->assign('chess_pawn_promote_popup', $user_color ? $xoopsTpl->fetch('db:chess_game_promote_popup.tpl') : '');
1155
1156
    $xoopsTpl->assign('chess_ratings_enabled', 'none' != chess_moduleConfig('rating_system'));
1157
1158
    // security token
1159
1160
    $xoopsTpl->assign('chess_xoops_request_token', is_object($GLOBALS['xoopsSecurity']) ? $GLOBALS['xoopsSecurity']->getTokenHTML() : '');
1161
}
1162
1163
?>
0 ignored issues
show
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
1164