Pandemonium: promotion on last 2 ranks
[vchess.git] / client / src / variants / Pandemonium.js
index 287aa3b..4894ec1 100644 (file)
@@ -1,17 +1,12 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
+import { ArrayFun } from "@/utils/array";
 
 export class PandemoniumRules extends ChessRules {
 
-  static get PawnSpecs() {
-    return Object.assign(
-      {},
-      ChessRules.PawnSpecs,
-      {
-        threeSquares: true,
-        promotions: [V.GILDING]
-      }
-    );
+  loseOnRepetition() {
+    // If current side is under check: lost
+    return this.underCheck(this.turn);
   }
 
   static get GILDING() {
@@ -147,23 +142,74 @@ export class PandemoniumRules extends ChessRules {
     return counts.join("");
   }
 
-  setFlags(fenflags) {
-    // white a-castle, h-castle, king pos, then same for black.
-    this.castleFlags = { w: [-1, -1, -1], b: [-1, -1, -1] };
-    for (let i = 0; i < 6; i++) {
-      this.castleFlags[i < 3 ? "w" : "b"][i % 3] =
-        V.ColumnToCoord(fenflags.charAt(i));
+  static GenRandInitFen(randomness) {
+    if (randomness == 0) {
+      return (
+        "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
+        "w 0 ajaj - 00000000000000"
+      );
     }
-  }
 
-  static GenRandInitFen(randomness) {
-    // No randomization here for now (but initial setup choice)
+    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 (
-      "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
-      "w 0 ajeaje - 00000000000000"
+      pieces["b"].join("") +
+      "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
+      pieces["w"].join("").toUpperCase() +
+      " w 0 " + flags + " - 00000000000000"
     );
-    // TODO later: randomization too --> 2 bishops, not next to each other.
-    // then knights next to bishops. Then other pieces (...).
   }
 
   getEnpassantFen() {
@@ -223,6 +269,7 @@ export class PandemoniumRules extends ChessRules {
 
   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]);
@@ -285,54 +332,53 @@ export class PandemoniumRules extends ChessRules {
         ];
       }
       const firstRank = (this.movesCount == 0 ? 9 : 0);
-      // TODO: initDestFile currently hardcoded for deterministic setup
-      const initDestFile = new Map([[1, 2], [8, 7]]);
-      // Only option is knight --> bishop swap:
-      if (
-        x == firstRank &&
-        !!initDestFile.get(y) &&
-        this.getPiece(x, y) == V.KNIGHT
-      ) {
-        const destFile = initDestFile.get(y);
-        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 }
-          })
-        ];
+      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);
       }
-      return [];
+      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);
+    if (x >= V.size.x) return this.getReserveMoves([x, y]);
     const p = this.getPiece(x, y);
     const sq = [x, y];
     let moves = [];
