Fix Koopa promotions with captures, and Balakhlava: pawns move forward
[vchess.git] / client / src / variants / Koopa.js
index f24e5b2..57d19b9 100644 (file)
@@ -49,11 +49,9 @@ export class KoopaRules extends ChessRules {
   }
 
   getStunnedFen() {
-    return (
-      Object.keys(this.stunned)
-      .map(square => square + this.stunned[square])
-      .join(",")
-    );
+    const squares = Object.keys(this.stunned);
+    if (squares.length == 0) return "-";
+    return squares.map(square => square + this.stunned[square]).join(",");
   }
 
   // Base GenRandInitFen() is fine because en-passant indicator will
@@ -80,7 +78,7 @@ export class KoopaRules extends ChessRules {
             this.INIT_COL_KING["w"] = k;
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
@@ -100,7 +98,7 @@ export class KoopaRules extends ChessRules {
         .map(s => {
           return {
             square: s.substr(0, 2),
-            state: parseInt(s[2])
+            state: parseInt(s[2], 10)
           };
         });
     }
@@ -120,7 +118,17 @@ export class KoopaRules extends ChessRules {
   }
 
   getPotentialMovesFrom([x, y]) {
-    let moves = super.getPotentialMovesFrom([x, y]);
+    let moves = super.getPotentialMovesFrom([x, y]).filter(m => {
+      if (
+        m.vanish[0].p != V.PAWN ||
+        m.appear[0].p == V.PAWN ||
+        m.vanish.length == 1
+      ) {
+        return true;
+      }
+      // Pawn promotion, "capturing": remove duplicates
+      return m.appear[0].p == V.QUEEN;
+    });
     // Complete moves: stuns & kicks
     let promoteAfterStun = [];
     const color = this.turn;
@@ -186,7 +194,10 @@ export class KoopaRules extends ChessRules {
             m.appear[0].x = i;
             m.appear[0].y = j;
             // Is it a pawn on last rank?
-            if ((color == 'w' && i == 0) || (color == 'b' && i == 7)) {
+            if (
+              m.appear[0].p == V.PAWN &&
+              ((color == 'w' && i == 0) || (color == 'b' && i == 7))
+            ) {
               m.appear[0].p = V.ROOK;
               for (let ppiece of [V.KNIGHT, V.BISHOP, V.QUEEN]) {
                 let mp = JSON.parse(JSON.stringify(m));
@@ -204,18 +215,28 @@ export class KoopaRules extends ChessRules {
     return moves.concat(promoteAfterStun);
   }
 
+  getPotentialKingMoves(sq) {
+    return (
+      this.getSlideNJumpMoves(
+        sq,
+        V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+        "oneStep"
+      ).concat(super.getCastleMoves(sq, true, ['r']))
+    );
+  }
+
   filterValid(moves) {
     // Forbid kicking own king out
     const color = this.turn;
     return moves.filter(m => {
-      const kingAppear = m.appear.some(a => a.c == color && a.p == V.KING);
-      return m.vanish.every(v => {
-        return (
-          v.c != color ||
-          !["k", "l"].includes(v.p) ||
-          (v.p == "k" && kingAppear)
-        );
-      });
+      const kingVanish =
+        m.vanish.some(v => v.c == color && ['k', 'l'].includes(v.p));
+      if (kingVanish) {
+        const kingAppear =
+          m.appear.some(a => a.c == color && ['k', 'l'].includes(a.p));
+        return kingAppear;
+      }
+      return true;
     });
   }
 
@@ -234,10 +255,16 @@ export class KoopaRules extends ChessRules {
     // Base method is fine because a stunned king (which won't be detected)
     // can still castle after going back to normal.
     super.postPlay(move);
-    const kIdx = move.vanish.findIndex(v => v.p == "l");
-    if (kIdx >= 0)
-      // A stunned king vanish (game over)
-      this.kingPos[move.vanish[kIdx].c] = [-1, -1];
+    const color = this.turn;
+    const kp = this.kingPos[color];
+    if (
+      this.board[kp[0], kp[1]] == V.EMPTY ||
+      !['k', 'l'].includes(this.getPiece(kp[0], kp[1])) ||
+      this.getColor(kp[0], kp[1]) != color
+    ) {
+      // King didn't move by itself, and vanished => game over
+      this.kingPos[color] = [-1, -1];
+    }
     move.stunned = JSON.stringify(this.stunned);
     // Array of stunned stage 1 pieces (just back to normal then)
     Object.keys(this.stunned).forEach(square => {
@@ -267,11 +294,12 @@ export class KoopaRules extends ChessRules {
 
   postUndo(move) {
     super.postUndo(move);
-    const kIdx = move.vanish.findIndex(v => v.p == "l");
-    if (kIdx >= 0) {
-      // A stunned king vanished
-      this.kingPos[move.vanish[kIdx].c] =
-        [move.vanish[kIdx].x, move.vanish[kIdx].y];
+    const oppCol = V.GetOppCol(this.turn);
+    if (this.kingPos[oppCol][0] < 0) {
+      // Opponent's king vanished
+      const psq =
+        move.vanish.find((v,i) => i >= 1 && ['k', 'l'].includes(v.p));
+      this.kingPos[oppCol] = [psq.x, psq.y];
     }
     this.stunned = JSON.parse(move.stunned);
     for (let i=0; i<8; i++) {