[Pandemonium] Forgot reserve in FEN
[vchess.git] / client / src / variants / Pandemonium.js
CommitLineData
723262f9
BA
1import { ChessRules, Move, PiPo } from "@/base_rules";
2import { randInt } from "@/utils/alea";
4f0025fb 3import { ArrayFun } from "@/utils/array";
723262f9
BA
4
5export class PandemoniumRules extends ChessRules {
6
7 static get PawnSpecs() {
8 return Object.assign(
9 {},
10 ChessRules.PawnSpecs,
4cf77a10 11 { promotions: [V.GILDING] }
723262f9
BA
12 );
13 }
14
809ab1a8
BA
15 loseOnRepetition() {
16 // If current side is under check: lost
17 return this.underCheck(this.turn);
18 }
19
723262f9
BA
20 static get GILDING() {
21 return "g";
22 }
23
24 static get SCEPTER() {
25 return "s";
26 }
27
28 static get HORSE() {
29 return "h";
30 }
31
32 static get DRAGON() {
33 return "d";
34 }
35
36 static get CARDINAL() {
37 return "c";
38 }
39
40 static get WHOLE() {
41 return "w";
42 }
43
44 static get MARSHAL() {
45 return "m";
46 }
47
48 static get APRICOT() {
49 return "a";
50 }
51
52 static get PIECES() {
53 return (
54 ChessRules.PIECES.concat([
55 V.GILDING, V.SCEPTER, V.HORSE, V.DRAGON,
56 V.CARDINAL, V.WHOLE, V.MARSHAL, V.APRICOT])
57 );
58 }
59
60 getPpath(b) {
61 const prefix = (ChessRules.PIECES.includes(b[1]) ? "" : "Pandemonium/");
62 return prefix + b;
63 }
64
65 static get size() {
66 return { x: 10, y: 10};
67 }
68
69 getColor(i, j) {
70 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
71 return this.board[i][j].charAt(0);
72 }
73
74 getPiece(i, j) {
75 if (i >= V.size.x) return V.RESERVE_PIECES[j];
76 return this.board[i][j].charAt(1);
77 }
78
79 setOtherVariables(fen) {
80 super.setOtherVariables(fen);
81 // Sub-turn is useful only at first move...
82 this.subTurn = 1;
83 // Also init reserves (used by the interface to show landable pieces)
84 const reserve =
85 V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
86 this.reserve = {
87 w: {
88 [V.PAWN]: reserve[0],
89 [V.ROOK]: reserve[1],
90 [V.KNIGHT]: reserve[2],
91 [V.BISHOP]: reserve[3],
92 [V.QUEEN]: reserve[4],
93 [V.CARDINAL]: reserve[5],
94 [V.MARSHAL]: reserve[6],
95 },
96 b: {
97 [V.PAWN]: reserve[7],
98 [V.ROOK]: reserve[8],
99 [V.KNIGHT]: reserve[9],
100 [V.BISHOP]: reserve[10],
101 [V.QUEEN]: reserve[11],
102 [V.CARDINAL]: reserve[12],
103 [V.MARSHAL]: reserve[13]
104 }
105 };
106 }
107
108 static IsGoodEnpassant(enpassant) {
109 if (enpassant != "-") {
110 const squares = enpassant.split(",");
111 if (squares.length > 2) return false;
112 for (let sq of squares) {
113 if (!sq.match(/[a-j0-9]/)) return false;
114 }
115 }
116 return true;
117 }
118
119 static IsGoodFen(fen) {
120 if (!ChessRules.IsGoodFen(fen)) return false;
121 const fenParsed = V.ParseFen(fen);
122 // Check reserves
123 if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{14,14}$/))
124 return false;
125 return true;
126 }
127
128 static ParseFen(fen) {
129 const fenParts = fen.split(" ");
130 return Object.assign(
131 ChessRules.ParseFen(fen),
132 { reserve: fenParts[5] }
133 );
134 }
135
136 getFen() {
137 return super.getFen() + " " + this.getReserveFen();
138 }
139
140 getFenForRepeat() {
141 return super.getFenForRepeat() + "_" + this.getReserveFen();
142 }
143
144 getReserveFen() {
145 let counts = new Array(14);
146 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
147 counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]];
148 counts[7 + i] = this.reserve["b"][V.RESERVE_PIECES[i]];
149 }
150 return counts.join("");
151 }
152
723262f9 153 static GenRandInitFen(randomness) {
4f0025fb
BA
154 if (randomness == 0) {
155 return (
156 "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
157 "w 0 ajaj - 00000000000000"
158 );
159 }
160
161 let pieces = { w: new Array(10), b: new Array(10) };
162 let flags = "";
163 for (let c of ["w", "b"]) {
164 if (c == 'b' && randomness == 1) {
165 pieces['b'] = pieces['w'];
166 flags += flags;
167 break;
168 }
169
170 let positions = ArrayFun.range(10);
171
172 // Get random squares for bishops (different colors)
173 let randIndex = 2 * randInt(5);
174 let bishop1Pos = positions[randIndex];
175 let randIndex_tmp = 2 * randInt(5) + 1;
176 let bishop2Pos = positions[randIndex_tmp];
177 positions.splice(Math.max(randIndex, randIndex_tmp), 1);
178 positions.splice(Math.min(randIndex, randIndex_tmp), 1);
179
180 randIndex = randInt(8);
181 let knight1Pos = positions[randIndex];
182 positions.splice(randIndex, 1);
183 randIndex = randInt(7);
184 let knight2Pos = positions[randIndex];
185 positions.splice(randIndex, 1);
186
187 randIndex = randInt(6);
188 let queenPos = positions[randIndex];
189 positions.splice(randIndex, 1);
190
191 // Random squares for cardinal + marshal
192 randIndex = randInt(5);
193 let cardinalPos = positions[randIndex];
194 positions.splice(randIndex, 1);
195 randIndex = randInt(4);
196 let marshalPos = positions[randIndex];
197 positions.splice(randIndex, 1);
198
199 let rook1Pos = positions[0];
200 let kingPos = positions[1];
201 let rook2Pos = positions[2];
202
203 pieces[c][rook1Pos] = "r";
204 pieces[c][knight1Pos] = "n";
205 pieces[c][bishop1Pos] = "b";
206 pieces[c][queenPos] = "q";
207 pieces[c][kingPos] = "k";
208 pieces[c][marshalPos] = "m";
209 pieces[c][cardinalPos] = "c";
210 pieces[c][bishop2Pos] = "b";
211 pieces[c][knight2Pos] = "n";
212 pieces[c][rook2Pos] = "r";
213 flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
214 }
723262f9 215 return (
4f0025fb
BA
216 pieces["b"].join("") +
217 "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
218 pieces["w"].join("").toUpperCase() +
847e5591 219 " w 0 " + flags + " - 00000000000000"
723262f9 220 );
723262f9
BA
221 }
222
223 getEnpassantFen() {
224 const L = this.epSquares.length;
225 if (!this.epSquares[L - 1]) return "-"; //no en-passant
226 let res = "";
227 this.epSquares[L - 1].forEach(sq => {
228 res += V.CoordsToSquare(sq) + ",";
229 });
230 return res.slice(0, -1); //remove last comma
231 }
232
233 getEpSquare(moveOrSquare) {
234 if (!moveOrSquare) return undefined;
235 if (typeof moveOrSquare === "string") {
236 const square = moveOrSquare;
237 if (square == "-") return undefined;
238 let res = [];
239 square.split(",").forEach(sq => {
240 res.push(V.SquareToCoords(sq));
241 });
242 return res;
243 }
244 // Argument is a move:
245 const move = moveOrSquare;
246 const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
247 if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) {
248 const step = (ex - sx) / Math.abs(ex - sx);
249 let res = [{
250 x: sx + step,
251 y: sy
252 }];
253 if (sx + 2 * step != ex) {
254 // 3-squares jump
255 res.push({
256 x: sx + 2 * step,
257 y: sy
258 });
259 }
260 return res;
261 }
262 return undefined; //default
263 }
264
265 getReservePpath(index, color) {
266 const p = V.RESERVE_PIECES[index];
267 const prefix = (ChessRules.PIECES.includes(p) ? "" : "Pandemonium/");
268 return prefix + color + p;;
269 }
270
271 // Ordering on reserve pieces
272 static get RESERVE_PIECES() {
273 return (
274 [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.CARDINAL, V.MARSHAL]
275 );
276 }
277
278 getReserveMoves([x, y]) {
279 const color = this.turn;
822d71d6 280 const oppCol = V.GetOppCol(color);
723262f9
BA
281 const p = V.RESERVE_PIECES[y];
282 if (this.reserve[color][p] == 0) return [];
283 const bounds = (p == V.PAWN ? [1, V.size.x - 1] : [0, V.size.x]);
284 let moves = [];
285 for (let i = bounds[0]; i < bounds[1]; i++) {
286 for (let j = 0; j < V.size.y; j++) {
287 if (this.board[i][j] == V.EMPTY) {
288 let mv = new Move({
289 appear: [
290 new PiPo({
291 x: i,
292 y: j,
293 c: color,
294 p: p
295 })
296 ],
297 vanish: [],
298 start: { x: x, y: y }, //a bit artificial...
299 end: { x: i, y: j }
300 });
301 if (p == V.PAWN) {
302 // Do not drop on checkmate:
303 this.play(mv);
304 const res = (
305 this.underCheck(oppCol) && !this.atLeastOneMove("noReserve")
306 );
307 this.undo(mv);
308 if (res) continue;
309 }
310 moves.push(mv);
311 }
312 }
313 }
314 return moves;
315 }
316
317 static get PromoteMap() {
318 return {
319 r: 'd',
320 n: 's',
321 b: 'h',
322 c: 'w',
323 m: 'a'
324 };
325 }
326
327 getPotentialMovesFrom([x, y]) {
328 const c = this.getColor(x, y);
329 const oppCol = V.GetOppCol(c);
330 if (this.movesCount <= 1) {
331 if (this.kingPos[c][0] == x && this.kingPos[c][1] == y) {
332 // Pass (if setup is ok)
333 return [
334 new Move({
335 appear: [],
336 vanish: [],
337 start: { x: this.kingPos[c][0], y: this.kingPos[c][1] },
338 end: { x: this.kingPos[oppCol][0], y: this.kingPos[oppCol][1] }
339 })
340 ];
341 }
342 const firstRank = (this.movesCount == 0 ? 9 : 0);
4f0025fb
BA
343 if (x != firstRank || this.getPiece(x, y) != V.KNIGHT) return [];
344 // Swap with who? search for matching bishop:
345 let knights = [],
346 bishops = [];
347 for (let i = 0; i < 10; i++) {
348 const elt = this.board[x][i][1];
349 if (elt == 'n') knights.push(i);
350 else if (elt == 'b') bishops.push(i);
723262f9 351 }
4f0025fb
BA
352 const destFile = (knights[0] == y ? bishops[0] : bishops[1]);
353 return [
354 new Move({
355 appear: [
356 new PiPo({
357 x: x,
358 y: destFile,
359 c: c,
360 p: V.KNIGHT
361 }),
362 new PiPo({
363 x: x,
364 y: y,
365 c: c,
366 p: V.BISHOP
367 })
368 ],
369 vanish: [
370 new PiPo({
371 x: x,
372 y: y,
373 c: c,
374 p: V.KNIGHT
375 }),
376 new PiPo({
377 x: x,
378 y: destFile,
379 c: c,
380 p: V.BISHOP
381 })
382 ],
383 start: { x: x, y: y },
384 end: { x: x, y: destFile }
385 })
386 ];
723262f9
BA
387 }
388 // Normal move (after initial setup)
e85bddc1 389 if (x >= V.size.x) return this.getReserveMoves([x, y]);
723262f9
BA
390 const p = this.getPiece(x, y);
391 const sq = [x, y];
392 let moves = [];
393 if (ChessRules.PIECES.includes(p))
394 moves = super.getPotentialMovesFrom(sq);
395 if ([V.GILDING, V.APRICOT, V.WHOLE].includes(p))
396 moves = super.getPotentialQueenMoves(sq);
397 switch (p) {
398 case V.SCEPTER:
399 moves = this.getPotentialScepterMoves(sq);
400 break;
401 case V.HORSE:
402 moves = this.getPotentialHorseMoves(sq);
403 break;
404 case V.DRAGON:
405 moves = this.getPotentialDragonMoves(sq);
406 break;
407 case V.CARDINAL:
408 moves = this.getPotentialCardinalMoves(sq);
409 break;
410 case V.MARSHAL:
411 moves = this.getPotentialMarshalMoves(sq);
412 break;
413 }
414 // Maybe apply promotions:
415 if (Object.keys(V.PromoteMap).includes(p)) {
416 const promoted = V.PromoteMap[p];
417 const lastRank = (c == 'w' ? 0 : 9);
418 let promotions = [];
419 moves.forEach(m => {
420 if (m.start.x == lastRank || m.end.x == lastRank) {
421 let pMove = JSON.parse(JSON.stringify(m));
422 pMove.appear[0].p = promoted;
423 promotions.push(pMove);
424 }
425 });
426 Array.prototype.push.apply(moves, promotions);
427 }
428 return moves;
429 }
430
431 getPotentialPawnMoves([x, y]) {
432 const color = this.turn;
4cf77a10 433 const shiftX = (color == 'w' ? -1 : 1);
723262f9
BA
434 let moves = [];
435 if (this.board[x + shiftX][y] == V.EMPTY) {
436 this.addPawnMoves([x, y], [x + shiftX, y], moves);
4cf77a10 437 if ((color == 'w' && x >= V.size.x - 3) || (color == 'b' && x <= 2)) {
723262f9
BA
438 if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
439 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
440 if (
441 (
4cf77a10
BA
442 (color == 'w' && x == V.size.x - 2) ||
443 (color == 'b' && x == 1)
723262f9
BA
444 )
445 &&
446 this.board[x + 3 * shiftX][y] == V.EMPTY
447 ) {
448 moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
449 }
450 }
451 }
452 }
453 for (let shiftY of [-1, 1]) {
454 if (y + shiftY >= 0 && y + shiftY < V.size.y) {
455 if (
456 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
457 this.canTake([x, y], [x + shiftX, y + shiftY])
458 ) {
459 this.addPawnMoves([x, y], [x + shiftX, y + shiftY], moves);
460 }
461 }
462 }
463 Array.prototype.push.apply(
464 moves,
465 this.getEnpassantCaptures([x, y], shiftX)
466 );
467 return moves;
468 }
469
470 getPotentialMarshalMoves(sq) {
471 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
472 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
473 );
474 }
475
476 getPotentialCardinalMoves(sq) {
477 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
478 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
479 );
480 }
481
482 getPotentialScepterMoves(sq) {
483 const steps =
484 V.steps[V.KNIGHT].concat(V.steps[V.BISHOP]).concat(V.steps[V.ROOK]);
485 return this.getSlideNJumpMoves(sq, steps, "oneStep");
486 }
487
488 getPotentialHorseMoves(sq) {
489 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
490 this.getSlideNJumpMoves(sq, V.steps[V.ROOK], "oneStep"));
491 }
492
493 getPotentialDragonMoves(sq) {
494 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
495 this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], "oneStep"));
496 }
497
498 getEnpassantCaptures([x, y], shiftX) {
499 const Lep = this.epSquares.length;
500 const epSquare = this.epSquares[Lep - 1];
501 let moves = [];
502 if (!!epSquare) {
503 for (let epsq of epSquare) {
504 // TODO: some redundant checks
505 if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
506 let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
507 // WARNING: the captured pawn may be diagonally behind us,
508 // if it's a 3-squares jump and we take on 1st passing square
509 const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
510 enpassantMove.vanish.push({
511 x: px,
512 y: epsq.y,
513 p: "p",
514 c: this.getColor(px, epsq.y)
515 });
516 moves.push(enpassantMove);
517 }
518 }
519 }
520 return moves;
521 }
522
523 getPotentialKingMoves(sq) {
524 // Initialize with normal moves
525 let moves = this.getSlideNJumpMoves(
526 sq,
527 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
528 "oneStep"
529 );
530 const c = this.turn;
531 if (
532 this.castleFlags[c][0] < V.size.y ||
533 this.castleFlags[c][1] < V.size.y
723262f9
BA
534 ) {
535 const finalSquares = [
536 [1, 2],
537 [7, 6]
538 ];
b2e8c34e 539 moves = moves.concat(super.getCastleMoves(sq, finalSquares));
723262f9 540 }
b2e8c34e 541 return moves;
723262f9
BA
542 }
543
544 isAttacked(sq, color) {
545 return (
546 this.isAttackedByPawn(sq, color) ||
547 this.isAttackedByRook(sq, color) ||
548 this.isAttackedByKnight(sq, color) ||
549 this.isAttackedByBishop(sq, color) ||
550 this.isAttackedByKing(sq, color) ||
551 this.isAttackedByQueens(sq, color) ||
552 this.isAttackedByScepter(sq, color) ||
553 this.isAttackedByDragon(sq, color) ||
554 this.isAttackedByHorse(sq, color) ||
555 this.isAttackedByMarshal(sq, color) ||
556 this.isAttackedByCardinal(sq, color)
557 );
558 }
559
560 isAttackedByQueens([x, y], color) {
561 // pieces: because queen = gilding = whole = apricot
562 const pieces = [V.QUEEN, V.GILDING, V.WHOLE, V.APRICOT];
563 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
564 for (let step of steps) {
565 let rx = x + step[0],
566 ry = y + step[1];
567 while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY) {
568 rx += step[0];
569 ry += step[1];
570 }
571 if (
572 V.OnBoard(rx, ry) &&
573 this.board[rx][ry] != V.EMPTY &&
574 pieces.includes(this.getPiece(rx, ry)) &&
575 this.getColor(rx, ry) == color
576 ) {
577 return true;
578 }
579 }
580 return false;
581 }
582
583 isAttackedByScepter(sq, color) {
584 const steps =
585 V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
586 return (
b2511e7e 587 super.isAttackedBySlideNJump(sq, color, V.SCEPTER, steps, "oneStep")
723262f9
BA
588 );
589 }
590
591 isAttackedByHorse(sq, color) {
592 return (
593 super.isAttackedBySlideNJump(sq, color, V.steps[V.BISHOP], V.HORSE) ||
594 super.isAttackedBySlideNJump(
b2511e7e 595 sq, color, V.HORSE, V.steps[V.ROOK], "oneStep")
723262f9
BA
596 );
597 }
598
599 isAttackedByDragon(sq, color) {
600 return (
601 super.isAttackedBySlideNJump(sq, color, V.steps[V.ROOK], V.DRAGON) ||
602 super.isAttackedBySlideNJump(
b2511e7e 603 sq, color, V.DRAGON, V.steps[V.BISHOP], "oneStep")
723262f9
BA
604 );
605 }
606
607 isAttackedByMarshal(sq, color) {
608 return (
609 super.isAttackedBySlideNJump(sq, color, V.MARSHAL, V.steps[V.ROOK]) ||
610 super.isAttackedBySlideNJump(
611 sq,
612 color,
613 V.MARSHAL,
614 V.steps[V.KNIGHT],
615 "oneStep"
616 )
617 );
618 }
619
620 isAttackedByCardinal(sq, color) {
621 return (
622 super.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
623 super.isAttackedBySlideNJump(
624 sq,
625 color,
626 V.CARDINAL,
627 V.steps[V.KNIGHT],
628 "oneStep"
629 )
630 );
631 }
632
633 getAllValidMoves() {
634 let moves = super.getAllPotentialMoves();
1220a5b9
BA
635 if (this.movesCount >= 2) {
636 const color = this.turn;
637 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
638 moves = moves.concat(
639 this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
640 );
641 }
723262f9
BA
642 }
643 return this.filterValid(moves);
644 }
645
646 atLeastOneMove(noReserve) {
647 if (!super.atLeastOneMove()) {
648 if (!noReserve) {
649 // Search one reserve move
650 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
651 let moves = this.filterValid(
652 this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
653 );
654 if (moves.length > 0) return true;
655 }
656 }
657 return false;
658 }
659 return true;
660 }
661
662 // Reverse 'PromoteMap'
663 static get P_CORRESPONDANCES() {
664 return {
665 d: 'r',
666 s: 'n',
667 h: 'b',
668 w: 'c',
51a4d21e
BA
669 a: 'm',
670 g: 'p'
723262f9
BA
671 };
672 }
673
674 static MayDecode(piece) {
675 if (Object.keys(V.P_CORRESPONDANCES).includes(piece))
676 return V.P_CORRESPONDANCES[piece];
677 return piece;
678 }
679
680 play(move) {
681 move.subTurn = this.subTurn; //much easier
682 if (this.movesCount >= 2 || this.subTurn == 2 || move.vanish.length == 0) {
683 this.turn = V.GetOppCol(this.turn);
684 this.subTurn = 1;
685 this.movesCount++;
686 }
687 else this.subTurn = 2;
688 move.flags = JSON.stringify(this.aggregateFlags());
689 this.epSquares.push(this.getEpSquare(move));
690 V.PlayOnBoard(this.board, move);
691 this.postPlay(move);
692 }
693
723262f9
BA
694 postPlay(move) {
695 if (move.vanish.length == 0 && move.appear.length == 0) return;
696 super.postPlay(move);
697 const color = move.appear[0].c;
698 if (move.vanish.length == 0)
699 // Drop unpromoted piece:
700 this.reserve[color][move.appear[0].p]--;
1220a5b9 701 else if (move.vanish.length == 2 && move.appear.length == 1)
723262f9
BA
702 // May capture a promoted piece:
703 this.reserve[color][V.MayDecode(move.vanish[1].p)]++;
704 }
705
706 undo(move) {
707 this.epSquares.pop();
708 this.disaggregateFlags(JSON.parse(move.flags));
709 V.UndoOnBoard(this.board, move);
710 if (this.movesCount >= 2 || this.subTurn == 1 || move.vanish.length == 0) {
711 this.turn = V.GetOppCol(this.turn);
712 this.movesCount--;
713 }
714 this.subTurn = move.subTurn;
715 this.postUndo(move);
716 }
717
718 postUndo(move) {
719 if (move.vanish.length == 0 && move.appear.length == 0) return;
720 super.postUndo(move);
721 const color = move.appear[0].c;
722 if (move.vanish.length == 0)
723 this.reserve[color][move.appear[0].p]++;
1220a5b9 724 else if (move.vanish.length == 2 && move.appear.length == 1)
723262f9
BA
725 this.reserve[color][V.MayDecode(move.vanish[1].p)]--;
726 }
727
723262f9
BA
728 static get VALUES() {
729 return Object.assign(
99ba622a
BA
730 {},
731 ChessRules.VALUES,
723262f9 732 {
99ba622a 733 n: 2.5, //knight is weaker
723262f9
BA
734 g: 9,
735 s: 5,
736 h: 6,
737 d: 7,
738 c: 7,
739 w: 9,
740 m: 8,
741 a: 9
99ba622a 742 }
723262f9
BA
743 );
744 }
745
746 static get SEARCH_DEPTH() {
747 return 2;
748 }
749
750 getComputerMove() {
751 if (this.movesCount <= 1) {
752 // Special case: swap and pass at random
753 const moves1 = this.getAllValidMoves();
754 const m1 = moves1[randInt(moves1.length)];
755 this.play(m1);
756 if (m1.vanish.length == 0) {
757 this.undo(m1);
758 return m1;
759 }
760 const moves2 = this.getAllValidMoves();
761 const m2 = moves2[randInt(moves2.length)];
762 this.undo(m1);
763 return [m1, m2];
764 }
765 return super.getComputerMove();
766 }
767
768 evalPosition() {
769 let evaluation = super.evalPosition();
770 // Add reserves:
771 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
772 const p = V.RESERVE_PIECES[i];
773 evaluation += this.reserve["w"][p] * V.VALUES[p];
774 evaluation -= this.reserve["b"][p] * V.VALUES[p];
775 }
776 return evaluation;
777 }
778
779 getNotation(move) {
780 if (move.vanish.length == 0) {
781 if (move.appear.length == 0) return "pass";
782 const pieceName =
783 (move.appear[0].p == V.PAWN ? "" : move.appear[0].p.toUpperCase());
784 return pieceName + "@" + V.CoordsToSquare(move.end);
785 }
786 if (move.appear.length == 2) {
787 if (move.appear[0].p != V.KING)
788 return V.CoordsToSquare(move.start) + "S" + V.CoordsToSquare(move.end);
789 return (move.end.y < move.start.y ? "0-0" : "0-0-0");
790 }
791 let notation = super.getNotation(move);
792 if (move.vanish[0].p != V.PAWN && move.appear[0].p != move.vanish[0].p)
793 // Add promotion indication:
794 notation += "=" + move.appear[0].p.toUpperCase();
795 return notation;
796 }
797
798};