Add Antiking v1
[vchess.git] / client / src / variants / Baroque.js
CommitLineData
0c3fe8a6
BA
1import { ChessRules, PiPo, Move } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
3import { randInt } from "@/utils/alea";
4
6808d7a1
BA
5export const VariantRules = class BaroqueRules extends ChessRules {
6 static get HasFlags() {
7 return false;
8 }
dac39588 9
c583ef1c
BA
10 static get HasCastle() {
11 return false;
12 }
13
6808d7a1
BA
14 static get HasEnpassant() {
15 return false;
16 }
dac39588 17
241bf8f2
BA
18 static get PIECES() {
19 return ChessRules.PIECES.concat([V.IMMOBILIZER]);
20 }
21
22 getPpath(b) {
6808d7a1
BA
23 if (b[1] == "m")
24 //'m' for Immobilizer (I is too similar to 1)
dac39588
BA
25 return "Baroque/" + b;
26 return b; //usual piece
27 }
28
dac39588 29 // No castling, but checks, so keep track of kings
6808d7a1
BA
30 setOtherVariables(fen) {
31 this.kingPos = { w: [-1, -1], b: [-1, -1] };
dac39588
BA
32 const fenParts = fen.split(" ");
33 const position = fenParts[0].split("/");
6808d7a1 34 for (let i = 0; i < position.length; i++) {
dac39588 35 let k = 0;
6808d7a1
BA
36 for (let j = 0; j < position[i].length; j++) {
37 switch (position[i].charAt(j)) {
38 case "k":
39 this.kingPos["b"] = [i, k];
dac39588 40 break;
6808d7a1
BA
41 case "K":
42 this.kingPos["w"] = [i, k];
dac39588 43 break;
6808d7a1
BA
44 default: {
45 const num = parseInt(position[i].charAt(j));
46 if (!isNaN(num)) k += num - 1;
47 }
dac39588
BA
48 }
49 k++;
50 }
51 }
52 }
53
6808d7a1
BA
54 static get IMMOBILIZER() {
55 return "m";
56 }
dac39588
BA
57 // Although other pieces keep their names here for coding simplicity,
58 // keep in mind that:
59 // - a "rook" is a coordinator, capturing by coordinating with the king
60 // - a "knight" is a long-leaper, capturing as in draughts
61 // - a "bishop" is a chameleon, capturing as its prey
62 // - a "queen" is a withdrawer, capturing by moving away from pieces
63
64 // Is piece on square (x,y) immobilized?
6808d7a1
BA
65 isImmobilized([x, y]) {
66 const piece = this.getPiece(x, y);
67 const color = this.getColor(x, y);
dac39588
BA
68 const oppCol = V.GetOppCol(color);
69 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
6808d7a1
BA
70 for (let step of adjacentSteps) {
71 const [i, j] = [x + step[0], y + step[1]];
72 if (
73 V.OnBoard(i, j) &&
74 this.board[i][j] != V.EMPTY &&
75 this.getColor(i, j) == oppCol
76 ) {
77 const oppPiece = this.getPiece(i, j);
78 if (oppPiece == V.IMMOBILIZER) {
4404e58c 79 // Moving is possible only if this immobilizer is neutralized
6808d7a1
BA
80 for (let step2 of adjacentSteps) {
81 const [i2, j2] = [i + step2[0], j + step2[1]];
82 if (i2 == x && j2 == y) continue; //skip initial piece!
83 if (
84 V.OnBoard(i2, j2) &&
85 this.board[i2][j2] != V.EMPTY &&
86 this.getColor(i2, j2) == color
87 ) {
88 if ([V.BISHOP, V.IMMOBILIZER].includes(this.getPiece(i2, j2)))
dac39588
BA
89 return false;
90 }
91 }
92 return true; //immobilizer isn't neutralized
93 }
94 // Chameleons can't be immobilized twice, because there is only one immobilizer
6808d7a1 95 if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER) return true;
dac39588
BA
96 }
97 }
98 return false;
99 }
100
6808d7a1 101 getPotentialMovesFrom([x, y]) {
dac39588 102 // Pre-check: is thing on this square immobilized?
6808d7a1
BA
103 if (this.isImmobilized([x, y])) return [];
104 switch (this.getPiece(x, y)) {
dac39588 105 case V.IMMOBILIZER:
6808d7a1 106 return this.getPotentialImmobilizerMoves([x, y]);
dac39588 107 default:
6808d7a1 108 return super.getPotentialMovesFrom([x, y]);
dac39588
BA
109 }
110 }
111
6808d7a1
BA
112 getSlideNJumpMoves([x, y], steps, oneStep) {
113 const piece = this.getPiece(x, y);
dac39588 114 let moves = [];
6808d7a1 115 outerLoop: for (let step of steps) {
dac39588
BA
116 let i = x + step[0];
117 let j = y + step[1];
6808d7a1
BA
118 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
119 moves.push(this.getBasicMove([x, y], [i, j]));
120 if (oneStep !== undefined) continue outerLoop;
dac39588
BA
121 i += step[0];
122 j += step[1];
123 }
124 // Only king can take on occupied square:
6808d7a1
BA
125 if (piece == V.KING && V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
126 moves.push(this.getBasicMove([x, y], [i, j]));
dac39588
BA
127 }
128 return moves;
129 }
130
131 // Modify capturing moves among listed pawn moves
6808d7a1 132 addPawnCaptures(moves, byChameleon) {
dac39588
BA
133 const steps = V.steps[V.ROOK];
134 const color = this.turn;
135 const oppCol = V.GetOppCol(color);
136 moves.forEach(m => {
6808d7a1 137 if (!!byChameleon && m.start.x != m.end.x && m.start.y != m.end.y) return; //chameleon not moving as pawn
dac39588 138 // Try capturing in every direction
6808d7a1
BA
139 for (let step of steps) {
140 const sq2 = [m.end.x + 2 * step[0], m.end.y + 2 * step[1]];
141 if (
142 V.OnBoard(sq2[0], sq2[1]) &&
143 this.board[sq2[0]][sq2[1]] != V.EMPTY &&
144 this.getColor(sq2[0], sq2[1]) == color
145 ) {
dac39588 146 // Potential capture
6808d7a1
BA
147 const sq1 = [m.end.x + step[0], m.end.y + step[1]];
148 if (
149 this.board[sq1[0]][sq1[1]] != V.EMPTY &&
150 this.getColor(sq1[0], sq1[1]) == oppCol
151 ) {
152 const piece1 = this.getPiece(sq1[0], sq1[1]);
153 if (!byChameleon || piece1 == V.PAWN) {
154 m.vanish.push(
155 new PiPo({
156 x: sq1[0],
157 y: sq1[1],
158 c: oppCol,
159 p: piece1
160 })
161 );
dac39588
BA
162 }
163 }
164 }
165 }
166 });
167 }
168
169 // "Pincer"
6808d7a1
BA
170 getPotentialPawnMoves([x, y]) {
171 let moves = super.getPotentialRookMoves([x, y]);
dac39588
BA
172 this.addPawnCaptures(moves);
173 return moves;
174 }
175
6808d7a1 176 addRookCaptures(moves, byChameleon) {
dac39588
BA
177 const color = this.turn;
178 const oppCol = V.GetOppCol(color);
179 const kp = this.kingPos[color];
180 moves.forEach(m => {
181 // Check piece-king rectangle (if any) corners for enemy pieces
6808d7a1 182 if (m.end.x == kp[0] || m.end.y == kp[1]) return; //"flat rectangle"
dac39588
BA
183 const corner1 = [m.end.x, kp[1]];
184 const corner2 = [kp[0], m.end.y];
6808d7a1
BA
185 for (let [i, j] of [corner1, corner2]) {
186 if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == oppCol) {
187 const piece = this.getPiece(i, j);
188 if (!byChameleon || piece == V.ROOK) {
189 m.vanish.push(
190 new PiPo({
191 x: i,
192 y: j,
193 p: piece,
194 c: oppCol
195 })
196 );
dac39588
BA
197 }
198 }
199 }
200 });
201 }
202
203 // Coordinator
6808d7a1 204 getPotentialRookMoves(sq) {
dac39588
BA
205 let moves = super.getPotentialQueenMoves(sq);
206 this.addRookCaptures(moves);
207 return moves;
208 }
209
6808d7a1 210 getKnightCaptures(startSquare, byChameleon) {
dac39588
BA
211 // Look in every direction for captures
212 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
213 const color = this.turn;
214 const oppCol = V.GetOppCol(color);
215 let moves = [];
6808d7a1
BA
216 const [x, y] = [startSquare[0], startSquare[1]];
217 const piece = this.getPiece(x, y); //might be a chameleon!
218 outerLoop: for (let step of steps) {
219 let [i, j] = [x + step[0], y + step[1]];
220 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
dac39588
BA
221 i += step[0];
222 j += step[1];
223 }
6808d7a1
BA
224 if (
225 !V.OnBoard(i, j) ||
226 this.getColor(i, j) == color ||
227 (!!byChameleon && this.getPiece(i, j) != V.KNIGHT)
228 ) {
dac39588
BA
229 continue;
230 }
231 // last(thing), cur(thing) : stop if "cur" is our color, or beyond board limits,
232 // or if "last" isn't empty and cur neither. Otherwise, if cur is empty then
233 // add move until cur square; if cur is occupied then stop if !!byChameleon and
234 // the square not occupied by a leaper.
6808d7a1
BA
235 let last = [i, j];
236 let cur = [i + step[0], j + step[1]];
237 let vanished = [new PiPo({ x: x, y: y, c: color, p: piece })];
238 while (V.OnBoard(cur[0], cur[1])) {
239 if (this.board[last[0]][last[1]] != V.EMPTY) {
240 const oppPiece = this.getPiece(last[0], last[1]);
241 if (!!byChameleon && oppPiece != V.KNIGHT) continue outerLoop;
dac39588 242 // Something to eat:
6808d7a1
BA
243 vanished.push(
244 new PiPo({ x: last[0], y: last[1], c: oppCol, p: oppPiece })
245 );
dac39588 246 }
6808d7a1
BA
247 if (this.board[cur[0]][cur[1]] != V.EMPTY) {
248 if (
249 this.getColor(cur[0], cur[1]) == color ||
250 this.board[last[0]][last[1]] != V.EMPTY
251 ) {
252 //TODO: redundant test
dac39588
BA
253 continue outerLoop;
254 }
6808d7a1
BA
255 } else {
256 moves.push(
257 new Move({
258 appear: [new PiPo({ x: cur[0], y: cur[1], c: color, p: piece })],
259 vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
260 start: { x: x, y: y },
261 end: { x: cur[0], y: cur[1] }
262 })
263 );
dac39588 264 }
6808d7a1
BA
265 last = [last[0] + step[0], last[1] + step[1]];
266 cur = [cur[0] + step[0], cur[1] + step[1]];
dac39588
BA
267 }
268 }
269 return moves;
270 }
271
272 // Long-leaper
6808d7a1 273 getPotentialKnightMoves(sq) {
dac39588
BA
274 return super.getPotentialQueenMoves(sq).concat(this.getKnightCaptures(sq));
275 }
276
78d64531 277 // Chameleon
6808d7a1
BA
278 getPotentialBishopMoves([x, y]) {
279 let moves = super
280 .getPotentialQueenMoves([x, y])
281 .concat(this.getKnightCaptures([x, y], "asChameleon"));
dac39588
BA
282 // No "king capture" because king cannot remain under check
283 this.addPawnCaptures(moves, "asChameleon");
284 this.addRookCaptures(moves, "asChameleon");
285 this.addQueenCaptures(moves, "asChameleon");
286 // Post-processing: merge similar moves, concatenating vanish arrays
287 let mergedMoves = {};
288 moves.forEach(m => {
289 const key = m.end.x + V.size.x * m.end.y;
6808d7a1
BA
290 if (!mergedMoves[key]) mergedMoves[key] = m;
291 else {
292 for (let i = 1; i < m.vanish.length; i++)
dac39588
BA
293 mergedMoves[key].vanish.push(m.vanish[i]);
294 }
295 });
296 // Finally return an array
297 moves = [];
6808d7a1
BA
298 Object.keys(mergedMoves).forEach(k => {
299 moves.push(mergedMoves[k]);
300 });
dac39588
BA
301 return moves;
302 }
303
6808d7a1
BA
304 addQueenCaptures(moves, byChameleon) {
305 if (moves.length == 0) return;
306 const [x, y] = [moves[0].start.x, moves[0].start.y];
dac39588
BA
307 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
308 let capturingDirections = [];
309 const color = this.turn;
310 const oppCol = V.GetOppCol(color);
311 adjacentSteps.forEach(step => {
6808d7a1
BA
312 const [i, j] = [x + step[0], y + step[1]];
313 if (
314 V.OnBoard(i, j) &&
315 this.board[i][j] != V.EMPTY &&
316 this.getColor(i, j) == oppCol &&
317 (!byChameleon || this.getPiece(i, j) == V.QUEEN)
318 ) {
dac39588
BA
319 capturingDirections.push(step);
320 }
321 });
322 moves.forEach(m => {
323 const step = [
6808d7a1
BA
324 m.end.x != x ? (m.end.x - x) / Math.abs(m.end.x - x) : 0,
325 m.end.y != y ? (m.end.y - y) / Math.abs(m.end.y - y) : 0
dac39588
BA
326 ];
327 // NOTE: includes() and even _.isEqual() functions fail...
328 // TODO: this test should be done only once per direction
6808d7a1
BA
329 if (
330 capturingDirections.some(dir => {
331 return dir[0] == -step[0] && dir[1] == -step[1];
332 })
333 ) {
334 const [i, j] = [x - step[0], y - step[1]];
335 m.vanish.push(
336 new PiPo({
337 x: i,
338 y: j,
339 p: this.getPiece(i, j),
340 c: oppCol
341 })
342 );
dac39588
BA
343 }
344 });
345 }
346
78d64531 347 // Withdrawer
6808d7a1 348 getPotentialQueenMoves(sq) {
dac39588
BA
349 let moves = super.getPotentialQueenMoves(sq);
350 this.addQueenCaptures(moves);
351 return moves;
352 }
353
6808d7a1 354 getPotentialImmobilizerMoves(sq) {
dac39588
BA
355 // Immobilizer doesn't capture
356 return super.getPotentialQueenMoves(sq);
357 }
358
dac39588
BA
359 // isAttacked() is OK because the immobilizer doesn't take
360
68e19a44 361 isAttackedByPawn([x, y], color) {
dac39588
BA
362 // Square (x,y) must be surroundable by two enemy pieces,
363 // and one of them at least should be a pawn (moving).
6808d7a1
BA
364 const dirs = [
365 [1, 0],
366 [0, 1]
367 ];
dac39588 368 const steps = V.steps[V.ROOK];
6808d7a1
BA
369 for (let dir of dirs) {
370 const [i1, j1] = [x - dir[0], y - dir[1]]; //"before"
371 const [i2, j2] = [x + dir[0], y + dir[1]]; //"after"
372 if (V.OnBoard(i1, j1) && V.OnBoard(i2, j2)) {
373 if (
68e19a44
BA
374 (
375 this.board[i1][j1] != V.EMPTY &&
376 this.getColor(i1, j1) == color &&
377 this.board[i2][j2] == V.EMPTY
378 )
379 ||
380 (
381 this.board[i2][j2] != V.EMPTY &&
382 this.getColor(i2, j2) == color &&
383 this.board[i1][j1] == V.EMPTY
384 )
6808d7a1 385 ) {
dac39588 386 // Search a movable enemy pawn landing on the empty square
6808d7a1
BA
387 for (let step of steps) {
388 let [ii, jj] = this.board[i1][j1] == V.EMPTY ? [i1, j1] : [i2, j2];
389 let [i3, j3] = [ii + step[0], jj + step[1]];
390 while (V.OnBoard(i3, j3) && this.board[i3][j3] == V.EMPTY) {
dac39588
BA
391 i3 += step[0];
392 j3 += step[1];
393 }
6808d7a1
BA
394 if (
395 V.OnBoard(i3, j3) &&
68e19a44 396 this.getColor(i3, j3) == color &&
6808d7a1
BA
397 this.getPiece(i3, j3) == V.PAWN &&
398 !this.isImmobilized([i3, j3])
399 ) {
dac39588
BA
400 return true;
401 }
402 }
403 }
404 }
405 }
406 return false;
407 }
408
68e19a44 409 isAttackedByRook([x, y], color) {
dac39588
BA
410 // King must be on same column or row,
411 // and a rook should be able to reach a capturing square
68e19a44
BA
412 const sameRow = x == this.kingPos[color][0];
413 const sameColumn = y == this.kingPos[color][1];
6808d7a1 414 if (sameRow || sameColumn) {
dac39588 415 // Look for the enemy rook (maximum 1)
6808d7a1
BA
416 for (let i = 0; i < V.size.x; i++) {
417 for (let j = 0; j < V.size.y; j++) {
418 if (
419 this.board[i][j] != V.EMPTY &&
68e19a44 420 this.getColor(i, j) == color &&
6808d7a1
BA
421 this.getPiece(i, j) == V.ROOK
422 ) {
423 if (this.isImmobilized([i, j])) return false; //because only one rook
dac39588
BA
424 // Can it reach a capturing square?
425 // Easy but quite suboptimal way (TODO): generate all moves (turn is OK)
6808d7a1
BA
426 const moves = this.getPotentialMovesFrom([i, j]);
427 for (let move of moves) {
428 if (
429 (sameRow && move.end.y == y) ||
430 (sameColumn && move.end.x == x)
431 )
dac39588
BA
432 return true;
433 }
434 }
435 }
436 }
437 }
438 return false;
439 }
440
68e19a44 441 isAttackedByKnight([x, y], color) {
dac39588
BA
442 // Square (x,y) must be on same line as a knight,
443 // and there must be empty square(s) behind.
444 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
6808d7a1
BA
445 outerLoop: for (let step of steps) {
446 const [i0, j0] = [x + step[0], y + step[1]];
447 if (V.OnBoard(i0, j0) && this.board[i0][j0] == V.EMPTY) {
dac39588 448 // Try in opposite direction:
6808d7a1
BA
449 let [i, j] = [x - step[0], y - step[1]];
450 while (V.OnBoard(i, j)) {
451 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
dac39588
BA
452 i -= step[0];
453 j -= step[1];
454 }
6808d7a1 455 if (V.OnBoard(i, j)) {
68e19a44 456 if (this.getColor(i, j) == color) {
6808d7a1
BA
457 if (
458 this.getPiece(i, j) == V.KNIGHT &&
459 !this.isImmobilized([i, j])
460 )
dac39588
BA
461 return true;
462 continue outerLoop;
463 }
464 // [else] Our color, could be captured *if there was an empty space*
6808d7a1 465 if (this.board[i + step[0]][j + step[1]] != V.EMPTY)
dac39588
BA
466 continue outerLoop;
467 i -= step[0];
468 j -= step[1];
469 }
470 }
471 }
472 }
473 return false;
474 }
475
68e19a44 476 isAttackedByBishop([x, y], color) {
4404e58c
BA
477 // We cheat a little here: since this function is used exclusively for
478 // the king, it's enough to check the immediate surrounding of the square.
dac39588 479 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
6808d7a1
BA
480 for (let step of adjacentSteps) {
481 const [i, j] = [x + step[0], y + step[1]];
482 if (
483 V.OnBoard(i, j) &&
484 this.board[i][j] != V.EMPTY &&
68e19a44 485 this.getColor(i, j) == color &&
6808d7a1
BA
486 this.getPiece(i, j) == V.BISHOP
487 ) {
dac39588
BA
488 return true; //bishops are never immobilized
489 }
490 }
491 return false;
492 }
493
68e19a44 494 isAttackedByQueen([x, y], color) {
dac39588
BA
495 // Square (x,y) must be adjacent to a queen, and the queen must have
496 // some free space in the opposite direction from (x,y)
497 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
6808d7a1
BA
498 for (let step of adjacentSteps) {
499 const sq2 = [x + 2 * step[0], y + 2 * step[1]];
500 if (V.OnBoard(sq2[0], sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY) {
501 const sq1 = [x + step[0], y + step[1]];
502 if (
503 this.board[sq1[0]][sq1[1]] != V.EMPTY &&
68e19a44 504 this.getColor(sq1[0], sq1[1]) == color &&
6808d7a1
BA
505 this.getPiece(sq1[0], sq1[1]) == V.QUEEN &&
506 !this.isImmobilized(sq1)
507 ) {
dac39588
BA
508 return true;
509 }
510 }
511 }
512 return false;
513 }
514
68e19a44 515 isAttackedByKing([x, y], color) {
4404e58c
BA
516 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
517 for (let step of steps) {
518 let rx = x + step[0],
519 ry = y + step[1];
520 if (
521 V.OnBoard(rx, ry) &&
522 this.getPiece(rx, ry) === V.KING &&
68e19a44 523 this.getColor(rx, ry) == color &&
4404e58c
BA
524 !this.isImmobilized([rx, ry])
525 ) {
526 return true;
527 }
528 }
529 return false;
530 }
531
6808d7a1 532 static get VALUES() {
dac39588 533 return {
6808d7a1
BA
534 p: 1,
535 r: 2,
536 n: 5,
537 b: 3,
538 q: 3,
539 m: 5,
540 k: 1000
dac39588
BA
541 };
542 }
543
6808d7a1
BA
544 static get SEARCH_DEPTH() {
545 return 2;
546 }
dac39588 547
7ba4a5bc 548 static GenRandInitFen(randomness) {
7ba4a5bc
BA
549 if (randomness == 0)
550 // Deterministic:
551 return "rnbqkbnrm/pppppppp/8/8/8/8/PPPPPPPP/MNBKQBNR w 0";
552
6808d7a1 553 let pieces = { w: new Array(8), b: new Array(8) };
dac39588 554 // Shuffle pieces on first and last rank
6808d7a1 555 for (let c of ["w", "b"]) {
7ba4a5bc
BA
556 if (c == 'b' && randomness == 1) {
557 pieces['b'] = pieces['w'];
558 break;
559 }
560
dac39588
BA
561 let positions = ArrayFun.range(8);
562 // Get random squares for every piece, totally freely
563
564 let randIndex = randInt(8);
565 const bishop1Pos = positions[randIndex];
566 positions.splice(randIndex, 1);
567
568 randIndex = randInt(7);
569 const bishop2Pos = positions[randIndex];
570 positions.splice(randIndex, 1);
571
572 randIndex = randInt(6);
573 const knight1Pos = positions[randIndex];
574 positions.splice(randIndex, 1);
575
576 randIndex = randInt(5);
577 const knight2Pos = positions[randIndex];
578 positions.splice(randIndex, 1);
579
580 randIndex = randInt(4);
581 const queenPos = positions[randIndex];
582 positions.splice(randIndex, 1);
583
584 randIndex = randInt(3);
585 const kingPos = positions[randIndex];
586 positions.splice(randIndex, 1);
587
588 randIndex = randInt(2);
589 const rookPos = positions[randIndex];
590 positions.splice(randIndex, 1);
591 const immobilizerPos = positions[0];
592
6808d7a1
BA
593 pieces[c][bishop1Pos] = "b";
594 pieces[c][bishop2Pos] = "b";
595 pieces[c][knight1Pos] = "n";
596 pieces[c][knight2Pos] = "n";
597 pieces[c][queenPos] = "q";
598 pieces[c][kingPos] = "k";
599 pieces[c][rookPos] = "r";
600 pieces[c][immobilizerPos] = "m";
dac39588 601 }
6808d7a1
BA
602 return (
603 pieces["b"].join("") +
dac39588
BA
604 "/pppppppp/8/8/8/8/PPPPPPPP/" +
605 pieces["w"].join("").toUpperCase() +
6808d7a1
BA
606 " w 0"
607 );
dac39588
BA
608 }
609
6808d7a1 610 getNotation(move) {
dac39588
BA
611 const initialSquare = V.CoordsToSquare(move.start);
612 const finalSquare = V.CoordsToSquare(move.end);
613 let notation = undefined;
6808d7a1 614 if (move.appear[0].p == V.PAWN) {
dac39588
BA
615 // Pawn: generally ambiguous short notation, so we use full description
616 notation = "P" + initialSquare + finalSquare;
6808d7a1
BA
617 } else if (move.appear[0].p == V.KING)
618 notation = "K" + (move.vanish.length > 1 ? "x" : "") + finalSquare;
619 else notation = move.appear[0].p.toUpperCase() + finalSquare;
e9b736ee
BA
620 // Add a capture mark (not describing what is captured...):
621 if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
dac39588
BA
622 return notation;
623 }
6808d7a1 624};