Pandemonium 1 & 2, Stealthbomb 1 & 2
[vchess.git] / client / src / variants / Pandemonium.js
diff --git a/client/src/variants/Pandemonium.js b/client/src/variants/Pandemonium.js
deleted file mode 100644 (file)
index 4894ec1..0000000
+++ /dev/null
@@ -1,805 +0,0 @@
-import { ChessRules, Move, PiPo } from "@/base_rules";
-import { randInt } from "@/utils/alea";
-import { ArrayFun } from "@/utils/array";
-
-export class PandemoniumRules extends ChessRules {
-
-  loseOnRepetition() {
-    // If current side is under check: lost
-    return this.underCheck(this.turn);
-  }
-
-  static get GILDING() {
-    return "g";
-  }
-
-  static get SCEPTER() {
-    return "s";
-  }
-
-  static get HORSE() {
-    return "h";
-  }
-
-  static get DRAGON() {
-    return "d";
-  }
-
-  static get CARDINAL() {
-    return "c";
-  }
-
-  static get WHOLE() {
-    return "w";
-  }
-
-  static get MARSHAL() {
-    return "m";
-  }
-
-  static get APRICOT() {
-    return "a";
-  }
-
-  static get PIECES() {
-    return (
-      ChessRules.PIECES.concat([
-        V.GILDING, V.SCEPTER, V.HORSE, V.DRAGON,
-        V.CARDINAL, V.WHOLE, V.MARSHAL, V.APRICOT])
-    );
-  }
-
-  getPpath(b) {
-    const prefix = (ChessRules.PIECES.includes(b[1]) ? "" : "Pandemonium/");
-    return prefix + b;
-  }
-
-  static get size() {
-    return { x: 10, y: 10};
-  }
-
-  getColor(i, j) {
-    if (i >= V.size.x) return i == V.size.x ? "w" : "b";
-    return this.board[i][j].charAt(0);
-  }
-
-  getPiece(i, j) {
-    if (i >= V.size.x) return V.RESERVE_PIECES[j];
-    return this.board[i][j].charAt(1);
-  }
-
-  setOtherVariables(fen) {
-    super.setOtherVariables(fen);
-    // Sub-turn is useful only at first move...
-    this.subTurn = 1;
-    // Also init reserves (used by the interface to show landable pieces)
-    const reserve =
-      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
-    this.reserve = {
-      w: {
-        [V.PAWN]: reserve[0],
-        [V.ROOK]: reserve[1],
-        [V.KNIGHT]: reserve[2],
-        [V.BISHOP]: reserve[3],
-        [V.QUEEN]: reserve[4],
-        [V.CARDINAL]: reserve[5],
-        [V.MARSHAL]: reserve[6],
-      },
-      b: {
-        [V.PAWN]: reserve[7],
-        [V.ROOK]: reserve[8],
-        [V.KNIGHT]: reserve[9],
-        [V.BISHOP]: reserve[10],
-        [V.QUEEN]: reserve[11],
-        [V.CARDINAL]: reserve[12],
-        [V.MARSHAL]: reserve[13]
-      }
-    };
-  }
-
-  static IsGoodEnpassant(enpassant) {
-    if (enpassant != "-") {
-      const squares = enpassant.split(",");
-      if (squares.length > 2) return false;
-      for (let sq of squares) {
-        if (!sq.match(/[a-j0-9]/)) return false;
-      }
-    }
-    return true;
-  }
-
-  static IsGoodFen(fen) {
-    if (!ChessRules.IsGoodFen(fen)) return false;
-    const fenParsed = V.ParseFen(fen);
-    // Check reserves
-    if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{14,14}$/))
-      return false;
-    return true;
-  }
-
-  static ParseFen(fen) {
-    const fenParts = fen.split(" ");
-    return Object.assign(
-      ChessRules.ParseFen(fen),
-      { reserve: fenParts[5] }
-    );
-  }
-
-  getFen() {
-    return super.getFen() + " " + this.getReserveFen();
-  }
-
-  getFenForRepeat() {
-    return super.getFenForRepeat() + "_" + this.getReserveFen();
-  }
-
-  getReserveFen() {
-    let counts = new Array(14);
-    for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
-      counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]];
-      counts[7 + i] = this.reserve["b"][V.RESERVE_PIECES[i]];
-    }
-    return counts.join("");
-  }
-
-  static GenRandInitFen(randomness) {
-    if (randomness == 0) {
-      return (
-        "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
-        "w 0 ajaj - 00000000000000"
-      );
-    }
-
-    let pieces = { w: new Array(10), b: new Array(10) };
-    let flags = "";
-    for (let c of ["w", "b"]) {
-      if (c == 'b' && randomness == 1) {
-        pieces['b'] = pieces['w'];
-        flags += flags;
-        break;
-      }
-
-      let positions = ArrayFun.range(10);
-
-      // Get random squares for bishops (different colors)
-      let randIndex = 2 * randInt(5);
-      let bishop1Pos = positions[randIndex];
-      let randIndex_tmp = 2 * randInt(5) + 1;
-      let bishop2Pos = positions[randIndex_tmp];
-      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
-      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
-      randIndex = randInt(8);
-      let knight1Pos = positions[randIndex];
-      positions.splice(randIndex, 1);
-      randIndex = randInt(7);
-      let knight2Pos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      randIndex = randInt(6);
-      let queenPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      // Random squares for cardinal + marshal
-      randIndex = randInt(5);
-      let cardinalPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-      randIndex = randInt(4);
-      let marshalPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      let rook1Pos = positions[0];
-      let kingPos = positions[1];
-      let rook2Pos = positions[2];
-
-      pieces[c][rook1Pos] = "r";
-      pieces[c][knight1Pos] = "n";
-      pieces[c][bishop1Pos] = "b";
-      pieces[c][queenPos] = "q";
-      pieces[c][kingPos] = "k";
-      pieces[c][marshalPos] = "m";
-      pieces[c][cardinalPos] = "c";
-      pieces[c][bishop2Pos] = "b";
-      pieces[c][knight2Pos] = "n";
-      pieces[c][rook2Pos] = "r";
-      flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
-    }
-    return (
-      pieces["b"].join("") +
-      "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
-      pieces["w"].join("").toUpperCase() +
-      " w 0 " + flags + " - 00000000000000"
-    );
-  }
-
-  getEnpassantFen() {
-    const L = this.epSquares.length;
-    if (!this.epSquares[L - 1]) return "-"; //no en-passant
-    let res = "";
-    this.epSquares[L - 1].forEach(sq => {
-      res += V.CoordsToSquare(sq) + ",";
-    });
-    return res.slice(0, -1); //remove last comma
-  }
-
-  getEpSquare(moveOrSquare) {
-    if (!moveOrSquare) return undefined;
-    if (typeof moveOrSquare === "string") {
-      const square = moveOrSquare;
-      if (square == "-") return undefined;
-      let res = [];
-      square.split(",").forEach(sq => {
-        res.push(V.SquareToCoords(sq));
-      });
-      return res;
-    }
-    // Argument is a move:
-    const move = moveOrSquare;
-    const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
-    if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) {
-      const step = (ex - sx) / Math.abs(ex - sx);
-      let res = [{
-        x: sx + step,
-        y: sy
-      }];
-      if (sx + 2 * step != ex) {
-        // 3-squares jump
-        res.push({
-          x: sx + 2 * step,
-          y: sy
-        });
-      }
-      return res;
-    }
-    return undefined; //default
-  }
-
-  getReservePpath(index, color) {
-    const p = V.RESERVE_PIECES[index];
-    const prefix = (ChessRules.PIECES.includes(p) ? "" : "Pandemonium/");
-    return prefix + color + p;;
-  }
-
-  // Ordering on reserve pieces
-  static get RESERVE_PIECES() {
-    return (
-      [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.CARDINAL, V.MARSHAL]
-    );
-  }
-
-  getReserveMoves([x, y]) {
-    const color = this.turn;
-    const oppCol = V.GetOppCol(color);
-    const p = V.RESERVE_PIECES[y];
-    if (this.reserve[color][p] == 0) return [];
-    const bounds = (p == V.PAWN ? [1, V.size.x - 1] : [0, V.size.x]);
-    let moves = [];
-    for (let i = bounds[0]; i < bounds[1]; i++) {
-      for (let j = 0; j < V.size.y; j++) {
-        if (this.board[i][j] == V.EMPTY) {
-          let mv = new Move({
-            appear: [
-              new PiPo({
-                x: i,
-                y: j,
-                c: color,
-                p: p
-              })
-            ],
-            vanish: [],
-            start: { x: x, y: y }, //a bit artificial...
-            end: { x: i, y: j }
-          });
-          if (p == V.PAWN) {
-            // Do not drop on checkmate:
-            this.play(mv);
-            const res = (
-              this.underCheck(oppCol) && !this.atLeastOneMove("noReserve")
-            );
-            this.undo(mv);
-            if (res) continue;
-          }
-          moves.push(mv);
-        }
-      }
-    }
-    return moves;
-  }
-
-  static get PromoteMap() {
-    return {
-      r: 'd',
-      n: 's',
-      b: 'h',
-      c: 'w',
-      m: 'a'
-    };
-  }
-
-  getPotentialMovesFrom([x, y]) {
-    const c = this.getColor(x, y);
-    const oppCol = V.GetOppCol(c);
-    if (this.movesCount <= 1) {
-      if (this.kingPos[c][0] == x && this.kingPos[c][1] == y) {
-        // Pass (if setup is ok)
-        return [
-          new Move({
-            appear: [],
-            vanish: [],
-            start: { x: this.kingPos[c][0], y: this.kingPos[c][1] },
-            end: { x: this.kingPos[oppCol][0], y: this.kingPos[oppCol][1] }
-          })
-        ];
-      }
-      const firstRank = (this.movesCount == 0 ? 9 : 0);
-      if (x != firstRank || this.getPiece(x, y) != V.KNIGHT) return [];
-      // Swap with who? search for matching bishop:
-      let knights = [],
-          bishops = [];
-      for (let i = 0; i < 10; i++) {
-        const elt = this.board[x][i][1];
-        if (elt == 'n') knights.push(i);
-        else if (elt == 'b') bishops.push(i);
-      }
-      const destFile = (knights[0] == y ? bishops[0] : bishops[1]);
-      return [
-        new Move({
-          appear: [
-            new PiPo({
-              x: x,
-              y: destFile,
-              c: c,
-              p: V.KNIGHT
-            }),
-            new PiPo({
-              x: x,
-              y: y,
-              c: c,
-              p: V.BISHOP
-            })
-          ],
-          vanish: [
-            new PiPo({
-              x: x,
-              y: y,
-              c: c,
-              p: V.KNIGHT
-            }),
-            new PiPo({
-              x: x,
-              y: destFile,
-              c: c,
-              p: V.BISHOP
-            })
-          ],
-          start: { x: x, y: y },
-          end: { x: x, y: destFile }
-        })
-      ];
-    }
-    // Normal move (after initial setup)
-    if (x >= V.size.x) return this.getReserveMoves([x, y]);
-    const p = this.getPiece(x, y);
-    const sq = [x, y];
-    let moves = [];
-    if (ChessRules.PIECES.includes(p))
-      moves = super.getPotentialMovesFrom(sq);
-    if ([V.GILDING, V.APRICOT, V.WHOLE].includes(p))
-      moves = super.getPotentialQueenMoves(sq);
-    switch (p) {
-      case V.SCEPTER:
-        moves = this.getPotentialScepterMoves(sq);
-        break;
-      case V.HORSE:
-        moves = this.getPotentialHorseMoves(sq);
-        break;
-      case V.DRAGON:
-        moves = this.getPotentialDragonMoves(sq);
-        break;
-      case V.CARDINAL:
-        moves = this.getPotentialCardinalMoves(sq);
-        break;
-      case V.MARSHAL:
-        moves = this.getPotentialMarshalMoves(sq);
-        break;
-    }
-    // Maybe apply promotions:
-    if (Object.keys(V.PromoteMap).includes(p)) {
-      const promoted = V.PromoteMap[p];
-      const lastRanks = (c == 'w' ? [0, 1] : [9, 8]);
-      let promotions = [];
-      moves.forEach(m => {
-        if (lastRanks.includes(m.start.x) || lastRanks.includes(m.end.x)) {
-          let pMove = JSON.parse(JSON.stringify(m));
-          pMove.appear[0].p = promoted;
-          promotions.push(pMove);
-        }
-      });
-      Array.prototype.push.apply(moves, promotions);
-    }
-    return moves;
-  }
-
-  addPawnMoves([x1, y1], [x2, y2], moves) {
-    const color = this.turn;
-    const lastRanks = (color == "w" ? [0, 1] : [9, 8]);
-    if (!lastRanks.includes(x2)) {
-      moves.push(this.getBasicMove([x1, y1], [x2, y2]));
-      return;
-    }
-    let finalPieces = [V.GILDING];
-    if (x2 == lastRanks[1]) finalPieces.push(V.PAWN);
-    for (let piece of finalPieces) {
-      const tr = (piece != V.PAWN ? { c: color, p: piece } : null);
-      moves.push(this.getBasicMove([x1, y1], [x2, y2], tr));
-    }
-  }
-
-  getPotentialPawnMoves([x, y]) {
-    const color = this.turn;
-    const shiftX = (color == 'w' ? -1 : 1);
-    let moves = [];
-    if (this.board[x + shiftX][y] == V.EMPTY) {
-      this.addPawnMoves([x, y], [x + shiftX, y], moves);
-      if ((color == 'w' && x >= V.size.x - 3) || (color == 'b' && x <= 2)) {
-        if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
-          moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
-          if (
-            (
-              (color == 'w' && x == V.size.x - 2) ||
-              (color == 'b' && x == 1)
-            )
-            &&
-            this.board[x + 3 * shiftX][y] == V.EMPTY
-          ) {
-            moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
-          }
-        }
-      }
-    }
-    for (let shiftY of [-1, 1]) {
-      if (y + shiftY >= 0 && y + shiftY < V.size.y) {
-        if (
-          this.board[x + shiftX][y + shiftY] != V.EMPTY &&
-          this.canTake([x, y], [x + shiftX, y + shiftY])
-        ) {
-          this.addPawnMoves([x, y], [x + shiftX, y + shiftY], moves);
-        }
-      }
-    }
-    Array.prototype.push.apply(
-      moves,
-      this.getEnpassantCaptures([x, y], shiftX)
-    );
-    return moves;
-  }
-
-  getPotentialMarshalMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
-      this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
-    );
-  }
-
-  getPotentialCardinalMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
-      this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
-    );
-  }
-
-  getPotentialScepterMoves(sq) {
-    const steps =
-      V.steps[V.KNIGHT].concat(V.steps[V.BISHOP]).concat(V.steps[V.ROOK]);
-    return this.getSlideNJumpMoves(sq, steps, "oneStep");
-  }
-
-  getPotentialHorseMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
-      this.getSlideNJumpMoves(sq, V.steps[V.ROOK], "oneStep"));
-  }
-
-  getPotentialDragonMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
-      this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], "oneStep"));
-  }
-
-  getEnpassantCaptures([x, y], shiftX) {
-    const Lep = this.epSquares.length;
-    const epSquare = this.epSquares[Lep - 1];
-    let moves = [];
-    if (!!epSquare) {
-      for (let epsq of epSquare) {
-        // TODO: some redundant checks
-        if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
-          let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
-          // WARNING: the captured pawn may be diagonally behind us,
-          // if it's a 3-squares jump and we take on 1st passing square
-          const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
-          enpassantMove.vanish.push({
-            x: px,
-            y: epsq.y,
-            p: "p",
-            c: this.getColor(px, epsq.y)
-          });
-          moves.push(enpassantMove);
-        }
-      }
-    }
-    return moves;
-  }
-
-  getPotentialKingMoves(sq) {
-    // Initialize with normal moves
-    let moves = this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-    const c = this.turn;
-    if (
-      this.castleFlags[c][0] < V.size.y ||
-      this.castleFlags[c][1] < V.size.y
-    ) {
-      const finalSquares = [
-        [1, 2],
-        [7, 6]
-      ];
-      moves = moves.concat(super.getCastleMoves(sq, finalSquares));
-    }
-    return moves;
-  }
-
-  isAttacked(sq, color) {
-    return (
-      this.isAttackedByPawn(sq, color) ||
-      this.isAttackedByRook(sq, color) ||
-      this.isAttackedByKnight(sq, color) ||
-      this.isAttackedByBishop(sq, color) ||
-      this.isAttackedByKing(sq, color) ||
-      this.isAttackedByQueens(sq, color) ||
-      this.isAttackedByScepter(sq, color) ||
-      this.isAttackedByDragon(sq, color) ||
-      this.isAttackedByHorse(sq, color) ||
-      this.isAttackedByMarshal(sq, color) ||
-      this.isAttackedByCardinal(sq, color)
-    );
-  }
-
-  isAttackedByQueens([x, y], color) {
-    // pieces: because queen = gilding = whole = apricot
-    const pieces = [V.QUEEN, V.GILDING, V.WHOLE, V.APRICOT];
-    const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-    for (let step of steps) {
-      let rx = x + step[0],
-          ry = y + step[1];
-      while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY) {
-        rx += step[0];
-        ry += step[1];
-      }
-      if (
-        V.OnBoard(rx, ry) &&
-        this.board[rx][ry] != V.EMPTY &&
-        pieces.includes(this.getPiece(rx, ry)) &&
-        this.getColor(rx, ry) == color
-      ) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  isAttackedByScepter(sq, color) {
-    const steps =
-      V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
-    return (
-      super.isAttackedBySlideNJump(sq, color, V.SCEPTER, steps, "oneStep")
-    );
-  }
-
-  isAttackedByHorse(sq, color) {
-    return (
-      super.isAttackedBySlideNJump(sq, color, V.HORSE, V.steps[V.BISHOP]) ||
-      super.isAttackedBySlideNJump(
-        sq, color, V.HORSE, V.steps[V.ROOK], "oneStep")
-    );
-  }
-
-  isAttackedByDragon(sq, color) {
-    return (
-      super.isAttackedBySlideNJump(sq, color, V.DRAGON, V.steps[V.ROOK]) ||
-      super.isAttackedBySlideNJump(
-        sq, color, V.DRAGON, V.steps[V.BISHOP], "oneStep")
-    );
-  }
-
-  isAttackedByMarshal(sq, color) {
-    return (
-      super.isAttackedBySlideNJump(sq, color, V.MARSHAL, V.steps[V.ROOK]) ||
-      super.isAttackedBySlideNJump(
-        sq,
-        color,
-        V.MARSHAL,
-        V.steps[V.KNIGHT],
-        "oneStep"
-      )
-    );
-  }
-
-  isAttackedByCardinal(sq, color) {
-    return (
-      super.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
-      super.isAttackedBySlideNJump(
-        sq,
-        color,
-        V.CARDINAL,
-        V.steps[V.KNIGHT],
-        "oneStep"
-      )
-    );
-  }
-
-  getAllValidMoves() {
-    let moves = super.getAllPotentialMoves();
-    if (this.movesCount >= 2) {
-      const color = this.turn;
-      for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
-        moves = moves.concat(
-          this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
-        );
-      }
-    }
-    return this.filterValid(moves);
-  }
-
-  atLeastOneMove(noReserve) {
-    if (!super.atLeastOneMove()) {
-      if (!noReserve) {
-        // Search one reserve move
-        for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
-          let moves = this.filterValid(
-            this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
-          );
-          if (moves.length > 0) return true;
-        }
-      }
-      return false;
-    }
-    return true;
-  }
-
-  // Reverse 'PromoteMap'
-  static get P_CORRESPONDANCES() {
-    return {
-      d: 'r',
-      s: 'n',
-      h: 'b',
-      w: 'c',
-      a: 'm',
-      g: 'p'
-    };
-  }
-
-  static MayDecode(piece) {
-    if (Object.keys(V.P_CORRESPONDANCES).includes(piece))
-      return V.P_CORRESPONDANCES[piece];
-    return piece;
-  }
-
-  play(move) {
-    move.subTurn = this.subTurn; //much easier
-    if (this.movesCount >= 2 || this.subTurn == 2 || move.vanish.length == 0) {
-      this.turn = V.GetOppCol(this.turn);
-      this.subTurn = 1;
-      this.movesCount++;
-    }
-    else this.subTurn = 2;
-    move.flags = JSON.stringify(this.aggregateFlags());
-    this.epSquares.push(this.getEpSquare(move));
-    V.PlayOnBoard(this.board, move);
-    this.postPlay(move);
-  }
-
-  postPlay(move) {
-    if (move.vanish.length == 0 && move.appear.length == 0) return;
-    super.postPlay(move);
-    const color = move.appear[0].c;
-    if (move.vanish.length == 0)
-      // Drop unpromoted piece:
-      this.reserve[color][move.appear[0].p]--;
-    else if (move.vanish.length == 2 && move.appear.length == 1)
-      // May capture a promoted piece:
-      this.reserve[color][V.MayDecode(move.vanish[1].p)]++;
-  }
-
-  undo(move) {
-    this.epSquares.pop();
-    this.disaggregateFlags(JSON.parse(move.flags));
-    V.UndoOnBoard(this.board, move);
-    if (this.movesCount >= 2 || this.subTurn == 1 || move.vanish.length == 0) {
-      this.turn = V.GetOppCol(this.turn);
-      this.movesCount--;
-    }
-    this.subTurn = move.subTurn;
-    this.postUndo(move);
-  }
-
-  postUndo(move) {
-    if (move.vanish.length == 0 && move.appear.length == 0) return;
-    super.postUndo(move);
-    const color = move.appear[0].c;
-    if (move.vanish.length == 0)
-      this.reserve[color][move.appear[0].p]++;
-    else if (move.vanish.length == 2 && move.appear.length == 1)
-      this.reserve[color][V.MayDecode(move.vanish[1].p)]--;
-  }
-
-  static get VALUES() {
-    return Object.assign(
-      {},
-      ChessRules.VALUES,
-      {
-        n: 2.5, //knight is weaker
-        g: 9,
-        s: 5,
-        h: 6,
-        d: 7,
-        c: 7,
-        w: 9,
-        m: 8,
-        a: 9
-      }
-    );
-  }
-
-  static get SEARCH_DEPTH() {
-    return 2;
-  }
-
-  getComputerMove() {
-    if (this.movesCount <= 1) {
-      // Special case: swap and pass at random
-      const moves1 = this.getAllValidMoves();
-      const m1 = moves1[randInt(moves1.length)];
-      this.play(m1);
-      if (m1.vanish.length == 0) {
-        this.undo(m1);
-        return m1;
-      }
-      const moves2 = this.getAllValidMoves();
-      const m2 = moves2[randInt(moves2.length)];
-      this.undo(m1);
-      return [m1, m2];
-    }
-    return super.getComputerMove();
-  }
-
-  evalPosition() {
-    let evaluation = super.evalPosition();
-    // Add reserves:
-    for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
-      const p = V.RESERVE_PIECES[i];
-      evaluation += this.reserve["w"][p] * V.VALUES[p];
-      evaluation -= this.reserve["b"][p] * V.VALUES[p];
-    }
-    return evaluation;
-  }
-
-  getNotation(move) {
-    if (move.vanish.length == 0) {
-      if (move.appear.length == 0) return "pass";
-      const pieceName =
-        (move.appear[0].p == V.PAWN ? "" : move.appear[0].p.toUpperCase());
-      return pieceName + "@" + V.CoordsToSquare(move.end);
-    }
-    if (move.appear.length == 2) {
-      if (move.appear[0].p != V.KING)
-        return V.CoordsToSquare(move.start) + "S" + V.CoordsToSquare(move.end);
-      return (move.end.y < move.start.y ? "0-0" : "0-0-0");
-    }
-    let notation = super.getNotation(move);
-    if (move.vanish[0].p != V.PAWN && move.appear[0].p != move.vanish[0].p)
-      // Add promotion indication:
-      notation += "=" + move.appear[0].p.toUpperCase();
-    return notation;
-  }
-
-};