chess_is_draw_threefold_repetition()   F
last analyzed

Complexity

Conditions 19
Paths 5184

Size

Total Lines 183
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 19
eloc 49
c 2
b 0
f 0
nc 5184
nop 1
dl 0
loc 183
rs 0.3499

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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) {
0 ignored issues
show
introduced by
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']);
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
introduced by
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
introduced by
$xoopsUser is of type object, thus it always evaluated to true.
Loading history...
Unused Code introduced by
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
introduced by
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
introduced by
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
introduced by
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
introduced by
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
Bug introduced by
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
Unused Code introduced by
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
        #*#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));
0 ignored issues
show
Bug introduced by
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

876
    $move_tokens = explode(' ', /** @scrutinizer ignore-type */ preg_replace('/\{.*\}/', '', $movetext));
Loading history...
877
878
    $index = -1;
879
880
    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...
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']);
0 ignored issues
show
Bug introduced by
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

925
    /** @scrutinizer ignore-call */ 
926
    $white_user = $memberHandler->getUser($gamedata['white_uid']);
Loading history...
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;
0 ignored issues
show
Bug introduced by
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

970
        $tiles[] = $flip ? array_reverse(/** @scrutinizer ignore-type */ $rank_tiles) : $rank_tiles;
Loading history...
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
?>
0 ignored issues
show
Best Practice introduced by
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...
1163