Commit | Line | Data |
---|---|---|
dbc79ee6 BA |
1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
2 | ||
3 | export class JangqiRules extends ChessRules { | |
4 | ||
5 | static get Monochrome() { | |
6 | return true; | |
7 | } | |
8 | ||
9 | static get Notoodark() { | |
10 | return true; | |
11 | } | |
12 | ||
13 | static get Lines() { | |
14 | let lines = []; | |
15 | // Draw all inter-squares lines, shifted: | |
16 | for (let i = 0; i < V.size.x; i++) | |
17 | lines.push([[i+0.5, 0.5], [i+0.5, V.size.y-0.5]]); | |
18 | for (let j = 0; j < V.size.y; j++) | |
19 | lines.push([[0.5, j+0.5], [V.size.x-0.5, j+0.5]]); | |
20 | // Add palaces: | |
21 | lines.push([[0.5, 3.5], [2.5, 5.5]]); | |
22 | lines.push([[0.5, 5.5], [2.5, 3.5]]); | |
23 | lines.push([[9.5, 3.5], [7.5, 5.5]]); | |
24 | lines.push([[9.5, 5.5], [7.5, 3.5]]); | |
25 | return lines; | |
26 | } | |
27 | ||
28 | // No castle, but flag: bikjang | |
29 | static get HasCastle() { | |
30 | return false; | |
31 | } | |
32 | ||
33 | static get HasEnpassant() { | |
34 | return false; | |
35 | } | |
36 | ||
37 | static get LoseOnRepetition() { | |
38 | return true; | |
39 | } | |
40 | ||
41 | static get ELEPHANT() { | |
42 | return "e"; | |
43 | } | |
44 | ||
45 | static get CANNON() { | |
46 | return "c"; | |
47 | } | |
48 | ||
49 | static get ADVISOR() { | |
50 | return "a"; | |
51 | } | |
52 | ||
53 | static get PIECES() { | |
54 | return [V.PAWN, V.ROOK, V.KNIGHT, V.ELEPHANT, V.ADVISOR, V.KING, V.CANNON]; | |
55 | } | |
56 | ||
57 | getPpath(b) { | |
58 | return "Jiangqi/" + b; | |
59 | } | |
60 | ||
61 | static get size() { | |
62 | return { x: 10, y: 9}; | |
63 | } | |
64 | ||
dbc79ee6 BA |
65 | static IsGoodFlags(flags) { |
66 | // bikjang status of last move + pass | |
67 | return !!flags.match(/^[0-2]{2,2}$/); | |
68 | } | |
69 | ||
70 | aggregateFlags() { | |
71 | return [this.bikjangFlag, this.passFlag]; | |
72 | } | |
73 | ||
74 | disaggregateFlags(flags) { | |
75 | this.bikjangFlag = flags[0]; | |
76 | this.passFlag = flags[1]; | |
77 | } | |
78 | ||
79 | getFlagsFen() { | |
80 | return this.bikjangFlag.toString() + this.passFlag.toString() | |
81 | } | |
82 | ||
83 | setFlags(fenflags) { | |
84 | this.bikjangFlag = parseInt(fenflags.charAt(0), 10); | |
85 | this.passFlag = parseInt(fenflags.charAt(1), 10); | |
86 | } | |
87 | ||
88 | setOtherVariables(fen) { | |
89 | super.setOtherVariables(fen); | |
90 | // Sub-turn is useful only at first move... | |
91 | this.subTurn = 1; | |
92 | } | |
93 | ||
94 | getPotentialMovesFrom([x, y]) { | |
95 | let moves = []; | |
96 | const c = this.getColor(x, y); | |
97 | const oppCol = V.GetOppCol(c); | |
98 | if (this.kingPos[c][0] == x && this.kingPos[c][1] == y) { | |
99 | // Add pass move (might be impossible if undercheck) | |
100 | moves.push( | |
101 | new Move({ | |
102 | appear: [], | |
103 | vanish: [], | |
104 | start: { x: this.kingPos[c][0], y: this.kingPos[c][1] }, | |
105 | end: { x: this.kingPos[oppCol][0], y: this.kingPos[oppCol][1] } | |
106 | }) | |
107 | ); | |
108 | } | |
109 | // TODO: next "if" is mutually exclusive with the block above | |
110 | if (this.movesCount <= 1) { | |
111 | const firstRank = (this.movesCount == 0 ? 9 : 0); | |
112 | const [initFile, destFile] = (this.subTurn == 1 ? [1, 2] : [7, 6]); | |
113 | // Only option is knight / elephant swap: | |
114 | if (x == firstRank && y == initFile) { | |
115 | moves.push( | |
116 | new Move({ | |
117 | appear: [ | |
118 | new PiPo({ | |
119 | x: x, | |
120 | y: destFile, | |
121 | c: c, | |
122 | p: V.KNIGHT | |
123 | }), | |
124 | new PiPo({ | |
125 | x: x, | |
126 | y: y, | |
127 | c: c, | |
128 | p: V.ELEPHANT | |
129 | }) | |
130 | ], | |
131 | vanish: [ | |
132 | new PiPo({ | |
133 | x: x, | |
134 | y: y, | |
135 | c: c, | |
136 | p: V.KNIGHT | |
137 | }), | |
138 | new PiPo({ | |
139 | x: x, | |
140 | y: destFile, | |
141 | c: c, | |
142 | p: V.ELEPHANT | |
143 | }) | |
144 | ], | |
145 | start: { x: x, y: y }, | |
146 | end: { x: x, y: destFile } | |
147 | }) | |
148 | ); | |
149 | } | |
150 | } | |
84ac89c2 BA |
151 | else { |
152 | let normalMoves = []; | |
153 | switch (this.getPiece(x, y)) { | |
154 | case V.PAWN: | |
155 | normalMoves = this.getPotentialPawnMoves([x, y]); | |
156 | break; | |
157 | case V.ROOK: | |
158 | normalMoves = this.getPotentialRookMoves([x, y]); | |
159 | break; | |
160 | case V.KNIGHT: | |
161 | normalMoves = this.getPotentialKnightMoves([x, y]); | |
162 | break; | |
163 | case V.ELEPHANT: | |
164 | normalMoves = this.getPotentialElephantMoves([x, y]); | |
165 | break; | |
166 | case V.ADVISOR: | |
167 | normalMoves = this.getPotentialAdvisorMoves([x, y]); | |
168 | break; | |
169 | case V.KING: | |
170 | normalMoves = this.getPotentialKingMoves([x, y]); | |
171 | break; | |
172 | case V.CANNON: | |
173 | normalMoves = this.getPotentialCannonMoves([x, y]); | |
174 | break; | |
175 | } | |
176 | Array.prototype.push.apply(moves, normalMoves); | |
177 | } | |
dbc79ee6 BA |
178 | return moves; |
179 | } | |
180 | ||
181 | getPotentialPawnMoves([x, y]) { | |
182 | const c = this.getColor(x, y); | |
183 | const oppCol = V.GetOppCol(c); | |
184 | const shiftX = (c == 'w' ? -1 : 1); | |
185 | const rank23 = (oppCol == 'w' ? [8, 7] : [1, 2]); | |
186 | let steps = [[shiftX, 0], [0, -1], [0, 1]]; | |
187 | // Diagonal moves inside enemy palace: | |
188 | if (y == 4 && x == rank23[0]) | |
189 | Array.prototype.push.apply(steps, [[shiftX, 1], [shiftX, -1]]); | |
190 | else if (x == rank23[1]) { | |
191 | if (y == 3) steps.push([shiftX, 1]); | |
192 | else if (y == 5) steps.push([shiftX, -1]); | |
193 | } | |
194 | return super.getSlideNJumpMoves([x, y], steps, "oneStep"); | |
195 | } | |
196 | ||
197 | knightStepsFromRookStep(step) { | |
198 | if (step[0] == 0) return [ [1, 2*step[1]], [-1, 2*step[1]] ]; | |
199 | return [ [2*step[0], 1], [2*step[0], -1] ]; | |
200 | } | |
201 | ||
202 | getPotentialKnightMoves([x, y]) { | |
203 | let steps = []; | |
204 | for (let rookStep of ChessRules.steps[V.ROOK]) { | |
205 | const [i, j] = [x + rookStep[0], y + rookStep[1]]; | |
206 | if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
207 | Array.prototype.push.apply(steps, | |
208 | // These moves might be impossible, but need to be checked: | |
209 | this.knightStepsFromRookStep(rookStep)); | |
210 | } | |
211 | } | |
212 | return super.getSlideNJumpMoves([x, y], steps, "oneStep"); | |
213 | } | |
214 | ||
215 | elephantStepsFromRookStep(step) { | |
216 | if (step[0] == 0) return [ [2, 3*step[1]], [-2, 3*step[1]] ]; | |
217 | return [ [3*step[0], 2], [3*step[0], -2] ]; | |
218 | } | |
219 | ||
220 | getPotentialElephantMoves([x, y]) { | |
221 | let steps = []; | |
222 | for (let rookStep of ChessRules.steps[V.ROOK]) { | |
223 | const eSteps = this.elephantStepsFromRookStep(rookStep); | |
224 | const [i, j] = [x + rookStep[0], y + rookStep[1]]; | |
225 | if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
226 | // Check second crossing: | |
227 | const knightSteps = this.knightStepsFromRookStep(rookStep); | |
228 | for (let k of [0, 1]) { | |
229 | const [ii, jj] = [x + knightSteps[k][0], y + knightSteps[k][1]]; | |
230 | if (V.OnBoard(ii, jj) && this.board[ii][jj] == V.EMPTY) | |
231 | steps.push(eSteps[k]); //ok: same ordering | |
232 | } | |
233 | } | |
234 | } | |
235 | return super.getSlideNJumpMoves([x, y], steps, "oneStep"); | |
236 | } | |
237 | ||
238 | palacePeopleMoves([x, y]) { | |
239 | const c = this.getColor(x, y); | |
240 | let steps = []; | |
241 | // Orthogonal steps: | |
242 | if (x < (c == 'w' ? 9 : 2)) steps.push([1, 0]); | |
243 | if (x > (c == 'w' ? 7 : 0)) steps.push([-1, 0]); | |
244 | if (y > 3) steps.push([0, -1]); | |
245 | if (y < 5) steps.push([0, 1]); | |
246 | // Diagonal steps, if in the middle or corner: | |
247 | if ( | |
248 | y != 4 && | |
249 | ( | |
250 | (c == 'w' && x != 8) || | |
251 | (c == 'b' && x != 1) | |
252 | ) | |
253 | ) { | |
254 | // In a corner: maximum one diagonal step available | |
255 | let step = null; | |
256 | const direction = (c == 'w' ? -1 : 1); | |
257 | if ((c == 'w' && x == 9) || (c == 'b' && x == 0)) { | |
258 | // On first line | |
259 | if (y == 3) step = [direction, 1]; | |
260 | else step = [direction, -1]; | |
261 | } | |
262 | else if ((c == 'w' && x == 7) || (c == 'b' && x == 2)) { | |
263 | // On third line | |
264 | if (y == 3) step = [-direction, 1]; | |
265 | else step = [-direction, -1]; | |
266 | } | |
267 | steps.push(step); | |
268 | } | |
269 | else if ( | |
270 | y == 4 && | |
271 | ( | |
272 | (c == 'w' && x == 8) || | |
273 | (c == 'b' && x == 1) | |
274 | ) | |
275 | ) { | |
276 | // At the middle: all directions available | |
277 | Array.prototype.push.apply(steps, ChessRules.steps[V.BISHOP]); | |
278 | } | |
279 | return super.getSlideNJumpMoves([x, y], steps, "oneStep"); | |
280 | } | |
281 | ||
282 | getPotentialAdvisorMoves(sq) { | |
283 | return this.palacePeopleMoves(sq); | |
284 | } | |
285 | ||
286 | getPotentialKingMoves(sq) { | |
287 | return this.palacePeopleMoves(sq); | |
288 | } | |
289 | ||
290 | getPotentialRookMoves([x, y]) { | |
291 | let moves = super.getPotentialRookMoves([x, y]); | |
292 | if ([3, 5].includes(y) && [0, 2, 7, 9].includes(x)) { | |
293 | // In a corner of a palace: move along diagonal | |
294 | const step = [[0, 7].includes(x) ? 1 : -1, 4 - y]; | |
295 | const oppCol = V.GetOppCol(this.getColor(x, y)); | |
296 | for (let i of [1, 2]) { | |
297 | const [xx, yy] = [x + i * step[0], y + i * step[1]]; | |
298 | if (this.board[xx][yy] == V.EMPTY) | |
299 | moves.push(this.getBasicMove([x, y], [xx, yy])); | |
300 | else { | |
301 | if (this.getColor(xx, yy) == oppCol) | |
302 | moves.push(this.getBasicMove([x, y], [xx, yy])); | |
303 | break; | |
304 | } | |
305 | } | |
306 | } | |
307 | else if (y == 4 && [1, 8].includes(x)) { | |
308 | // In the middle of a palace: 4 one-diagonal-step to check | |
309 | Array.prototype.push.apply( | |
310 | moves, | |
311 | super.getSlideNJumpMoves([x, y], | |
312 | ChessRules.steps[V.BISHOP], | |
313 | "oneStep") | |
314 | ); | |
315 | } | |
316 | return moves; | |
317 | } | |
318 | ||
319 | // NOTE: (mostly) duplicated from Shako (TODO?) | |
320 | getPotentialCannonMoves([x, y]) { | |
321 | const oppCol = V.GetOppCol(this.turn); | |
322 | let moves = []; | |
323 | // Look in every direction until an obstacle (to jump) is met | |
324 | for (const step of V.steps[V.ROOK]) { | |
325 | let i = x + step[0]; | |
326 | let j = y + step[1]; | |
327 | while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
328 | i += step[0]; | |
329 | j += step[1]; | |
330 | } | |
331 | // Then, search for an enemy (if jumped piece isn't a cannon) | |
332 | if (V.OnBoard(i, j) && this.getPiece(i, j) != V.CANNON) { | |
333 | i += step[0]; | |
334 | j += step[1]; | |
335 | while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
336 | moves.push(this.getBasicMove([x, y], [i, j])); | |
337 | i += step[0]; | |
338 | j += step[1]; | |
339 | } | |
340 | if ( | |
341 | V.OnBoard(i, j) && | |
342 | this.getColor(i, j) == oppCol && | |
343 | this.getPiece(i, j) != V.CANNON | |
344 | ) { | |
345 | moves.push(this.getBasicMove([x, y], [i, j])); | |
346 | } | |
347 | } | |
348 | } | |
349 | if ([3, 5].includes(y) && [0, 2, 7, 9].includes(x)) { | |
350 | // In a corner of a palace: hop over next obstacle if possible | |
351 | const step = [[0, 7].includes(x) ? 1 : -1, 4 - y]; | |
352 | const [x1, y1] = [x + step[0], y + step[1]]; | |
353 | const [x2, y2] = [x + 2 * step[0], y + 2 * step[1]]; | |
354 | if ( | |
355 | this.board[x1][y1] != V.EMPTY && | |
356 | this.getPiece(x1, y1) != V.CANNON && | |
357 | ( | |
358 | this.board[x2][y2] == V.EMPTY || | |
359 | ( | |
360 | this.getColor(x2, y2) == oppCol && | |
361 | this.getPiece(x2, y2) != V.CANNON | |
362 | ) | |
363 | ) | |
364 | ) { | |
365 | moves.push(this.getBasicMove([x, y], [x2, y2])); | |
366 | } | |
367 | } | |
368 | return moves; | |
369 | } | |
370 | ||
371 | // (King) Never attacked by advisor, since it stays in the palace | |
372 | isAttacked(sq, color) { | |
373 | return ( | |
374 | this.isAttackedByPawn(sq, color) || | |
375 | this.isAttackedByRook(sq, color) || | |
376 | this.isAttackedByKnight(sq, color) || | |
377 | this.isAttackedByElephant(sq, color) || | |
378 | this.isAttackedByCannon(sq, color) | |
379 | ); | |
380 | } | |
381 | ||
382 | onPalaceDiagonal([x, y]) { | |
383 | return ( | |
384 | (y == 4 && [1, 8].includes(x)) || | |
385 | ([3, 5].includes(y) && [0, 2, 7, 9].includes(x)) | |
386 | ); | |
387 | } | |
388 | ||
389 | isAttackedByPawn([x, y], color) { | |
390 | const shiftX = (color == 'w' ? 1 : -1); //shift from king | |
391 | if (super.isAttackedBySlideNJump( | |
392 | [x, y], color, V.PAWN, [[shiftX, 0], [0, 1], [0, -1]], "oneStep") | |
393 | ) { | |
394 | return true; | |
395 | } | |
396 | if (this.onPalaceDiagonal([x, y])) { | |
397 | for (let yStep of [-1, 1]) { | |
398 | const [xx, yy] = [x + shiftX, y + yStep]; | |
399 | if ( | |
400 | this.onPalaceDiagonal([xx,yy]) && | |
401 | this.board[xx][yy] != V.EMPTY && | |
402 | this.getColor(xx, yy) == color && | |
403 | this.getPiece(xx, yy) == V.PAWN | |
404 | ) { | |
405 | return true; | |
406 | } | |
407 | } | |
408 | } | |
409 | return false; | |
410 | } | |
411 | ||
412 | knightStepsFromBishopStep(step) { | |
413 | return [ [2*step[0], step[1]], [step[0], 2*step[1]] ]; | |
414 | } | |
415 | ||
416 | isAttackedByKnight([x, y], color) { | |
417 | // Check bishop steps: if empty, look continuation knight step | |
418 | let steps = []; | |
419 | for (let s of ChessRules.steps[V.BISHOP]) { | |
420 | const [i, j] = [x + s[0], y + s[1]]; | |
421 | if ( | |
422 | V.OnBoard(i, j) && | |
423 | this.board[i][j] == V.EMPTY | |
424 | ) { | |
425 | Array.prototype.push.apply(steps, this.knightStepsFromBishopStep(s)); | |
426 | } | |
427 | } | |
428 | return ( | |
429 | super.isAttackedBySlideNJump([x, y], color, V.KNIGHT, steps, "oneStep") | |
430 | ); | |
431 | } | |
432 | ||
433 | elephantStepsFromBishopStep(step) { | |
434 | return [ [3*step[0], 2*step[1]], [2*step[0], 3*step[1]] ]; | |
435 | } | |
436 | ||
437 | isAttackedByElephant([x, y], color) { | |
438 | // Check bishop steps: if empty, look continuation elephant step | |
439 | let steps = []; | |
440 | for (let s of ChessRules.steps[V.BISHOP]) { | |
441 | const [i1, j1] = [x + s[0], y + s[1]]; | |
442 | const [i2, j2] = [x + 2*s[0], y + 2*s[1]]; | |
443 | if ( | |
444 | V.OnBoard(i2, j2) && this.board[i2][j2] == V.EMPTY && | |
445 | V.OnBoard(i1, j1) && this.board[i1][j1] == V.EMPTY | |
446 | ) { | |
447 | Array.prototype.push.apply(steps, this.elephantStepsFromBishopStep(s)); | |
448 | } | |
449 | } | |
450 | return ( | |
451 | super.isAttackedBySlideNJump([x, y], color, V.ELEPHANT, steps, "oneStep") | |
452 | ); | |
453 | } | |
454 | ||
455 | isAttackedByRook([x, y], color) { | |
456 | if (super.isAttackedByRook([x, y], color)) return true; | |
457 | // Also check diagonals, if inside palace | |
458 | if (this.onPalaceDiagonal([x, y])) { | |
459 | // TODO: next scan is clearly suboptimal | |
460 | for (let s of ChessRules.steps[V.BISHOP]) { | |
461 | for (let i of [1, 2]) { | |
462 | const [xx, yy] = [x + i * s[0], y + i * s[1]]; | |
463 | if ( | |
464 | V.OnBoard(xx, yy) && | |
465 | this.onPalaceDiagonal([xx, yy]) | |
466 | ) { | |
467 | if (this.board[xx][yy] != V.EMPTY) { | |
468 | if ( | |
469 | this.getColor(xx, yy) == color && | |
470 | this.getPiece(xx, yy) == V.ROOK | |
471 | ) { | |
472 | return true; | |
473 | } | |
474 | break; | |
475 | } | |
476 | } | |
477 | else continue; | |
478 | } | |
479 | } | |
480 | } | |
481 | return false; | |
482 | } | |
483 | ||
484 | // NOTE: (mostly) duplicated from Shako (TODO?) | |
485 | isAttackedByCannon([x, y], color) { | |
486 | // Reversed process: is there an obstacle in line, | |
487 | // and a cannon next in the same line? | |
488 | for (const step of V.steps[V.ROOK]) { | |
489 | let [i, j] = [x+step[0], y+step[1]]; | |
490 | while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
491 | i += step[0]; | |
492 | j += step[1]; | |
493 | } | |
494 | if (V.OnBoard(i, j) && this.getPiece(i, j) != V.CANNON) { | |
495 | // Keep looking in this direction | |
496 | i += step[0]; | |
497 | j += step[1]; | |
498 | while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
499 | i += step[0]; | |
500 | j += step[1]; | |
501 | } | |
502 | if ( | |
503 | V.OnBoard(i, j) && | |
504 | this.getPiece(i, j) == V.CANNON && | |
505 | this.getColor(i, j) == color | |
506 | ) { | |
507 | return true; | |
508 | } | |
509 | } | |
510 | } | |
511 | return false; | |
512 | } | |
513 | ||
514 | getCurrentScore() { | |
515 | if ([this.bikjangFlag, this.passFlag].includes(2)) return "1/2"; | |
516 | const color = this.turn; | |
517 | // super.atLeastOneMove() does not consider passing (OK) | |
518 | if (this.underCheck(color) && !super.atLeastOneMove()) | |
519 | return (color == "w" ? "0-1" : "1-0"); | |
520 | return "*"; | |
521 | } | |
522 | ||
523 | static get VALUES() { | |
524 | return { | |
525 | p: 2, | |
526 | r: 13, | |
527 | n: 5, | |
528 | e: 3, | |
529 | a: 3, | |
530 | c: 7, | |
531 | k: 1000 | |
532 | }; | |
533 | } | |
534 | ||
535 | static get SEARCH_DEPTH() { | |
536 | return 2; | |
537 | } | |
538 | ||
539 | static GenRandInitFen() { | |
540 | // No randomization here (but initial setup choice) | |
541 | return ( | |
542 | "rnea1aenr/4k4/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/4K4/RNEA1AENR w 0 00" | |
543 | ); | |
544 | } | |
545 | ||
546 | play(move) { | |
547 | move.subTurn = this.subTurn; //much easier | |
548 | if (this.movesCount >= 2 || this.subTurn == 2 || move.vanish.length == 0) { | |
549 | this.turn = V.GetOppCol(this.turn); | |
550 | this.subTurn = 1; | |
551 | this.movesCount++; | |
552 | } | |
553 | else this.subTurn = 2; | |
554 | move.flags = JSON.stringify(this.aggregateFlags()); | |
555 | V.PlayOnBoard(this.board, move); | |
556 | this.postPlay(move); | |
557 | } | |
558 | ||
559 | postPlay(move) { | |
560 | if (move.vanish.length > 0) super.postPlay(move); | |
561 | else if (this.movesCount > 2) this.passFlag++; | |
562 | // Update bikjang flag | |
563 | if (this.kingPos['w'][1] == this.kingPos['b'][1]) { | |
564 | const y = this.kingPos['w'][1]; | |
565 | let bikjang = true; | |
566 | for (let x = this.kingPos['b'][0] + 1; x < this.kingPos['w'][0]; x++) { | |
567 | if (this.board[x][y] != V.EMPTY) { | |
568 | bikjang = false; | |
569 | break; | |
570 | } | |
571 | } | |
572 | if (bikjang) this.bikjangFlag++; | |
573 | else this.bikjangFlag = 0; | |
574 | } | |
575 | else this.bikjangFlag = 0; | |
576 | } | |
577 | ||
578 | undo(move) { | |
579 | this.disaggregateFlags(JSON.parse(move.flags)); | |
580 | V.UndoOnBoard(this.board, move); | |
581 | this.postUndo(move); | |
582 | if (this.movesCount >= 2 || this.subTurn == 1 || move.vanish.length == 0) { | |
583 | this.turn = V.GetOppCol(this.turn); | |
584 | this.movesCount--; | |
585 | } | |
586 | this.subTurn = move.subTurn; | |
587 | } | |
588 | ||
589 | postUndo(move) { | |
590 | if (move.vanish.length > 0) super.postUndo(move); | |
591 | } | |
592 | ||
593 | getNotation(move) { | |
594 | if (move.vanish.length == 0) return "pass"; | |
595 | if (move.appear.length == 2) return "S"; //"swap" | |
596 | let notation = super.getNotation(move); | |
597 | if (move.vanish.length == 2 && move.vanish[0].p == V.PAWN) | |
598 | notation = "P" + notation.substr(1); | |
599 | return notation; | |
600 | } | |
601 | ||
602 | }; |