Fix Musketeer, Joker, Shinobi. Start draft of Cwda
[vchess.git] / client / src / variants / Musketeer.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2 import { randInt } from "@/utils/alea";
3
4 export class MusketeerRules extends ChessRules {
5
6 // Extra pieces get strange letters because many taken by combinations below
7 static get LEOPARD() {
8 return "d";
9 }
10 static get CANNON() {
11 return "w";
12 }
13 static get UNICORN() {
14 return "x";
15 }
16 static get ELEPHANT() {
17 return "e";
18 }
19 static get HAWK() {
20 return "h";
21 }
22 static get FORTRESS() {
23 return "f";
24 }
25 static get SPIDER() {
26 return "y";
27 }
28
29 static get RESERVE_PIECES() {
30 return (
31 [V.LEOPARD, V.CANNON, V.UNICORN, V.ELEPHANT,
32 V.HAWK, V.FORTRESS, V.SPIDER]
33 );
34 }
35
36 static get PIECES() {
37 return ChessRules.PIECES.concat(V.RESERVE_PIECES);
38 }
39
40 // Decode if normal piece, or + piece1 or piece2
41 getPiece(i, j) {
42 if (i >= V.size.x) return V.RESERVE_PIECES[j];
43 const piece = this.board[i][j].charAt(1);
44 if (V.PIECES.includes(piece)) return piece;
45 // Augmented piece:
46 switch (piece) {
47 case 'a':
48 case 'c':
49 return 'b';
50 case 'j':
51 case 'l':
52 return 'k';
53 case 'm':
54 case 'o':
55 return 'n';
56 case 's':
57 case 't':
58 return 'q';
59 case 'u':
60 case 'v':
61 return 'r';
62 }
63 }
64
65 getColor(i, j) {
66 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
67 return this.board[i][j].charAt(0);
68 }
69
70 // Code: a/c = bishop + piece1/piece2 j/l for king,
71 // m/o for knight, s/t for queen, u/v for rook
72 static get AUGMENTED_PIECES() {
73 return [
74 'a',
75 'c',
76 'j',
77 'l',
78 'm',
79 'o',
80 's',
81 't',
82 'u',
83 'v'
84 ];
85 }
86
87 getPpath(b) {
88 return (ChessRules.PIECES.includes(b[1]) ? "" : "Musketeer/") + b;
89 }
90
91 getReservePpath(index, color) {
92 return "Musketeer/" + color + V.RESERVE_PIECES[index];
93 }
94
95 // Decode above notation into additional piece
96 getExtraPiece(symbol) {
97 if (['a','j','m','s','u'].includes(symbol))
98 return this.extraPieces[0];
99 return this.extraPieces[1];
100 }
101
102 // Inverse operation: augment piece
103 getAugmented(piece) {
104 const p1 = [2, 3].includes(this.movesCount);
105 switch (piece) {
106 case V.ROOK: return (p1 ? 'u' : 'v');
107 case V.KNIGHT: return (p1 ? 'm' : 'o');
108 case V.BISHOP: return (p1 ? 'a' : 'c');
109 case V.QUEEN: return (p1 ? 's' : 't');
110 case V.KING: return (p1 ? 'j' : 'l');
111 }
112 return '_'; //never reached
113 }
114
115 static IsGoodFen(fen) {
116 if (!ChessRules.IsGoodFen(fen)) return false;
117 const fenParsed = V.ParseFen(fen);
118 // 5) Check extra pieces
119 if (!fenParsed.extraPieces) return false;
120 // Not exact matching (would need to look at movesCount), but OK for now
121 if (!fenParsed.extraPieces.match(/^[dwxejfy-]{2,2}$/)) return false;
122 return true;
123 }
124
125 static IsGoodPosition(position) {
126 if (position.length == 0) return false;
127 const rows = position.split("/");
128 if (rows.length != V.size.x) return false;
129 let kings = { "w": 0, "b": 0 };
130 const allPiecesCodes = V.PIECES.concat(V.AUGMENTED_PIECES);
131 const kingBlackCodes = ['j','k','l'];
132 const kingWhiteCodes = ['J','K','L'];
133 for (let row of rows) {
134 let sumElts = 0;
135 for (let i = 0; i < row.length; i++) {
136 if (kingBlackCodes.includes(row[i])) kings['b']++;
137 else if (kingWhiteCodes.includes(row[i])) kings['w']++;
138 if (allPiecesCodes.includes(row[i].toLowerCase())) sumElts++;
139 else {
140 const num = parseInt(row[i], 10);
141 if (isNaN(num)) return false;
142 sumElts += num;
143 }
144 }
145 if (sumElts != V.size.y) return false;
146 }
147 // Both kings should be on board, only one of each color:
148 if (Object.values(kings).some(v => v != 1)) return false;
149 return true;
150 }
151
152 static ParseFen(fen) {
153 const fenParts = fen.split(" ");
154 return Object.assign(
155 ChessRules.ParseFen(fen),
156 { extraPieces: fenParts[5] }
157 );
158 }
159
160 static GenRandInitFen(randomness) {
161 return ChessRules.GenRandInitFen(randomness) + " --";
162 }
163
164 getFen() {
165 return super.getFen() + " " + this.extraPieces.join("");
166 }
167
168 setOtherVariables(fen) {
169 super.setOtherVariables(fen);
170 // Extra pieces may not be defined yet (thus '-')
171 this.extraPieces = V.ParseFen(fen).extraPieces.split("");
172 // At early stages, also init reserves
173 if (this.movesCount <= 5) {
174 const condShow = (piece) => {
175 if (this.movesCount == 0) return true;
176 if (this.movesCount == 1) return piece != this.extraPieces[0];
177 if (this.movesCount <= 3) return this.extraPiece.includes(piece);
178 return this.extraPiece[1] == piece;
179 }
180 this.reserve = { w : {}, b: {} };
181 for (let c of ['w', 'b']) {
182 V.RESERVE_PIECES.forEach(p =>
183 this.reserve[c][p] = condShow(p) ? 1 : 0);
184 }
185 }
186 }
187
188 // Kings may be augmented:
189 scanKings(fen) {
190 this.kingPos = { w: [-1, -1], b: [-1, -1] };
191 const rows = V.ParseFen(fen).position.split("/");
192 for (let i = 0; i < rows.length; i++) {
193 let k = 0; //column index on board
194 for (let j = 0; j < rows[i].length; j++) {
195 const piece = rows[i].charAt(j);
196 if (['j','k','l'].includes(piece.toLowerCase())) {
197 const color = (piece.charCodeAt(0) <= 90 ? 'w' : 'b');
198 this.kingPos[color] = [i, k];
199 }
200 else {
201 const num = parseInt(rows[i].charAt(j), 10);
202 if (!isNaN(num)) k += num - 1;
203 }
204 k++;
205 }
206 }
207 }
208
209 getReserveMoves([x, y]) {
210 const color = this.turn;
211 const p = V.RESERVE_PIECES[y];
212 if (
213 this.reserve[color][p] == 0 ||
214 ([2, 3].includes(this.movesCount) && p != this.extraPieces[0]) ||
215 ([4, 5].includes(this.movesCount) && p != this.extraPieces[1])
216 ) {
217 return [];
218 }
219 let moves = [];
220 const iIdx =
221 (this.movesCount <= 1 ? [2, 3, 4, 5] : [color == 'w' ? 7 : 0]);
222 const mappingAppear = [ [3, 4], [3, 3], [4, 4], [4, 3] ];
223 for (let i of iIdx) {
224 for (let j = 0; j < V.size.y; j++) {
225 if (
226 (this.movesCount <= 1 && this.board[i][j] == V.EMPTY) ||
227 (
228 this.movesCount >= 2 &&
229 ChessRules.PIECES.includes(this.board[i][j].charAt(1))
230 )
231 ) {
232 const [appearX, appearY] =
233 this.movesCount <= 1
234 ? mappingAppear[this.movesCount]
235 : [i, j];
236 const pOnBoard =
237 (this.movesCount >= 2 ? this.board[i][j].charAt(1) : '');
238 let mv = new Move({
239 appear: [
240 new PiPo({
241 x: appearX,
242 y: appearY,
243 c: color,
244 p: (this.movesCount <= 1 ? p : this.getAugmented(pOnBoard))
245 })
246 ],
247 vanish: [],
248 start: { x: x, y: y }, //a bit artificial...
249 end: { x: i, y: j }
250 });
251 if (this.movesCount >= 2)
252 mv.vanish.push(new PiPo({ x: i, y: j, c: color, p: pOnBoard }))
253 moves.push(mv);
254 }
255 }
256 }
257 return moves;
258 }
259
260 // Assumption: movesCount >= 6
261 getPotentialMovesFrom([x, y]) {
262 // Standard moves. If piece not in usual list, new piece appears.
263 const initialPiece = this.getPiece(x, y);
264 if (V.RESERVE_PIECES.includes(initialPiece)) {
265 switch (initialPiece) {
266 case V.LEOPARD: return this.getPotentialLeopardMoves([x, y]);
267 case V.CANNON: return this.getPotentialCannonMoves([x, y]);
268 case V.UNICORN: return this.getPotentialUnicornMoves([x, y]);
269 case V.ELEPHANT: return this.getPotentialElephantMoves([x, y]);
270 case V.HAWK: return this.getPotentialHawkMoves([x, y]);
271 case V.FORTRESS: return this.getPotentialFortressMoves([x, y]);
272 case V.SPIDER: return this.getPotentialSpiderMoves([x, y]);
273 }
274 return []; //never reached
275 }
276 // Following is mostly copy-paste from Titan Chess (TODO?)
277 let moves = [];
278 if (initialPiece == V.PAWN) {
279 const promotions =
280 ChessRules.PawnSpecs.promotions.concat(this.extraPieces);
281 moves = super.getPotentialPawnMoves([x, y], promotions);
282 }
283 else moves = super.getPotentialMovesFrom([x, y]);
284 const color = this.turn;
285 if (
286 ((color == 'w' && x == 7) || (color == "b" && x == 0)) &&
287 V.AUGMENTED_PIECES.includes(this.board[x][y][1])
288 ) {
289 const newPiece = this.getExtraPiece(this.board[x][y][1]);
290 moves.forEach(m => {
291 m.appear[0].p = initialPiece;
292 m.appear.push(
293 new PiPo({
294 p: newPiece,
295 c: color,
296 x: x,
297 y: y
298 })
299 );
300 });
301 moves.forEach(m => {
302 if (m.vanish.length <= 1) return;
303 const [vx, vy] = [m.vanish[1].x, m.vanish[1].y];
304 if (
305 m.appear.length >= 2 && //3 if the king was also augmented
306 m.vanish.length == 2 &&
307 m.vanish[1].c == color &&
308 V.AUGMENTED_PIECES.includes(this.board[vx][vy][1])
309 ) {
310 // Castle, rook is an "augmented piece"
311 m.appear[1].p = V.ROOK;
312 m.appear.push(
313 new PiPo({
314 p: this.getExtraPiece(this.board[vx][vy][1]),
315 c: color,
316 x: vx,
317 y: vy
318 })
319 );
320 }
321 });
322 }
323 return moves;
324 }
325
326 getSlideNJumpMoves([x, y], steps, nbSteps) {
327 let moves = [];
328 outerLoop: for (let step of steps) {
329 let i = x + step[0];
330 let j = y + step[1];
331 let stepCounter = 0;
332 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
333 moves.push(this.getBasicMove([x, y], [i, j]));
334 stepCounter++;
335 if (
336 !!nbSteps &&
337 // Next condition to remain compatible with super method
338 (stepCounter >= nbSteps || isNaN(parseInt(nbSteps, 10)))
339 ) {
340 continue outerLoop;
341 }
342 i += step[0];
343 j += step[1];
344 }
345 if (V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
346 moves.push(this.getBasicMove([x, y], [i, j]));
347 }
348 return moves;
349 }
350
351 // All types of leaps used here:
352 static get Leap2Ortho() {
353 return [ [-2, 0], [0, -2], [2, 0], [0, 2] ];
354 }
355 static get Leap2Diago() {
356 return [ [-2, -2], [-2, 2], [2, -2], [2, 2] ];
357 }
358 static get Leap3Ortho() {
359 return [ [-3, 0], [0, -3], [3, 0], [0, 3] ];
360 }
361 static get Leap3Diago() {
362 return [ [-3, -3], [-3, 3], [3, -3], [3, 3] ];
363 }
364 static get CamelSteps() {
365 return [
366 [-3, -1], [-3, 1], [-1, -3], [-1, 3],
367 [1, -3], [1, 3], [3, -1], [3, 1]
368 ];
369 }
370 static get VerticalKnight() {
371 return [ [-2, -1], [-2, 1], [2, -1], [2, 1] ];
372 }
373 static get HorizontalKnight() {
374 return [ [-1, -2], [-1, 2], [1, -2], [1, 2] ];
375 }
376
377 getPotentialLeopardMoves(sq) {
378 return (
379 this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 2)
380 .concat(super.getPotentialKnightMoves(sq))
381 );
382 }
383
384 getPotentialCannonMoves(sq) {
385 const steps =
386 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
387 .concat(V.Leap2Ortho).concat(V.HorizontalKnight);
388 return super.getSlideNJumpMoves(sq, steps, "oneStep");
389 }
390
391 getPotentialUnicornMoves(sq) {
392 return (
393 super.getPotentialKnightMoves(sq)
394 .concat(super.getSlideNJumpMoves(sq, V.CamelSteps, "oneStep"))
395 );
396 }
397
398 getPotentialElephantMoves(sq) {
399 const steps =
400 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
401 .concat(V.Leap2Ortho)
402 .concat(V.Leap2Diago);
403 return super.getSlideNJumpMoves(sq, steps, "oneStep");
404 }
405
406 getPotentialHawkMoves(sq) {
407 const steps =
408 V.Leap2Ortho.concat(V.Leap2Diago)
409 .concat(V.Leap3Ortho).concat(V.Leap3Diago);
410 return super.getSlideNJumpMoves(sq, steps, "oneStep");
411 }
412
413 getPotentialFortressMoves(sq) {
414 const steps = V.Leap2Ortho.concat(V.VerticalKnight)
415 return (
416 super.getSlideNJumpMoves(sq, steps, "oneStep")
417 .concat(this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 3))
418 );
419 }
420
421 getPotentialSpiderMoves(sq) {
422 const steps = V.Leap2Ortho.concat(V.steps[V.KNIGHT])
423 return (
424 super.getSlideNJumpMoves(sq, steps, "oneStep")
425 .concat(this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 2))
426 );
427 }
428
429 getPossibleMovesFrom([x, y]) {
430 if (this.movesCount <= 5)
431 return (x >= V.size.x ? this.getReserveMoves([x, y]) : []);
432 return super.getPossibleMovesFrom([x, y]);
433 }
434
435 getAllValidMoves() {
436 if (this.movesCount >= 6) return super.getAllValidMoves();
437 let moves = [];
438 const color = this.turn;
439 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
440 moves = moves.concat(
441 this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
442 );
443 }
444 return moves;
445 }
446
447 atLeastOneMove() {
448 if (this.movesCount <= 5) return true;
449 return super.atLeastOneMove();
450 }
451
452 isAttacked(sq, color) {
453 if (super.isAttacked(sq, color)) return true;
454 if (
455 this.extraPieces.includes(V.LEOPARD) &&
456 this.isAttackedByLeopard(sq, color)
457 ) {
458 return true;
459 }
460 if (
461 this.extraPieces.includes(V.CANNON) &&
462 this.isAttackedByCannon(sq, color)
463 ) {
464 return true;
465 }
466 if (
467 this.extraPieces.includes(V.UNICORN) &&
468 this.isAttackedByUnicorn(sq, color)
469 ) {
470 return true;
471 }
472 if (
473 this.extraPieces.includes(V.ELEPHANT) &&
474 this.isAttackedByElephant(sq, color)
475 ) {
476 return true;
477 }
478 if (
479 this.extraPieces.includes(V.HAWK) &&
480 this.isAttackedByHawk(sq, color)
481 ) {
482 return true;
483 }
484 if (
485 this.extraPieces.includes(V.FORTRESS) &&
486 this.isAttackedByFortress(sq, color)
487 ) {
488 return true;
489 }
490 if (
491 this.extraPieces.includes(V.SPIDER) &&
492 this.isAttackedBySpider(sq, color)
493 ) {
494 return true;
495 }
496 return false;
497 }
498
499 // Modify because of the limiyted steps options of some of the pieces here
500 isAttackedBySlideNJump([x, y], color, piece, steps, nbSteps) {
501 if (!!nbSteps && isNaN(parseInt(nbSteps, 10))) nbSteps = 1;
502 for (let step of steps) {
503 let rx = x + step[0],
504 ry = y + step[1];
505 let stepCounter = 1;
506 while (
507 V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY &&
508 (!nbSteps || stepCounter < nbSteps)
509 ) {
510 rx += step[0];
511 ry += step[1];
512 stepCounter++;
513 }
514 if (
515 V.OnBoard(rx, ry) &&
516 this.board[rx][ry] != V.EMPTY &&
517 this.getPiece(rx, ry) == piece &&
518 this.getColor(rx, ry) == color
519 ) {
520 return true;
521 }
522 }
523 return false;
524 }
525
526 isAttackedByLeopard(sq, color) {
527 return (
528 super.isAttackedBySlideNJump(
529 sq, color, V.LEOPARD, V.steps[V.KNIGHT], "oneStep") ||
530 this.isAttackedBySlideNJump(sq, color, V.LEOPARD, V.steps[V.BISHOP], 2)
531 );
532 }
533
534 isAttackedByCannon(sq, color) {
535 const steps =
536 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
537 .concat(V.Leap2Ortho).concat(V.HorizontalKnight);
538 return super.isAttackedBySlideNJump(sq, color, V.CANNON, steps, "oneStep");
539 }
540
541 isAttackedByUnicorn(sq, color) {
542 const steps = V.steps[V.KNIGHT].concat(V.CamelSteps)
543 return (
544 super.isAttackedBySlideNJump(sq, color, V.UNICORN, steps, "oneStep")
545 );
546 }
547
548 isAttackedByElephant(sq, color) {
549 const steps =
550 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
551 .concat(V.Leap2Ortho)
552 .concat(V.Leap2Diago);
553 return (
554 super.isAttackedBySlideNJump(sq, color, V.ELEPHANT, steps, "oneStep")
555 );
556 }
557
558 isAttackedByHawk(sq, color) {
559 const steps =
560 V.Leap2Ortho.concat(V.Leap2Diago)
561 .concat(V.Leap3Ortho).concat(V.Leap3Diago);
562 return super.isAttackedBySlideNJump(sq, color, V.HAWK, steps, "oneStep");
563 }
564
565 isAttackedByFortress(sq, color) {
566 const steps = V.Leap2Ortho.concat(V.VerticalKnight)
567 return (
568 super.isAttackedBySlideNJump(sq, color, V.FORTRESS, steps, "oneStep") ||
569 this.isAttackedBySlideNJump(sq, color, V.FORTRESS, V.steps[V.BISHOP], 3)
570 );
571 }
572
573 isAttackedBySpider(sq, color) {
574 const steps = V.Leap2Ortho.concat(V.steps[V.KNIGHT])
575 return (
576 super.isAttackedBySlideNJump(sq, color, V.SPIDER, steps, "oneStep") ||
577 this.isAttackedBySlideNJump(sq, color, V.SPIDER, V.steps[V.BISHOP], 2)
578 );
579 }
580
581 getCheckSquares() {
582 if (this.movesCount <= 6) return [];
583 return super.getCheckSquares();
584 }
585
586 // At movesCount == 0,1: show full reserves [minus chosen piece1]
587 // At movesCount == 2,3: show reserve with only 2 selected pieces
588 // At movesCount == 4,5: show reserve with only piece2
589 // Then, no reserve.
590 postPlay(move) {
591 if (this.movesCount > 6) super.postPlay(move);
592 else {
593 switch (this.movesCount) {
594 case 1:
595 this.reserve['w'][move.appear[0].p]--;
596 this.reserve['b'][move.appear[0].p]--;
597 this.extraPieces[0] = move.appear[0].p;
598 break;
599 case 2:
600 this.extraPieces[1] = move.appear[0].p;
601 for (let p of V.RESERVE_PIECES) {
602 const resVal = (this.extraPieces.includes(p) ? 1 : 0);
603 this.reserve['w'][p] = resVal;
604 this.reserve['b'][p] = resVal;
605 }
606 break;
607 case 3:
608 this.reserve['w'][this.extraPieces[0]]--;
609 break;
610 case 4:
611 this.reserve['b'][this.extraPieces[0]]--;
612 break;
613 case 5:
614 this.reserve['w'][this.extraPieces[1]]--;
615 break;
616 case 6:
617 this.reserve = null;
618 this.board[3][3] = "";
619 this.board[3][4] = "";
620 break;
621 }
622 }
623 }
624
625 postUndo(move) {
626 if (this.movesCount >= 6) super.postUndo(move);
627 else {
628 switch (this.movesCount) {
629 case 0:
630 this.reserve['w'][move.appear[0].p]++;
631 this.reserve['b'][move.appear[0].p]++;
632 this.extraPieces[0] = '-';
633 break;
634 case 1:
635 this.extraPieces[1] = '-';
636 for (let p of V.RESERVE_PIECES) {
637 const resVal = (p != this.extraPieces[0] ? 1 : 0);
638 this.reserve['w'][p] = resVal;
639 this.reserve['b'][p] = resVal;
640 }
641 break;
642 case 2:
643 this.reserve['w'][this.extraPieces[0]]++;
644 break;
645 case 3:
646 this.reserve['b'][this.extraPieces[0]]++;
647 break;
648 case 4:
649 this.reserve['w'][this.extraPieces[1]]++;
650 break;
651 case 5:
652 this.reserve = { w: {}, b: {} };
653 for (let c of ['w', 'b'])
654 V.RESERVE_PIECES.forEach(p => this.reserve[c][p] = 0);
655 this.reserve['b'][this.extraPieces[1]] = 1;
656 this.board[3][3] = 'b' + this.extraPieces[1];
657 this.board[3][4] = 'w' + this.extraPieces[0];
658 break;
659 }
660 }
661 }
662
663 getComputerMove() {
664 if (this.movesCount >= 6) return super.getComputerMove();
665 // Choose a move at random
666 const moves = this.getAllValidMoves();
667 return moves[randInt(moves.length)];
668 }
669
670 static get SEARCH_DEPTH() {
671 return 2;
672 }
673
674 evalPosition() {
675 let evaluation = 0;
676 for (let i = 0; i < V.size.x; i++) {
677 for (let j = 0; j < V.size.y; j++) {
678 if (this.board[i][j] != V.EMPTY) {
679 const sign = this.getColor(i, j) == "w" ? 1 : -1;
680 const piece = this.getPiece(i, j);
681 evaluation += sign * V.VALUES[piece];
682 const symbol = this.board[i][j][1];
683 if (V.AUGMENTED_PIECES.includes(symbol)) {
684 const extraPiece = this.getExtraPiece(symbol);
685 evaluation += sign * V.VALUES[extraPiece]
686 }
687 }
688 }
689 }
690 return evaluation;
691 }
692
693 static get VALUES() {
694 return Object.assign(
695 {
696 d: 6.7,
697 w: 7.5,
698 x: 5.6,
699 e: 6.3,
700 h: 5.5,
701 f: 7.6,
702 y: 8.15
703 },
704 ChessRules.VALUES
705 );
706 }
707
708 static get ExtraDictionary() {
709 return {
710 [V.LEOPARD]: { prefix: 'L', name: "Leopard" },
711 [V.CANNON]: { prefix: 'C', name: "Cannon" },
712 [V.UNICORN]: { prefix: 'U', name: "Unicorn" },
713 [V.ELEPHANT]: { prefix: 'E', name: "Elephant" },
714 [V.HAWK]: { prefix: 'H', name: "Hawk" },
715 [V.FORTRESS]: { prefix: 'F', name: "Fortress" },
716 [V.SPIDER]: { prefix: 'S', name: "Spider" }
717 }
718 }
719
720 getNotation(move) {
721 if (this.movesCount <= 5) {
722 if (this.movesCount <= 1)
723 return V.ExtraDictionary[move.appear[0].p].name;
724 // Put something on the board:
725 return (
726 V.ExtraDictionary[V.RESERVE_PIECES[move.start.y]].prefix +
727 "@" + V.CoordsToSquare(move.end)
728 );
729 }
730 let notation = "";
731 if (
732 V.AUGMENTED_PIECES.includes(move.vanish[0].p) ||
733 (
734 move.vanish.length >= 2 &&
735 V.AUGMENTED_PIECES.includes(move.vanish[1].p)
736 )
737 ) {
738 // Simplify move before calling super.getNotation()
739 let smove = JSON.parse(JSON.stringify(move));
740 if (ChessRules.PIECES.includes(move.vanish[0].p)) {
741 // Castle with an augmented rook
742 smove.appear.pop();
743 smove.vanish[1].p = smove.appear[1].p;
744 }
745 else {
746 // Moving an augmented piece
747 smove.appear.pop();
748 smove.vanish[0].p = smove.appear[0].p;
749 if (
750 smove.vanish.length == 2 &&
751 smove.vanish[0].c == smove.vanish[1].c &&
752 V.AUGMENTED_PIECES.includes(move.vanish[1].p)
753 ) {
754 // Castle with an augmented rook
755 smove.appear.pop();
756 smove.vanish[1].p = smove.appear[1].p;
757 }
758 }
759 notation = super.getNotation(smove);
760 }
761 // Else, more common case:
762 notation = super.getNotation(move);
763 const pieceSymbol = notation.charAt(0).toLowerCase();
764 if (move.vanish[0].p != V.PAWN && V.RESERVE_PIECES.includes(pieceSymbol))
765 notation = V.ExtraDictionary[pieceSymbol].prefix + notation.substr(1);
766 return notation;
767 }
768
769 };