Add Tencubed and Omega variants + some fixes (updateCastleFlags()) + cleaner FEN...
[vchess.git] / client / src / variants / Grand.js
index d28f639..aeb7b6f 100644 (file)
@@ -4,41 +4,41 @@ import { randInt } from "@/utils/alea";
 
 // NOTE: initial setup differs from the original; see
 // https://www.chessvariants.com/large.dir/freeling.html
-export const VariantRules = class GrandRules extends ChessRules {
-  static getPpath(b) {
-    return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
-  }
-
+export class GrandRules extends ChessRules {
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
     // 5) Check captures
-    if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{14,14}$/))
+    if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{12,12}$/))
       return false;
     return true;
   }
 
   static IsGoodEnpassant(enpassant) {
-    if (enpassant != "-") {
-      const squares = enpassant.split(",");
-      if (squares.length > 2) return false;
-      for (let sq of squares) {
-        const ep = V.SquareToCoords(sq);
-        if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
-      }
-    }
+    if (enpassant != "-") return !!enpassant.match(/^([a-j][0-9]{1,2},?)+$/);
     return true;
   }
 
   static ParseFen(fen) {
     const fenParts = fen.split(" ");
-    return Object.assign(ChessRules.ParseFen(fen), { captured: fenParts[5] });
+    return Object.assign(
+      ChessRules.ParseFen(fen),
+      { captured: fenParts[5] }
+    );
+  }
+
+  getPpath(b) {
+    return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
   }
 
   getFen() {
     return super.getFen() + " " + this.getCapturedFen();
   }
 
+  getFenForRepeat() {
+    return super.getFenForRepeat() + "_" + this.getCapturedFen();
+  }
+
   getCapturedFen() {
     let counts = [...Array(14).fill(0)];
     let i = 0;
@@ -83,12 +83,15 @@ export const VariantRules = class GrandRules extends ChessRules {
     return { x: 10, y: 10 };
   }
 
+  // Rook + knight:
   static get MARSHALL() {
     return "m";
-  } //rook+knight
+  }
+
+  // Bishop + knight
   static get CARDINAL() {
     return "c";
-  } //bishop+knight
+  }
 
   static get PIECES() {
     return ChessRules.PIECES.concat([V.MARSHALL, V.CARDINAL]);
@@ -129,7 +132,7 @@ export const VariantRules = class GrandRules extends ChessRules {
         }
       ];
       if (sx + 2 * step != ex) {
-        //3-squares move
+        // 3-squares jump
         res.push({
           x: sx + 2 * step,
           y: sy
@@ -215,11 +218,11 @@ export const VariantRules = class GrandRules extends ChessRules {
     // En passant
     const Lep = this.epSquares.length;
     const epSquare = this.epSquares[Lep - 1];
-    if (epSquare) {
+    if (!!epSquare) {
       for (let epsq of epSquare) {
         // TODO: some redundant checks
         if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
-          var enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
+          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;
@@ -251,20 +254,20 @@ export const VariantRules = class GrandRules extends ChessRules {
     );
   }
 
-  isAttacked(sq, colors) {
+  isAttacked(sq, color) {
     return (
-      super.isAttacked(sq, colors) ||
-      this.isAttackedByMarshall(sq, colors) ||
-      this.isAttackedByCardinal(sq, colors)
+      super.isAttacked(sq, color) ||
+      this.isAttackedByMarshall(sq, color) ||
+      this.isAttackedByCardinal(sq, color)
     );
   }
 
-  isAttackedByMarshall(sq, colors) {
+  isAttackedByMarshall(sq, color) {
     return (
-      this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK]) ||
+      this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.ROOK]) ||
       this.isAttackedBySlideNJump(
         sq,
-        colors,
+        color,
         V.MARSHALL,
         V.steps[V.KNIGHT],
         "oneStep"
@@ -272,12 +275,12 @@ export const VariantRules = class GrandRules extends ChessRules {
     );
   }
 
-  isAttackedByCardinal(sq, colors) {
+  isAttackedByCardinal(sq, color) {
     return (
-      this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP]) ||
+      this.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
       this.isAttackedBySlideNJump(
         sq,
-        colors,
+        color,
         V.CARDINAL,
         V.steps[V.KNIGHT],
         "oneStep"
@@ -285,8 +288,8 @@ export const VariantRules = class GrandRules extends ChessRules {
     );
   }
 
-  updateVariables(move) {
-    super.updateVariables(move);
+  postPlay(move) {
+    super.postPlay(move);
     if (move.vanish.length == 2 && move.appear.length == 1) {
       // Capture: update this.captured
       this.captured[move.vanish[1].c][move.vanish[1].p]++;
@@ -297,8 +300,8 @@ export const VariantRules = class GrandRules extends ChessRules {
     }
   }
 
-  unupdateVariables(move) {
-    super.unupdateVariables(move);
+  postUndo(move) {
+    super.postUndo(move);
     if (move.vanish.length == 2 && move.appear.length == 1)
       this.captured[move.vanish[1].c][move.vanish[1].p]--;
     if (move.vanish[0].p != move.appear[0].p)
@@ -307,8 +310,8 @@ export const VariantRules = class GrandRules extends ChessRules {
 
   static get VALUES() {
     return Object.assign(
-      ChessRules.VALUES,
-      { c: 5, m: 7 } //experimental
+      { c: 5, m: 7 }, //experimental
+      ChessRules.VALUES
     );
   }
 
@@ -316,11 +319,25 @@ export const VariantRules = class GrandRules extends ChessRules {
     return 2;
   }
 
-  // TODO: this function could be generalized and shared better (how ?!...)
-  static GenRandInitFen() {
+  static GenRandInitFen(randomness) {
+    if (randomness == 0) {
+      return (
+        "r8r/1nbqkmcbn1/pppppppppp/91/91/91/91/PPPPPPPPPP/1NBQKMCBN1/R8R " +
+        // No castling in the official initial setup
+        "w 0 zzzz - 00000000000000"
+      );
+    }
+
     let pieces = { w: new Array(10), b: new Array(10) };
+    let flags = "";
     // Shuffle pieces on first and last rank
     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
@@ -356,7 +373,8 @@ export const VariantRules = class GrandRules extends ChessRules {
       let cardinalPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
-      // Rooks and king positions are now fixed, because of the ordering rook-king-rook
+      // Rooks and king positions are now fixed,
+      // because of the ordering rook-king-rook
       let rook1Pos = positions[0];
       let kingPos = positions[1];
       let rook2Pos = positions[2];
@@ -372,12 +390,13 @@ export const VariantRules = class GrandRules extends ChessRules {
       pieces[c][bishop2Pos] = "b";
       pieces[c][knight2Pos] = "n";
       pieces[c][rook2Pos] = "r";
+      flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
     }
     return (
       pieces["b"].join("") +
-      "/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
+      "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0 1111 - 00000000000000"
+      " w 0 " + flags + " - 00000000000000"
     );
   }
 };