Add Knightrelay1. Some fixes. Move odd 'isAttackedBy_multiple_colors' to Checkered...
[vchess.git] / client / src / variants / Knightrelay1.js
diff --git a/client/src/variants/Knightrelay1.js b/client/src/variants/Knightrelay1.js
new file mode 100644 (file)
index 0000000..3e7bf38
--- /dev/null
@@ -0,0 +1,139 @@
+import { ChessRules } from "@/base_rules";
+
+export const VariantRules = class Knightrelay1Rules extends ChessRules {
+  static get HasEnpassant() {
+    return false;
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    let moves = super.getPotentialMovesFrom([x, y]);
+
+    // Expand possible moves if guarded by a knight, and is not a king:
+    const piece = this.getPiece(x,y);
+    if (![V.KNIGHT,V.KING].includes(piece)) {
+      const color = this.turn;
+      let guardedByKnight = false;
+      for (const step of V.steps[V.KNIGHT]) {
+        if (
+          V.OnBoard(x+step[0],y+step[1]) &&
+          this.getPiece(x+step[0],y+step[1]) == V.KNIGHT &&
+          this.getColor(x+step[0],y+step[1]) == color
+        ) {
+          guardedByKnight = true;
+          break;
+        }
+      }
+      if (guardedByKnight) {
+        const lastRank = (color == "w" ? 0 : V.size.x - 1);
+        for (const step of V.steps[V.KNIGHT]) {
+          if (
+            V.OnBoard(x+step[0],y+step[1]) &&
+            this.getColor(x+step[0],y+step[1]) != color &&
+            // Pawns cannot promote by knight-relay
+            (piece != V.PAWN || x+step[0] != lastRank)
+          ) {
+            moves.push(this.getBasicMove([x,y], [x+step[0],y+step[1]]));
+          }
+        }
+      }
+    }
+
+    // Forbid captures of knights (invincible in this variant)
+    return moves.filter(m => {
+      return (
+        m.vanish.length == 1 ||
+        m.appear.length == 2 ||
+        m.vanish[1].p != V.KNIGHT
+      );
+    });
+  }
+
+  getPotentialKnightMoves(sq) {
+    // Knights don't capture:
+    return super.getPotentialKnightMoves(sq).filter(m => m.vanish.length == 1);
+  }
+
+  isAttacked(sq, color) {
+    if (super.isAttacked(sq, color)) return true;
+
+    // Check if a (non-knight) piece at knight distance
+    // is guarded by a knight (and thus attacking)
+    // --> Except for pawns targetting last rank.
+    const x = sq[0],
+          y = sq[1];
+    // Last rank for me, that is to say oppCol of color:
+    const lastRank = (color == 'w' ? V.size.x - 1 : 0);
+    for (const step of V.steps[V.KNIGHT]) {
+      if (
+        V.OnBoard(x+step[0],y+step[1]) &&
+        this.getColor(x+step[0],y+step[1]) == color
+      ) {
+        const piece = this.getPiece(x+step[0],y+step[1]);
+        if (piece != V.KNIGHT && (piece != V.PAWN || x != lastRank)) {
+          for (const step2 of V.steps[V.KNIGHT]) {
+            const xx = x+step[0]+step2[0],
+                  yy = y+step[1]+step2[1];
+            if (
+              V.OnBoard(xx,yy) &&
+              this.getColor(xx,yy) == color &&
+              this.getPiece(xx,yy) == V.KNIGHT
+            ) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  isAttackedByKnight(sq, color) {
+    // Knights don't attack
+    return false;
+  }
+
+  static get VALUES() {
+    return {
+      p: 1,
+      r: 5,
+      n: 7, //the knight is valuable
+      b: 3,
+      q: 9,
+      k: 1000
+    };
+  }
+
+  static get SEARCH_DEPTH() {
+    return 2;
+  }
+
+  getNotation(move) {
+    if (move.appear.length == 2 && move.appear[0].p == V.KING)
+      // Castle
+      return move.end.y < move.start.y ? "0-0-0" : "0-0";
+
+    // Translate final and initial square
+    const initSquare = V.CoordsToSquare(move.start);
+    const finalSquare = V.CoordsToSquare(move.end);
+    const piece = this.getPiece(move.start.x, move.start.y);
+
+    // Since pieces and pawns could move like knight, indicate start and end squares
+    let notation =
+      piece.toUpperCase() +
+      initSquare +
+      (move.vanish.length > move.appear.length ? "x" : "") +
+      finalSquare
+
+    if (
+      piece == V.PAWN &&
+      move.appear.length > 0 &&
+      move.appear[0].p != V.PAWN
+    ) {
+      // Promotion
+      notation += "=" + move.appear[0].p.toUpperCase();
+    }
+
+    return notation;
+  }
+};