New variant idea
[xogo.git] / base_rules.js
index 20ffbee..3d8e463 100644 (file)
@@ -97,6 +97,14 @@ export default class ChessRules {
     return true;
   }
 
+  // Allow to take (moving: not disappearing) own pieces?
+  get hasSelfCaptures() {
+    return (
+      this.options["recycle"] ||
+      (this.options["teleport"] && this.subTurnTeleport == 1)
+    );
+  }
+
   get hasReserve() {
     return (
       !!this.options["crazyhouse"] ||
@@ -167,6 +175,22 @@ export default class ChessRules {
     return Object.values(cd).map(c => c.toString(36)).join("");
   }
 
+  // c10 --> 02 (assuming 10 rows)
+  static SquareFromUsual(sq) {
+    return (
+      (this.size.x - parseInt(sq.substring(1), 10)).toString(36) +
+      (sq.charCodeAt(0) - 97).toString(36)
+    );
+  }
+
+  // 02 --> c10
+  static UsualFromSquare(sq) {
+    return (
+      String.fromCharCode(parseInt(sq.charAt(1), 36) + 97) +
+      (this.size.x - parseInt(sq.charAt(0), 36)).toString(10)
+    );
+  }
+
   coordsToId(cd) {
     if (typeof cd.x == "number") {
       return (
@@ -225,7 +249,7 @@ export default class ChessRules {
       ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
       {
         randomness: this.options["randomness"],
-        between: {p1: 'k', p2: 'r'},
+        between: [{p1: 'k', p2: 'r'}],
         diffCol: ['b'],
         flags: ['r']
       }
@@ -316,7 +340,7 @@ export default class ChessRules {
 
   // Flags part of the FEN string
   getFlagsFen() {
-    return ["w", "b"].map(c => {
+    return ['w', 'b'].map(c => {
       return this.castleFlags[c].map(x => x.toString(36)).join("");
     }).join("");
   }
@@ -330,9 +354,9 @@ export default class ChessRules {
 
   getReserveFen(o) {
     if (o.init)
-      return "000000000000";
+      return Array(2 * V.ReserveArray.length).fill('0').join("");
     return (
-      ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("")
+      ['w', 'b'].map(c => Object.values(this.reserve[c]).join("")).join("")
     );
   }
 
@@ -408,14 +432,14 @@ export default class ChessRules {
   }
 
   // Some additional variables from FEN (variant dependant)
-  setOtherVariables(fenParsed, pieceArray) {
+  setOtherVariables(fenParsed) {
     // Set flags and enpassant:
     if (this.hasFlags)
       this.setFlags(fenParsed.flags);
     if (this.hasEnpassant)
       this.epSquare = this.getEpSquare(fenParsed.enpassant);
     if (this.hasReserve && !this.isDiagram)
-      this.initReserves(fenParsed.reserve, pieceArray);
+      this.initReserves(fenParsed.reserve);
     if (this.options["crazyhouse"])
       this.initIspawn(fenParsed.ispawn);
     if (this.options["teleport"]) {
@@ -433,14 +457,16 @@ export default class ChessRules {
   }
 
   // ordering as in pieces() p,r,n,b,q,k
-  initReserves(reserveStr, pieceArray) {
-    if (!pieceArray)
-      pieceArray = ['p', 'r', 'n', 'b', 'q', 'k'];
+  static get ReserveArray() {
+    return ['p', 'r', 'n', 'b', 'q', 'k'];
+  }
+
+  initReserves(reserveStr) {
     const counts = reserveStr.split("").map(c => parseInt(c, 36));
-    const L = pieceArray.length;
+    const L = V.ReserveArray.length;
     this.reserve = {
-      w: ArrayFun.toObject(pieceArray, counts.slice(0, L)),
-      b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L))
+      w: ArrayFun.toObject(V.ReserveArray, counts.slice(0, L)),
+      b: ArrayFun.toObject(V.ReserveArray, counts.slice(L, 2 * L))
     };
   }
 
@@ -455,7 +481,7 @@ export default class ChessRules {
   // VISUAL UTILS
 
   getPieceWidth(rwidth) {
-    return (rwidth / this.size.y);
+    return (rwidth / Math.max(this.size.x, this.size.y));
   }
 
   getReserveSquareSize(rwidth, nbR) {
@@ -571,11 +597,18 @@ export default class ChessRules {
 
   // Get SVG board (background, no pieces)
   getSvgChessboard() {
-    const flipped = this.flippedBoard;
     let board = `
       <svg
         viewBox="0 0 ${10*this.size.y} ${10*this.size.x}"
         class="chessboard_SVG">`;
+    board += this.getBaseSvgChessboard();
+    board += "</svg>";
+    return board;
+  }
+
+  getBaseSvgChessboard() {
+    let board = "";
+    const flipped = this.flippedBoard;
     for (let i=0; i < this.size.x; i++) {
       for (let j=0; j < this.size.y; j++) {
         if (!this.onBoard(i, j))
@@ -597,7 +630,6 @@ export default class ChessRules {
           />`;
       }
     }
-    board += "</svg>";
     return board;
   }
 
@@ -634,7 +666,11 @@ export default class ChessRules {
       else
         this[arrName] = ArrayFun.init(this.size.x, this.size.y, null);
       if (arrName == "d_pieces")
-        this.marks.forEach(([i, j]) => addPiece(i, j, arrName, "mark"));
+        this.marks.forEach((m) => {
+          const formattedSquare = C.SquareFromUsual(m);
+          const mCoords = C.SquareToCoords(formattedSquare);
+          addPiece(mCoords.x, mCoords.y, arrName, "mark");
+        });
     };
     if (this.marks)
       conditionalReset("d_pieces");
@@ -834,9 +870,10 @@ export default class ChessRules {
       y = (this.playerColor == i ? y = r.height + 5 : - 5 - rsqSize);
     }
     else {
-      const sqSize = r.width / this.size.y;
+      const sqSize = r.width / Math.max(this.size.x, this.size.y);
       const flipped = this.flippedBoard;
-      x = (flipped ? this.size.y - 1 - j : j) * sqSize;
+      x = (flipped ? this.size.y - 1 - j : j) * sqSize +
+          Math.abs(this.size.x - this.size.y) * sqSize / 2;
       y = (flipped ? this.size.x - 1 - i : i) * sqSize;
     }
     return [r.x + x, r.y + y];
@@ -1188,7 +1225,7 @@ export default class ChessRules {
   }
 
   pieces(color, x, y) {
-    const pawnShift = this.getPawnShift(color);
+    const pawnShift = this.getPawnShift(color || 'w');
     return {
       'p': {
         "class": "pawn",
@@ -1512,12 +1549,8 @@ export default class ChessRules {
     let moves = this.getPotentialMovesOf(piece, [x, y]);
     if (piece == "p" && this.hasEnpassant && this.epSquare)
       Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y]));
-    if (
-      this.isKing(0, 0, piece) && this.hasCastle &&
-      this.castleFlags[color || this.turn].some(v => v < this.size.y)
-    ) {
+    if (this.isKing(0, 0, piece) && this.hasCastle)
       Array.prototype.push.apply(moves, this.getCastleMoves([x, y]));
-    }
     return this.postProcessPotentialMoves(moves);
   }
 
@@ -1720,10 +1753,7 @@ export default class ChessRules {
       });
       Array.prototype.push.apply(squares, zenCaptures);
     }
-    if (
-      this.options["recycle"] ||
-      (this.options["teleport"] && this.subTurnTeleport == 1)
-    ) {
+    if (this.hasSelfCaptures) {
       const selfCaptures = this.findDestSquares(
         [x, y],
         {
@@ -1791,7 +1821,10 @@ export default class ChessRules {
               continue outerLoop;
             const oldIJ = [i, j];
             [i, j] = this.increment([i, j], step);
-            if (Math.abs(j - oldIJ[1]) > 1 || Math.abs(i - oldIJ[0]) > 1) {
+            if (
+              Math.abs(i - oldIJ[0]) != Math.abs(step[0]) ||
+              Math.abs(j - oldIJ[1]) != Math.abs(step[1])
+            ) {
               // Boundary between segments (cylinder or circular mode)
               segments.push([[segStart[0], segStart[1]], oldIJ]);
               segStart = [i, j];
@@ -2010,8 +2043,9 @@ export default class ChessRules {
     return [];
   }
 
-  getCastleMoves([x, y], finalSquares, castleWith) {
+  getCastleMoves([x, y], finalSquares, castleWith, castleFlags) {
     const c = this.getColor(x, y);
+    castleFlags = castleFlags || this.castleFlags[c];
 
     // Castling ?
     const oppCols = this.getOppCols(c);
@@ -2025,12 +2059,12 @@ export default class ChessRules {
       castleSide < 2;
       castleSide++ //large, then small
     ) {
-      if (this.castleFlags[c][castleSide] >= this.size.y)
+      if (castleFlags[castleSide] >= this.size.y)
         continue;
       // If this code is reached, rook and king are on initial position
 
       // NOTE: in some variants this is not a rook
-      const rookPos = this.castleFlags[c][castleSide];
+      const rookPos = castleFlags[castleSide];
       const castlingPiece = this.getPiece(x, rookPos);
       if (
         this.board[x][rookPos] == "" ||
@@ -2233,11 +2267,20 @@ export default class ChessRules {
       this.board[psq.x][psq.y] = psq.c + psq.p;
   }
 
-  updateCastleFlags(move) {
+  // NOTE: arg "castleFlags" for Coregal or Twokings
+  updateCastleFlags(move, castleFlags, king) {
+    castleFlags = castleFlags || this.castleFlags;
+    // If flags already off, no need to re-check:
+    if (
+      Object.values(castleFlags).every(cvals =>
+        cvals.every(val => val >= this.size.y))
+    ) {
+      return;
+    }
     // Update castling flags if start or arrive from/at rook/king locations
     move.appear.concat(move.vanish).forEach(psq => {
-      if (this.isKing(0, 0, psq.p))
-        this.castleFlags[psq.c] = [this.size.y, this.size.y];
+      if ((king && psq.p == king) || (!king && this.isKing(0, 0, psq.p)))
+        castleFlags[psq.c] = [this.size.y, this.size.y];
       // NOTE: not "else if" because king can capture enemy rook...
       let c = "";
       if (psq.x == 0)
@@ -2245,28 +2288,17 @@ export default class ChessRules {
       else if (psq.x == this.size.x - 1)
         c = "w";
       if (c != "") {
-        const fidx = this.castleFlags[c].findIndex(f => f == psq.y);
+        const fidx = castleFlags[c].findIndex(f => f == psq.y);
         if (fidx >= 0)
-          this.castleFlags[c][fidx] = this.size.y;
+          castleFlags[c][fidx] = this.size.y;
       }
     });
   }
 
   prePlay(move) {
-    if (
-      this.hasCastle &&
-      // If flags already off, no need to re-check:
-      Object.values(this.castleFlags).some(cvals =>
-        cvals.some(val => val < this.size.y))
-    ) {
+    if (this.hasCastle)
       this.updateCastleFlags(move);
-    }
     if (this.options["crazyhouse"]) {
-      move.vanish.forEach(v => {
-        const square = C.CoordsToSquare({x: v.x, y: v.y});
-        if (this.ispawn[square])
-          delete this.ispawn[square];
-      });
       if (move.appear.length > 0 && move.vanish.length > 0) {
         // Assumption: something is moving
         const initSquare = C.CoordsToSquare(move.start);
@@ -2285,6 +2317,11 @@ export default class ChessRules {
           delete this.ispawn[destSquare];
         }
       }
+      move.vanish.forEach(v => {
+        const square = C.CoordsToSquare({x: v.x, y: v.y});
+        if (this.ispawn[square])
+          delete this.ispawn[square];
+      });
     }
     const minSize = Math.min(move.appear.length, move.vanish.length);
     if (
@@ -2327,10 +2364,11 @@ export default class ChessRules {
     if (this.options["teleport"]) {
       if (
         this.subTurnTeleport == 1 &&
-        move.vanish.length > move.appear.length &&
+        move.vanish.length == 2 &&
+        move.appear.length == 1 &&
         move.vanish[1].c == this.turn
       ) {
-        const v = move.vanish[move.vanish.length - 1];
+        const v = move.vanish[1];
         this.captured = {x: v.x, y: v.y, c: v.c, p: v.p};
         this.subTurnTeleport = 2;
         return;