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