Fix parseInt() usage, rename Doubleorda --> Ordamirror, implement Clorange variant
[vchess.git] / client / src / variants / Alice.js
index d64c077..61fcd01 100644 (file)
@@ -2,8 +2,9 @@ import { ChessRules } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 
 // NOTE: alternative implementation, probably cleaner = use only 1 board
-// TODO? atLeastOneMove() would be more efficient if rewritten here (less sideBoard computations)
-export const VariantRules = class AliceRules extends ChessRules {
+// TODO? atLeastOneMove() would be more efficient if rewritten here
+// (less sideBoard computations)
+export class AliceRules extends ChessRules {
   static get ALICE_PIECES() {
     return {
       s: "p",
@@ -33,11 +34,62 @@ export const VariantRules = class AliceRules extends ChessRules {
     return (Object.keys(V.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
   }
 
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
+      const square = moveOrSquare;
+      if (square == "-") return undefined;
+      return V.SquareToCoords(square);
+    }
+    // Argument is a move:
+    const move = moveOrSquare;
+    const s = move.start,
+          e = move.end;
+    if (
+      s.y == e.y &&
+      Math.abs(s.x - e.x) == 2 &&
+      // Special conditions: a pawn can be on the other side
+      ['p','s'].includes(move.appear[0].p) &&
+      ['p','s'].includes(move.vanish[0].p)
+    ) {
+      return {
+        x: (s.x + e.x) / 2,
+        y: s.y
+      };
+    }
+    return undefined; //default
+  }
+
+  // king can be l or L (on the other mirror side)
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
+    const rows = position.split("/");
+    if (rows.length != V.size.x) return false;
+    let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 };
+    for (let row of rows) {
+      let sumElts = 0;
+      for (let i = 0; i < row.length; i++) {
+        if (['K','k','L','l'].includes(row[i])) kings[row[i]]++;
+        if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
+        else {
+          const num = parseInt(row[i], 10);
+          if (isNaN(num)) return false;
+          sumElts += num;
+        }
+      }
+      if (sumElts != V.size.y) return false;
+    }
+    if (kings['k'] + kings['l'] != 1 || kings['K'] + kings['L'] != 1)
+      return false;
+    return true;
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const rows = V.ParseFen(fen).position.split("/");
     if (this.kingPos["w"][0] < 0 || this.kingPos["b"][0] < 0) {
-      // INIT_COL_XXX won't be required if Alice kings are found (means 'king moved')
+      // INIT_COL_XXX won't be required if Alice kings are found
+      // (it means 'king moved')
       for (let i = 0; i < rows.length; i++) {
         let k = 0; //column index on board
         for (let j = 0; j < rows[i].length; j++) {
@@ -49,7 +101,7 @@ export const VariantRules = class AliceRules extends ChessRules {
               this.kingPos["w"] = [i, k];
               break;
             default: {
-              const num = parseInt(rows[i].charAt(j));
+              const num = parseInt(rows[i].charAt(j), 10);
               if (!isNaN(num)) k += num - 1;
             }
           }
@@ -80,7 +132,8 @@ export const VariantRules = class AliceRules extends ChessRules {
     return sideBoard;
   }
 
-  // NOTE: castle & enPassant https://www.chessvariants.com/other.dir/alice.html
+  // NOTE: castle & enPassant
+  // https://www.chessvariants.com/other.dir/alice.html
   getPotentialMovesFrom([x, y], sideBoard) {
     const pieces = Object.keys(V.ALICE_CODES);
     const codes = Object.keys(V.ALICE_PIECES);
@@ -109,8 +162,11 @@ export const VariantRules = class AliceRules extends ChessRules {
       if (m.appear.length == 2) {
         // Castle: appear[i] must be an empty square on the other board
         for (let psq of m.appear) {
-          if (this.getSquareOccupation(psq.x, psq.y, 3 - mirrorSide) != V.EMPTY)
+          if (
+            this.getSquareOccupation(psq.x, psq.y, 3 - mirrorSide) != V.EMPTY
+          ) {
             return false;
+          }
         }
       } else if (this.board[m.end.x][m.end.y] != V.EMPTY) {
         // Attempt to capture
@@ -141,18 +197,9 @@ export const VariantRules = class AliceRules extends ChessRules {
         m.vanish.length == 2 &&
         this.board[m.end.x][m.end.y] == V.EMPTY
       ) {
-        m.vanish[1].c = V.GetOppCol(this.getColor(x, y));
-        // In the special case of en-passant, if
-        //  - board1 takes board2 : vanish[1] --> Alice
-        //  - board2 takes board1 : vanish[1] --> normal
-        let van = m.vanish[1];
-        if (mirrorSide == 1 && codes.includes(this.getPiece(van.x, van.y)))
-          van.p = V.ALICE_CODES[van.p];
-        else if (
-          mirrorSide == 2 &&
-          pieces.includes(this.getPiece(van.x, van.y))
-        )
-          van.p = V.ALICE_PIECES[van.p];
+        m.vanish[1].c = V.GetOppCol(this.turn);
+        const [epX, epY] = [m.vanish[1].x, m.vanish[1].y];
+        m.vanish[1].p = this.getPiece(epX, epY);
       }
       return true;
     });
@@ -229,7 +276,8 @@ export const VariantRules = class AliceRules extends ChessRules {
     return res;
   }
 
-  getCheckSquares(color) {
+  getCheckSquares() {
+    const color = this.turn;
     const pieces = Object.keys(V.ALICE_CODES);
     const kp = this.kingPos[color];
     const mirrorSide = pieces.includes(this.getPiece(kp[0], kp[1])) ? 1 : 2;
@@ -263,10 +311,7 @@ export const VariantRules = class AliceRules extends ChessRules {
   }
 
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      // game not over
-      return "*";
-
+    if (this.atLeastOneMove()) return "*";
     const pieces = Object.keys(V.ALICE_CODES);
     const color = this.turn;
     const kp = this.kingPos[color];
@@ -316,10 +361,9 @@ export const VariantRules = class AliceRules extends ChessRules {
 
     // Piece or pawn movement
     let notation = piece.toUpperCase() + pawnMark + captureMark + finalSquare;
-    if (["s", "p"].includes(piece) && !["s", "p"].includes(move.appear[0].p)) {
+    if (["s", "p"].includes(piece) && !["s", "p"].includes(move.appear[0].p))
       // Promotion
       notation += "=" + move.appear[0].p.toUpperCase();
-    }
     return notation;
   }
 };