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 | |
42 | private const PGN_KEY_ACTION_ARROW = "ar"; |
43 | private const PGN_KEY_ACTION_HIGHLIGHT = "sq"; |
44 | private const PGN_KEY_ACTION_CLR_HIGHLIGHT = "csl"; |
45 | private const PGN_KEY_ACTION_CLR_ARROW = "cal"; |
46 | |
47 | private $moves = []; |
48 | private $moveReferences = []; |
49 | private $pointer = 0; |
50 | private $currentIndex = 0; |
51 | |
52 | public function __construct() { |
53 | $this->moveReferences[0] = &$this->moves; |
54 | } |
55 | |
56 | /** |
57 | * Add moves, separated by spaces |
58 | * |
59 | * @param string $moveString |
60 | */ |
61 | public function addMoves( $moveString ) { |
62 | $moves = explode( " ", $moveString ); |
63 | foreach ( $moves as $move ) { |
64 | $this->addMove( $move ); |
65 | } |
66 | } |
67 | |
68 | /** |
69 | * Add a single move |
70 | * |
71 | * @param string $move |
72 | */ |
73 | private function addMove( $move ) { |
74 | if ( !$this->isChessMove( $move ) ) { |
75 | return; |
76 | } |
77 | $move = preg_replace( "/^([a-h])([18])([QRNB])$/", "$1$2=$3", $move ); |
78 | $this->moveReferences[$this->pointer][] = [ ChessJson::MOVE_NOTATION => $move ]; |
79 | $this->currentIndex++; |
80 | } |
81 | |
82 | /** |
83 | * Check if a string is a valid chess move |
84 | * |
85 | * @param string $move |
86 | * @return bool |
87 | */ |
88 | private function isChessMove( $move ) { |
89 | if ( $move == '--' ) { |
90 | return true; |
91 | } |
92 | $regex = "/([PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](?:\=[PNBRQK])?|O(-?O){1,2})[\+#]?(\s*[\!\?]+)?/s"; |
93 | return preg_match( $regex, $move ); |
94 | } |
95 | |
96 | /** |
97 | * Insert a comment before the first move |
98 | * |
99 | * @param string $comment |
100 | */ |
101 | public function addCommentBeforeFirstMove( $comment ) { |
102 | $comment = trim( $comment ); |
103 | if ( !strlen( $comment ) ) { |
104 | return; |
105 | } |
106 | $this->moveReferences[$this->pointer][] = []; |
107 | $this->addComment( $comment ); |
108 | } |
109 | |
110 | /** |
111 | * Insert a comment at the current location |
112 | * |
113 | * @param string $comment |
114 | */ |
115 | public function addComment( $comment ) { |
116 | $comment = trim( $comment ); |
117 | if ( !strlen( $comment ) ) { |
118 | return; |
119 | } |
120 | # $index = max(0,count($this->moveReferences[$this->pointer])-1); |
121 | $index = count( $this->moveReferences[$this->pointer] ) - 1; |
122 | |
123 | if ( strstr( $comment, '[%clk' ) ) { |
124 | $clk = preg_replace( '/\[%clk\D*?([\d\:]+?)[\]]/si', '$1', $comment ); |
125 | $comment = str_replace( '[%clk ' . $clk . ']', '', $comment ); |
126 | $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_CLOCK] = $clk; |
127 | } |
128 | |
129 | $actions = $this->getActions( $comment ); |
130 | if ( $actions ) { |
131 | foreach ( $actions as $action ) { |
132 | $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_ACTIONS][] = $action; |
133 | } |
134 | } |
135 | |
136 | $comment = preg_replace( |
137 | '/\[%' |
138 | . self::PGN_KEY_ACTION_ARROW |
139 | . '[^\]]+?\]/si', '', $comment |
140 | ); |
141 | $comment = preg_replace( |
142 | '/\[%' |
143 | . self::PGN_KEY_ACTION_CLR_ARROW |
144 | . '[^\]]+?\]/si', '', $comment |
145 | ); |
146 | $comment = preg_replace( |
147 | '/\[%' |
148 | . self::PGN_KEY_ACTION_HIGHLIGHT |
149 | . '[^\]]+?\]/si', '', $comment |
150 | ); |
151 | $comment = preg_replace( |
152 | '/\[%' |
153 | . self::PGN_KEY_ACTION_CLR_HIGHLIGHT |
154 | . '[^\]]+?\]/si', '', $comment |
155 | ); |
156 | $comment = trim( $comment ); |
157 | |
158 | if ( $comment === '' ) { |
159 | return; |
160 | } |
161 | |
162 | if ( $index === -1 ) { |
163 | $index = 0; |
164 | $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_COMMENT] = $comment; |
165 | $this->currentIndex++; |
166 | } else { |
167 | $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_COMMENT] = $comment; |
168 | } |
169 | } |
170 | |
171 | /** |
172 | * getActions |
173 | * |
174 | * TODO document |
175 | * |
176 | * @param string $comment |
177 | * @return array |
178 | */ |
179 | private function getActions( $comment ) { |
180 | $ret = []; |
181 | if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_ARROW ) ) { |
182 | $arrow = preg_replace( |
183 | '/.*?\[%' |
184 | . self::PGN_KEY_ACTION_ARROW |
185 | . ' ([^\]]+?)\].*/si', '$1', $comment |
186 | ); |
187 | $arrows = explode( ",", $arrow ); |
188 | |
189 | foreach ( $arrows as $arrow ) { |
190 | $tokens = explode( ";", $arrow ); |
191 | if ( strlen( $tokens[0] ) == 4 ) { |
192 | $action = [ |
193 | 'from' => substr( $arrow, 0, 2 ), |
194 | 'to' => substr( $arrow, 2, 2 ), |
195 | 'type' => 'arrow' |
196 | ]; |
197 | if ( isset( $tokens[1] ) ) { |
198 | $action['color'] = $tokens[1]; |
199 | } |
200 | $ret[] = $action; |
201 | } |
202 | } |
203 | } |
204 | |
205 | if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_CLR_ARROW ) ) { |
206 | $arrow = preg_replace( |
207 | '/.*?\[%' |
208 | . self::PGN_KEY_ACTION_CLR_ARROW |
209 | . ' ([^\]]+?)\].*/si', '$1', $comment |
210 | ); |
211 | $arrows = explode( ",", $arrow ); |
212 | |
213 | foreach ( $arrows as $arrow ) { |
214 | $len = strlen( $arrow ); |
215 | $color = "G"; |
216 | if ( $len === 5 ) { |
217 | $color = substr( $arrow, 0, 1 ); |
218 | $arrow = substr( $arrow, 1 ); |
219 | |
220 | } |
221 | |
222 | if ( strlen( $arrow ) === 4 ) { |
223 | $action = [ |
224 | 'from' => substr( $arrow, 0, 2 ), |
225 | 'to' => substr( $arrow, 2, 2 ), |
226 | 'color' => $color, |
227 | 'type' => 'arrow', |
228 | ]; |
229 | $ret[] = $action; |
230 | } |
231 | } |
232 | } |
233 | |
234 | if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_HIGHLIGHT ) ) { |
235 | $arrow = preg_replace( |
236 | '/.*?\[%' |
237 | . self::PGN_KEY_ACTION_HIGHLIGHT |
238 | . ' ([^\]]+?)\].*/si', '$1', $comment |
239 | ); |
240 | $arrows = explode( ",", $arrow ); |
241 | |
242 | foreach ( $arrows as $arrow ) { |
243 | $tokens = explode( ";", $arrow ); |
244 | if ( strlen( $tokens[0] ) == 2 ) { |
245 | $action = [ |
246 | 'square' => substr( $arrow, 0, 2 ), |
247 | 'type' => 'highlight', |
248 | ]; |
249 | if ( isset( $tokens[1] ) ) { |
250 | $action["color"] = $tokens[1]; |
251 | } |
252 | $ret[] = $action; |
253 | } |
254 | } |
255 | } |
256 | |
257 | if ( strstr( $comment, '[%' . self::PGN_KEY_ACTION_CLR_HIGHLIGHT ) ) { |
258 | $arrow = preg_replace( |
259 | '/.*?\[%' |
260 | . self::PGN_KEY_ACTION_CLR_HIGHLIGHT |
261 | . ' ([^\]]+?)\].*/si', '$1', $comment |
262 | ); |
263 | $arrows = explode( ",", $arrow ); |
264 | |
265 | foreach ( $arrows as $arrow ) { |
266 | $color = "G"; |
267 | if ( strlen( $arrow ) === 3 ) { |
268 | $color = substr( $arrow, 0, 1 ); |
269 | $arrow = substr( $arrow, 1 ); |
270 | } |
271 | |
272 | if ( strlen( $arrow ) === 2 ) { |
273 | $action = [ |
274 | 'square' => substr( $arrow, 0, 2 ), |
275 | 'color' => $color, |
276 | 'type' => 'highlight', |
277 | ]; |
278 | $ret[] = $action; |
279 | } |
280 | } |
281 | } |
282 | |
283 | return $ret; |
284 | } |
285 | |
286 | /** |
287 | * Begin a variation at the current index |
288 | */ |
289 | public function startVariation() { |
290 | $index = count( $this->moveReferences[$this->pointer] ) - 1; |
291 | if ( !isset( $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_VARIATIONS] ) ) { |
292 | $this->moveReferences[$this->pointer][$index][ChessJson::MOVE_VARIATIONS] = []; |
293 | } |
294 | $moveVar = ChessJson::MOVE_VARIATIONS; |
295 | $countVars = count( $this->moveReferences[$this->pointer][$index][$moveVar] ); |
296 | $this->moveReferences[$this->pointer][$index][$moveVar][$countVars] = []; |
297 | // @phan-suppress-next-line PhanTypeArraySuspiciousNullable |
298 | $this->moveReferences[] =& $this->moveReferences[$this->pointer][$index][$moveVar][$countVars]; |
299 | $this->pointer++; |
300 | } |
301 | |
302 | /** |
303 | * End a variation |
304 | */ |
305 | public function endVariation() { |
306 | array_pop( $this->moveReferences ); |
307 | $this->pointer--; |
308 | } |
309 | |
310 | /** |
311 | * Get the moves |
312 | * |
313 | * @return array |
314 | */ |
315 | public function getMoves() { |
316 | return $this->moveReferences; |
317 | } |
318 | } |