Add Alice Chess, fix a few things in base_rules.js
[xogo.git] / variants / Alice / class.js
diff --git a/variants/Alice/class.js b/variants/Alice/class.js
new file mode 100644 (file)
index 0000000..1957383
--- /dev/null
@@ -0,0 +1,126 @@
+import ChessRules from "/base_rules.js";
+import { ArrayFun } from "/utils/array.js";
+
+export default class AliceRules extends ChessRules {
+
+  static get Options() {
+    return {
+      select: C.Options.select,
+      input: C.Options.input,
+      styles: [
+        "balance",
+        "capture",
+        "cylinder",
+        "dark",
+        "doublemove",
+        "progressive",
+        "zen"
+      ]
+    };
+  }
+
+  // To the other side of the mirror and back...
+  static get ALICE_PIECES() {
+    return {
+      s: "p",
+      u: "r",
+      o: "n",
+      c: "b",
+      t: "q",
+      l: "k"
+    };
+  }
+  static get ALICE_CODES() {
+    return {
+      p: "s",
+      r: "u",
+      n: "o",
+      b: "c",
+      q: "t",
+      k: "l"
+    };
+  }
+
+  getPieceType(x, y, p) {
+    if (!p)
+      p = super.getPiece(x, y);
+    return V.ALICE_PIECES[p] || p;
+  }
+
+  pieces(color, x, y) {
+    let alices = {
+      's': {"class": "alice-pawn", "moveas": "p"},
+      'u': {"class": "alice-rook", "moveas": "r"},
+      'o': {"class": "alice-knight", "moveas": "n"},
+      'c': {"class": "alice-bishop", "moveas": "b"},
+      't': {"class": "alice-queen", "moveas": "q"},
+      'l': {"class": "alice-king", "moveas": "k"}
+    };
+    return Object.assign(alices, super.pieces(color, x, y));
+  }
+
+  fromSameWorld(p1, p2) {
+    return (
+      (V.ALICE_PIECES[p1] && V.ALICE_PIECES[p2]) ||
+      (V.ALICE_CODES[p1] && V.ALICE_CODES[p2])
+    );
+  }
+
+  // Step of p over i,j ?
+  canStepOver(i, j, p) {
+    return (
+      this.board[i][j] == "" || !this.fromSameWorld(this.getPiece(i, j), p));
+  }
+
+  // NOTE: castle & enPassant
+  // https://www.chessvariants.com/other.dir/alice.html
+  getPotentialMovesFrom([x, y]) {
+    return super.getPotentialMovesFrom([x, y]).filter(m => {
+      // Remove moves landing on occupied square on other board
+      return (
+        this.board[m.end.x][m.end.y] == "" ||
+        this.fromSameWorld(m.vanish[0].p, m.vanish[1].p)
+      );
+    }).map(m => {
+      // Apply Alice rule: go to the other side of the mirror
+      if (Object.keys(V.ALICE_CODES).includes(m.vanish[0].p))
+        // Board 1
+        m.appear.forEach(a => a.p = V.ALICE_CODES[a.p])
+      else
+        // Board 2
+        m.appear.forEach(a => a.p = V.ALICE_PIECES[a.p])
+      return m;
+    });
+  }
+
+  isKing(symbol) {
+    return ['k', 'l'].includes(symbol);
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    const inCheck = this.underCheck(this.searchKingPos(color));
+    let someLegalMove = false;
+    // Search for legal moves: if any is found and
+    // does not change king world (if under check), then game not over.
+    for (let i=0; i<this.size.x; i++) {
+      for (let j=0; j<this.size.y; j++) {
+        if (this.getColor(i, j) == color) {
+          const moves = this.filterValid(this.getPotentialMovesFrom([i, j]));
+          if (
+            moves.length >= 1 &&
+            (
+              !inCheck ||
+              moves.some(m => m.vanish.every(v => !this.isKing(v.p)))
+            )
+          ) {
+            return "*";
+          }
+        }
+      }
+    }
+    // Couldn't find any legal move
+    return (inCheck ? "1/2" : (color == 'w' ? "0-1" : "1-0"));
+  }
+
+};