@@ -360,10 +406,10 @@ export class PandemoniumRules extends ChessRules {
     // Maybe apply promotions:
     if (Object.keys(V.PromoteMap).includes(p)) {
       const promoted = V.PromoteMap[p];
-      const lastRank = (c == 'w' ? 0 : 9);
+      const lastRanks = (c == 'w' ? [0, 1] : [9, 8]);
       let promotions = [];
       moves.forEach(m => {
-        if (m.start.x == lastRank || m.end.x == lastRank) {
+        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);
@@ -374,19 +420,34 @@ export class PandemoniumRules extends ChessRules {
     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 = V.PawnSpecs.directions[color];
+    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 <= 3)) {
+      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 <= 2)
+              (color == 'w' && x == V.size.x - 2) ||
+              (color == 'b' && x == 1)
             )
             &&
             this.board[x + 3 * shiftX][y] == V.EMPTY
@@ -477,25 +538,14 @@ export class PandemoniumRules extends ChessRules {
     if (
       this.castleFlags[c][0] < V.size.y ||
       this.castleFlags[c][1] < V.size.y
-    ) {
-      moves = moves.concat(this.getCastleMoves(sq));
-    }
-    return moves;
-  }
-
-  getCastleMoves([x, y]) {
-    const c = this.getColor(x, y);
-    if (
-      ((c == 'w' && x == 9) || (c == 'b' && x == 0)) &&
-      y == this.castleFlags[c][2]
     ) {
       const finalSquares = [
         [1, 2],
         [7, 6]
       ];
-      return super.getCastleMoves([x, y], finalSquares, false, [V.ROOK]);
+      moves = moves.concat(super.getCastleMoves(sq, finalSquares));
     }
-    return [];
+    return moves;
   }
 
   isAttacked(sq, color) {
@@ -541,23 +591,23 @@ export class PandemoniumRules extends ChessRules {
     const steps =
       V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
     return (
-      super.isAttackedBySlideNJump(sq, color, steps, V.SCEPTER, "oneStep")
+      super.isAttackedBySlideNJump(sq, color, V.SCEPTER, steps, "oneStep")
     );
   }
 
   isAttackedByHorse(sq, color) {
     return (
-      super.isAttackedBySlideNJump(sq, color, V.steps[V.BISHOP], V.HORSE) ||
+      super.isAttackedBySlideNJump(sq, color, V.HORSE, V.steps[V.BISHOP]) ||
       super.isAttackedBySlideNJump(
-        sq, color, V.steps[V.ROOK], V.HORSE, "oneStep")
+        sq, color, V.HORSE, V.steps[V.ROOK], "oneStep")
     );
   }
 
   isAttackedByDragon(sq, color) {
     return (
-      super.isAttackedBySlideNJump(sq, color, V.steps[V.ROOK], V.DRAGON) ||
+      super.isAttackedBySlideNJump(sq, color, V.DRAGON, V.steps[V.ROOK]) ||
       super.isAttackedBySlideNJump(
-        sq, color, V.steps[V.BISHOP], V.DRAGON, "oneStep")
+        sq, color, V.DRAGON, V.steps[V.BISHOP], "oneStep")
     );
   }
 
@@ -623,7 +673,8 @@ export class PandemoniumRules extends ChessRules {
       s: 'n',
       h: 'b',
       w: 'c',
-      a: 'm'
+      a: 'm',
+      g: 'p'
     };
   }
 
@@ -647,14 +698,6 @@ export class PandemoniumRules extends ChessRules {
     this.postPlay(move);
   }
 
-  updateCastleFlags(move, piece) {
-    if (piece == V.KING && move.appear.length == 2) {
-      // Castling (only move which disable flags)
-      this.castleFlags[move.appear[0].c][0] = 10;
-      this.castleFlags[move.appear[0].c][1] = 10;
-    }
-  }
-
   postPlay(move) {
     if (move.vanish.length == 0 && move.appear.length == 0) return;
     super.postPlay(move);
@@ -689,37 +732,6 @@ export class PandemoniumRules extends ChessRules {
       this.reserve[color][V.MayDecode(move.vanish[1].p)]--;
   }
 
-  getCurrentScore() {
-    const c = this.turn,
-          oppCol = V.GetOppCol(this.turn);
-    let facingKings = false;
-    if (
-      this.kingPos[c][0] == this.kingPos[oppCol][0] ||
-      this.kingPos[c][1] == this.kingPos[oppCol][1]
-    ) {
-      facingKings = true;
-      let step = [
-        this.kingPos[oppCol][0] - this.kingPos[c][0],
-        this.kingPos[oppCol][1] - this.kingPos[c][1]
-      ];
-      if (step[0] != 0) step[0] /= Math.abs(step[0]);
-      else step[1] /= Math.abs(step[1]);
-      let [x, y] =
-        [ this.kingPos[c][0] + step[0], this.kingPos[c][1] + step[1] ];
-      while (x != this.kingPos[oppCol][0] || y != this.kingPos[oppCol][1]) {
-        if (this.board[x][y] != V.EMPTY) {
-          facingKings = false;
-          break;
-        }
-        x += step[0];
-        y += step[1];
-      }
-    }
-    if (facingKings) return (c == "w" ? "1-0" : "0-1");
-    if (!this.atLeastOneMove()) return (c == "w" ? "0-1" : "1-0");
-    return "*";
-  }
-
   static get VALUES() {
     return Object.assign(
       {},