Add Ice Age Chess
[vchess.git] / client / src / variants / Iceage.js
diff --git a/client/src/variants/Iceage.js b/client/src/variants/Iceage.js
new file mode 100644 (file)
index 0000000..01f82d8
--- /dev/null
@@ -0,0 +1,143 @@
+import { ChessRules } from "@/base_rules";
+
+export class IceageRules extends ChessRules {
+
+  static get IgnoreRepetition() {
+    return true;
+  }
+
+  static get ICECUBE() {
+    return "cc";
+  }
+
+  static board2fen(b) {
+    if (b[0] == 'c') return 'c';
+    return ChessRules.board2fen(b);
+  }
+
+  static fen2board(f) {
+    if (f == 'c') return V.ICECUBE;
+    return ChessRules.fen2board(f);
+  }
+
+  getPpath(b) {
+    if (b[0] == 'c') return "Iceage/icecube";
+    return b;
+  }
+
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
+    const rows = position.split("/");
+    if (rows.length != V.size.x) return false;
+    let kings = { "k": 0, "K": 0 };
+    for (let row of rows) {
+      let sumElts = 0;
+      for (let i = 0; i < row.length; i++) {
+        if (['K','k'].includes(row[i])) kings[row[i]]++;
+        if (['c'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
+        else {
+          const num = parseInt(row[i], 10);
+          if (isNaN(num)) return false;
+          sumElts += num;
+        }
+      }
+      if (sumElts != V.size.y) return false;
+    }
+    if (Object.values(kings).some(v => v != 1)) return false;
+    return true;
+  }
+
+  static GenRandInitFen(randomness) {
+    return ChessRules.GenRandInitFen(randomness).replace(/8/g, "cccccccc");
+  }
+
+  play(move) {
+    const iceAgeAfterMove = (this.movesCount % 40 == 39);
+    if (iceAgeAfterMove)
+      // Next ice age after this move:
+      move.state = JSON.stringify(this.board);
+    super.play(move);
+    if (iceAgeAfterMove) {
+      for (let i=0; i<8; i++) {
+        for (let j=0; j<8; j++) {
+          if (this.board[i][j] == V.EMPTY) {
+            const surrounded = V.steps[V.ROOK].every(s => {
+              const [ii, jj] = [i + s[0], j + s[1]];
+              return (
+                !V.OnBoard(ii, jj) ||
+                ![V.EMPTY, V.ICECUBE].includes(this.board[ii][jj])
+              );
+            });
+            if (!surrounded) this.board[i][j] = V.ICECUBE;
+          }
+          else if (this.board[i][j] != V.ICECUBE) {
+            const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+            const connected = steps.some(s => {
+              const [ii, jj] = [i + s[0], j + s[1]];
+              return (
+                V.OnBoard(ii, jj) &&
+                ![V.EMPTY, V.ICECUBE].includes(this.board[ii][jj])
+              );
+            });
+            if (!connected) this.board[i][j] = V.ICECUBE;
+          }
+        }
+      }
+      // Update king position (no need to update flags: game over)
+      const kp = this.kingPos;
+      if (this.getPiece(kp['w'][0], kp['w'][1]) != V.KING)
+        this.kingPos['w'] = [-1, -1];
+      if (this.getPiece(kp['b'][0], kp['b'][1]) != V.KING)
+        this.kingPos['b'] = [-1, -1];
+    }
+  }
+
+  undo(move) {
+    super.undo(move);
+    if (!!move.state) {
+      this.board = JSON.parse(move.state);
+      if (this.kingPos['w'][0] < 0 || this.kingPos['b'][0] < 0) {
+        for (let i=0; i<8; i++) {
+          for (let j=0; j<8; j++) {
+            if (this.board[i][j] != V.EMPTY && this.getPiece(i, j) == V.KING)
+              this.kingPos[this.getColor(i, j)] = [i, j];
+          }
+        }
+      }
+    }
+  }
+
+  getCheckSquares() {
+    if (this.kingPos[this.turn][0] < 0) return [];
+    return super.getCheckSquares();
+  }
+
+  getCurrentScore() {
+    const kingDisappear = {
+      w: this.kingPos['w'][0] < 0,
+      b: this.kingPos['b'][0] < 0
+    };
+    if (kingDisappear['w'] && kingDisappear['b']) return "1/2";
+    if (kingDisappear['w']) return "0-1";
+    if (kingDisappear['b']) return "1-0";
+    return super.getCurrentScore();
+  }
+
+  static get SEARCH_DEPTH() {
+    return 2;
+  }
+
+  evalPosition() {
+    let evaluation = 0;
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (![V.EMPTY,V.ICECUBE].includes(this.board[i][j])) {
+          const sign = this.getColor(i, j) == "w" ? 1 : -1;
+          evaluation += sign * V.VALUES[this.getPiece(i, j)];
+        }
+      }
+    }
+    return evaluation;
+  }
+
+};