Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 146 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
MoveBuilder | |
0.00% |
0 / 146 |
|
0.00% |
0 / 10 |
1406 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
addMoves | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
addMove | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
isChessMove | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
addCommentBeforeFirstMove | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
addComment | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
56 | |||
getActions | |
0.00% |
0 / 77 |
|
0.00% |
0 / 1 |
306 | |||
startVariation | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
endVariation | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getMoves | |
0.00% |
0 / 1 |
|
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 | |
38 | namespace MediaWiki\Extension\ChessBrowser\PgnParser; |
39 | |
40 | class 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 | } |