Pandemonium 1 & 2, Stealthbomb 1 & 2
[vchess.git] / client / src / variants / Wildebeest.js
index 1634f49..93353f9 100644 (file)
@@ -1,8 +1,9 @@
-import { ChessRules } from "@/base_rules";
+import { ChessRules, Move, PiPo } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { sample, randInt } from "@/utils/alea";
 
-export const VariantRules = class WildebeestRules extends ChessRules {
+export class WildebeestRules extends ChessRules {
+
   static get size() {
     return { x: 10, y: 11 };
   }
@@ -20,7 +21,9 @@ export const VariantRules = class WildebeestRules extends ChessRules {
 
   static get steps() {
     return Object.assign(
-      ChessRules.steps, //add camel moves:
+      {},
+      ChessRules.steps,
+      // Add camel moves:
       {
         c: [
           [-3, -1],
@@ -37,14 +40,7 @@ export const VariantRules = class WildebeestRules extends ChessRules {
   }
 
   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;
   }
 
@@ -87,7 +83,7 @@ export const VariantRules = class WildebeestRules extends ChessRules {
         }
       ];
       if (sx + 2 * step != ex) {
-        //3-squares move
+        // 3-squares move
         res.push({
           x: sx + 2 * step,
           y: sy
@@ -116,10 +112,12 @@ export const VariantRules = class WildebeestRules extends ChessRules {
     const [sizeX, sizeY] = [V.size.x, V.size.y];
     const shiftX = color == "w" ? -1 : 1;
     const startRanks = color == "w" ? [sizeX - 2, sizeX - 3] : [1, 2];
-    const lastRank = color == "w" ? 0 : sizeX - 1;
-    const finalPieces = x + shiftX == lastRank
-      ? [V.WILDEBEEST, V.QUEEN]
-      : [V.PAWN];
+    const lastRanks = color == "w" ? [0, 1] : [sizeX - 1, sizeX  -2];
+    let finalPieces = [V.PAWN];
+    if (x + shiftX == lastRanks[1])
+      Array.prototype.push.apply(finalPieces, [V.WILDEBEEST, V.QUEEN]);
+    else if (x + shiftX == lastRanks[0])
+      finalPieces = [V.WILDEBEEST, V.QUEEN];
 
     if (this.board[x + shiftX][y] == V.EMPTY) {
       // One square forward
@@ -160,11 +158,11 @@ export const VariantRules = class WildebeestRules 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;
@@ -182,8 +180,6 @@ export const VariantRules = class WildebeestRules extends ChessRules {
     return moves;
   }
 
-  // TODO: wildebeest castle
-
   getPotentialCamelMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], "oneStep");
   }
@@ -196,28 +192,102 @@ export const VariantRules = class WildebeestRules extends ChessRules {
     );
   }
 
-  isAttacked(sq, colors) {
+  getPPpath(m) {
+    if (
+      m.appear.length == 2 && m.vanish.length == 2 &&
+      Math.abs(m.end.y - m.start.y) == 1 &&
+      this.board[m.end.x][m.end.y] == V.EMPTY
+    ) {
+      // Castle, king moved by one square only, not directly onto rook
+      return "Wildebeest/castle";
+    }
+    return super.getPPpath(m);
+  }
+
+  // Special Wildebeest castling rules:
+  getCastleMoves([x, y]) {
+    const c = this.getColor(x, y);
+    const oppCol = V.GetOppCol(c);
+    let moves = [];
+    let i = 0;
+    const castlingKing = this.board[x][y].charAt(1);
+    castlingCheck: for (
+      let castleSide = 0;
+      castleSide < 2;
+      castleSide++ //"large", then "small"
+    ) {
+      if (this.castleFlags[c][castleSide] >= V.size.y) continue;
+      // Rook and king are on initial position
+      const rookPos = this.castleFlags[c][castleSide];
+      const range = (castleSide == 0 ? [rookPos, y] : [y, rookPos]);
+
+      // King and rook must be connected:
+      for (let i = range[0] + 1; i <= range[1] - 1; i++) {
+        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
+      }
+      const step = 2 * castleSide - 1;
+      // No attacks on the path of the king ?
+      for (let i = range[0]; i <= range[1]; i++) {
+        if (i != rookPos && this.isAttacked([x, i], oppCol))
+          continue castlingCheck;
+        if (
+          i != y &&
+          // Do not end in the corner, except if starting square is too near
+          (i > 0 || y == 1) &&
+          (i < V.size.y - 1 || y == V.size.y - 2)
+        ) {
+          // Found a possible castle move:
+          moves.push(
+            new Move({
+              appear: [
+                new PiPo({
+                  x: x,
+                  y: i,
+                  p: V.KING,
+                  c: c
+                }),
+                new PiPo({
+                  x: x,
+                  y: i - step,
+                  p: V.ROOK,
+                  c: c
+                })
+              ],
+              vanish: [
+                new PiPo({ x: x, y: y, p: V.KING, c: c }),
+                new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
+              ]
+            })
+          );
+        }
+      }
+    }
+
+    return moves;
+  }
+
+  isAttacked(sq, color) {
     return (
-      super.isAttacked(sq, colors) ||
-      this.isAttackedByCamel(sq, colors) ||
-      this.isAttackedByWildebeest(sq, colors)
+      super.isAttacked(sq, color) ||
+      this.isAttackedByCamel(sq, color) ||
+      this.isAttackedByWildebeest(sq, color)
     );
   }
 
-  isAttackedByCamel(sq, colors) {
+  isAttackedByCamel(sq, color) {
     return this.isAttackedBySlideNJump(
       sq,
-      colors,
+      color,
       V.CAMEL,
       V.steps[V.CAMEL],
       "oneStep"
     );
   }
 
-  isAttackedByWildebeest(sq, colors) {
+  isAttackedByWildebeest(sq, color) {
     return this.isAttackedBySlideNJump(
       sq,
-      colors,
+      color,
       V.WILDEBEEST,
       V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
       "oneStep"
@@ -225,18 +295,15 @@ export const VariantRules = class WildebeestRules extends ChessRules {
   }
 
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      // game not over
-      return "*";
-
+    if (this.atLeastOneMove()) return "*";
     // No valid move: game is lost (stalemate is a win)
     return this.turn == "w" ? "0-1" : "1-0";
   }
 
   static get VALUES() {
     return Object.assign(
-      ChessRules.VALUES,
-      { c: 3, w: 7 } //experimental
+      { c: 3, w: 7 }, //experimental
+      ChessRules.VALUES
     );
   }
 
@@ -244,9 +311,23 @@ export const VariantRules = class WildebeestRules extends ChessRules {
     return 2;
   }
 
-  static GenRandInitFen() {
-    let pieces = { w: new Array(10), b: new Array(10) };
+  static GenRandInitFen(randomness) {
+    if (randomness == 0) {
+      return (
+        "rnccwkqbbnr/ppppppppppp/92/92/92/92/92/92/PPPPPPPPPPP/RNBBQKWCCNR " +
+        "w 0 akak -"
+      );
+    }
+
+    let pieces = { w: new Array(11), b: new Array(11) };
+    let flags = "";
     for (let c of ["w", "b"]) {
+      if (c == 'b' && randomness == 1) {
+        pieces['b'] = pieces['w'];
+        flags += flags;
+        break;
+      }
+
       let positions = ArrayFun.range(11);
 
       // Get random squares for bishops + camels (different colors)
@@ -264,7 +345,7 @@ export const VariantRules = class WildebeestRules extends ChessRules {
       for (let idx of randIndexes.concat(randIndexes_tmp).sort((a, b) => {
         return b - a;
       })) {
-        //largest indices first
+        // Largest indices first
         positions.splice(idx, 1);
       }
 
@@ -299,12 +380,14 @@ export const VariantRules = class WildebeestRules 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("") +
-      "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
+      "/ppppppppppp/92/92/92/92/92/92/PPPPPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0 1111 -"
+      " w 0 " + flags + " -"
     );
   }
+
 };