Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 146
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
MoveBuilder
0.00% covered (danger)
0.00%
0 / 146
0.00% covered (danger)
0.00%
0 / 10
1406
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addMoves
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 addMove
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 isChessMove
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 addCommentBeforeFirstMove
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 addComment
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
56
 getActions
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 1
306
 startVariation
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 endVariation
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getMoves
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This file is a part of ChessBrowser.
4 *
5 * ChessBrowser is free software: you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation, either version 3 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 *
18 * This file is a part of PgnParser
19 *
20 * PgnParser is free software: you can redistribute it and/or modify
21 *  it under the terms of the GNU Lesser General Public License as published by
22 *  the Free Software Foundation, either version 3 of the License, or
23 *  (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28 *  GNU Lesser General Public License for more details.
29 *
30 *  You should have received a copy of the GNU Lesser General Public License
31 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
32 *
33 * @file MoveBuilder
34 * @ingroup ChessBrowser
35 * @author Alf Magne Kalleland
36 */
37
38namespace MediaWiki\Extension\ChessBrowser\PgnParser;
39
40class MoveBuilder {
41    private const PGN_KEY_ACTION_ARROW = "ar";
42    private const PGN_KEY_ACTION_HIGHLIGHT = "sq";
43    private const PGN_KEY_ACTION_CLR_HIGHLIGHT = "csl";
44    private const PGN_KEY_ACTION_CLR_ARROW = "cal";
45
46    /** @var array */
47    private $moves = [];
48    /** @var array */
49    private $moveReferences = [];
50    /** @var int */
51    private $pointer = 0;
52    /** @var int */
53    private $currentIndex = 0;
54
55    public function __construct() {
56        $this->moveReferences[0] = &$this->moves;
57    }
58
59    /**
60     * Add moves, separated by spaces
61     *
62     * @param string $moveString
63     */
64    public function addMoves( $moveString ) {
65        $moves = explode( " ", $moveString );
66        foreach ( $moves as $move ) {
67            $this->addMove( $move );
68        }
69    }
70
71    /**
72     * Add a single move
73     *
74     * @param string $move
75     */
76    private function addMove( $move ) {
77        if ( !$this->isChessMove( $move ) ) {
78            return;
79        }
80        $move = preg_replace( "/^([a-h])([18])([QRNB])$/", "$1$2=$3", $move );
81        $this->moveReferences[$this->pointer][] = [ ChessJson::MOVE_NOTATION => $move ];
82        $this->currentIndex++;
83    }
84
85    /**
86     * Check if a string is a valid chess move
87     *
88     * @param string $move
89     * @return bool
90     */
91    private function isChessMove( $move ) {
92        if ( $move == '--' ) {
93            return true;
94        }
95        $regex = "/([PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?/s";
96        return preg_match( $regex, $move );
97    }
98
99    /**
100     * Insert a comment before the first move
101     *
102     * @param string $comment
103     */
104    public function addCommentBeforeFirstMove( $comment ) {
105        $comment = trim( $comment );
106        if ( !strlen( $comment ) ) {
107            return;
108        }
109        $this->moveReferences[$this->pointer][] = [];
110        $this->addComment( $comment );
111    }
112
113    /**
114     * Insert a comment at the current location
115     *
116     * @param string $comment
117     */
118    public function addComment( $comment ) {
119        $comment = trim( $comment );
120        if ( !strlen( $comment ) ) {
121            return;
122        }
123        # $index = max(0,count($this->moveReferences[$this->pointer])-1);
124        $index = count( $this->moveReferences[$this->pointer] ) - 1;
125
126        if ( strstr( $comment, '[%clk' ) ) {
127            $clk = preg_replace( '/\[%clk\D*?([\d\:]+?)[\]]/si', '$1', $comment );
128            $comment = str_replace( '[%clk ' . $clk . ']', '', $comment );
129            $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_CLOCK] = $clk;
130        }
131
132        $actions = $this->getActions( $comment );
133        if ( $actions ) {
134            foreach ( $actions as $action ) {
135                $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_ACTIONS][] = $action;
136            }
137        }
138
139        $comment = preg_replace(
140            '/\[%'
141            . self::PGN_KEY_ACTION_ARROW
142            . '[^\]]+?\]/si', '', $comment
143        );
144        $comment = preg_replace(
145            '/\[%'
146            . self::PGN_KEY_ACTION_CLR_ARROW
147            . '[^\]]+?\]/si', '', $comment
148        );
149        $comment = preg_replace(
150            '/\[%'
151            . self::PGN_KEY_ACTION_HIGHLIGHT
152            . '[^\]]+?\]/si', '', $comment
153        );
154        $comment = preg_replace(
155            '/\[%'
156            . self::PGN_KEY_ACTION_CLR_HIGHLIGHT
157            . '[^\]]+?\]/si', '', $comment
158        );
159        $comment = trim( $comment );
160
161        if ( $comment === '' ) {
162            return;
163        }
164
165        if ( $index === -1 ) {
166            $index = 0;
167            $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_COMMENT] = $comment;
168            $this->currentIndex++;
169        } else {
170            $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_COMMENT] = $comment;
171        }
172    }
173
174    /**
175     * getActions
176     *
177     * TODO document
178     *
179     * @param string $comment
180     * @return array
181     */
182    private function getActions( $comment ) {
183        $ret = [];
184        if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_ARROW ) ) {
185            $arrow = preg_replace(
186                '/.*?\[%'
187                . self::PGN_KEY_ACTION_ARROW
188                . ' ([^\]]+?)\].*/si', '$1', $comment
189            );
190            $arrows = explode( ",", $arrow );
191
192            foreach ( $arrows as $arrow ) {
193                $tokens = explode( ";", $arrow );
194                if ( strlen( $tokens[0] ) == 4 ) {
195                    $action = [
196                        'from' => substr( $arrow, 0, 2 ),
197                        'to' => substr( $arrow, 2, 2 ),
198                        'type' => 'arrow'
199                    ];
200                    if ( isset( $tokens[1] ) ) {
201                        $action['color'] = $tokens[1];
202                    }
203                    $ret[] = $action;
204                }
205            }
206        }
207
208        if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_CLR_ARROW ) ) {
209            $arrow = preg_replace(
210                '/.*?\[%'
211                . self::PGN_KEY_ACTION_CLR_ARROW
212                . ' ([^\]]+?)\].*/si', '$1', $comment
213            );
214            $arrows = explode( ",", $arrow );
215
216            foreach ( $arrows as $arrow ) {
217                $len = strlen( $arrow );
218                $color = "G";
219                if ( $len === 5 ) {
220                    $color = substr( $arrow, 0, 1 );
221                    $arrow = substr( $arrow, 1 );
222
223                }
224
225                if ( strlen( $arrow ) === 4 ) {
226                    $action = [
227                        'from' => substr( $arrow, 0, 2 ),
228                        'to' => substr( $arrow, 2, 2 ),
229                        'color' => $color,
230                        'type' => 'arrow',
231                    ];
232                    $ret[] = $action;
233                }
234            }
235        }
236
237        if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_HIGHLIGHT ) ) {
238            $arrow = preg_replace(
239                '/.*?\[%'
240                . self::PGN_KEY_ACTION_HIGHLIGHT
241                . ' ([^\]]+?)\].*/si', '$1', $comment
242            );
243            $arrows = explode( ",", $arrow );
244
245            foreach ( $arrows as $arrow ) {
246                $tokens = explode( ";", $arrow );
247                if ( strlen( $tokens[0] ) == 2 ) {
248                    $action = [
249                        'square' => substr( $arrow, 0, 2 ),
250                        'type' => 'highlight',
251                    ];
252                    if ( isset( $tokens[1] ) ) {
253                        $action["color"] = $tokens[1];
254                    }
255                    $ret[] = $action;
256                }
257            }
258        }
259
260        if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_CLR_HIGHLIGHT ) ) {
261            $arrow = preg_replace(
262                '/.*?\[%'
263                . self::PGN_KEY_ACTION_CLR_HIGHLIGHT
264                . ' ([^\]]+?)\].*/si', '$1', $comment
265            );
266            $arrows = explode( ",", $arrow );
267
268            foreach ( $arrows as $arrow ) {
269                $color = "G";
270                if ( strlen( $arrow ) === 3 ) {
271                    $color = substr( $arrow, 0, 1 );
272                    $arrow = substr( $arrow, 1 );
273                }
274
275                if ( strlen( $arrow ) === 2 ) {
276                    $action = [
277                        'square' => substr( $arrow, 0, 2 ),
278                        'color' => $color,
279                        'type' => 'highlight',
280                    ];
281                    $ret[] = $action;
282                }
283            }
284        }
285
286        return $ret;
287    }
288
289    /**
290     * Begin a variation at the current index
291     */
292    public function startVariation() {
293        $index = count( $this->moveReferences[$this->pointer] ) - 1;
294        if ( !isset( $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_VARIATIONS] ) ) {
295            $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_VARIATIONS] = [];
296        }
297        $moveVar = ChessJson::MOVE_VARIATIONS;
298        $countVars = count( $this->moveReferences[$this->pointer][$index][$moveVar] );
299        $this->moveReferences[$this->pointer][$index][$moveVar][$countVars] = [];
300        // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
301        $this->moveReferences[] =& $this->moveReferences[$this->pointer][$index][$moveVar][$countVars];
302        $this->pointer++;
303    }
304
305    /**
306     * End a variation
307     */
308    public function endVariation() {
309        array_pop( $this->moveReferences );
310        $this->pointer--;
311    }
312
313    /**
314     * Get the moves
315     *
316     * @return array
317     */
318    public function getMoves() {
319        return $this->moveReferences;
320    }
321}