Add Hamilton Chess
[vchess.git] / client / src / variants / Hamilton.js
diff --git a/client/src/variants/Hamilton.js b/client/src/variants/Hamilton.js
new file mode 100644 (file)
index 0000000..8c3a1bd
--- /dev/null
@@ -0,0 +1,153 @@
+import { ChessRules, Move, PiPo } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+
+export class HamiltonRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
+
+  static get HasEnpassant() {
+    return false;
+  }
+
+  static get HOLE() {
+    return "xx";
+  }
+
+  static board2fen(b) {
+    if (b[0] == 'x') return 'x';
+    return ChessRules.board2fen(b);
+  }
+
+  static fen2board(f) {
+    if (f == 'x') return V.HOLE;
+    return ChessRules.fen2board(f);
+  }
+
+  getPpath(b) {
+    if (b[0] == 'x') return "Hamilton/hole";
+    return b;
+  }
+
+  static get PIECES() {
+    return [ChessRules.KNIGHT];
+  }
+
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
+    const rows = position.split("/");
+    if (rows.length != V.size.x) return false;
+    for (let row of rows) {
+      let sumElts = 0;
+      for (let i = 0; i < row.length; i++) {
+        if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
+        else {
+          const num = parseInt(row[i]);
+          if (isNaN(num)) return false;
+          sumElts += num;
+        }
+      }
+      if (sumElts != V.size.y) return false;
+    }
+    return true;
+  }
+
+  static GenRandInitFen() {
+    return "8/8/8/8/8/8/8/8 w 0";
+  }
+
+  canIplay(side, [x, y]) {
+    return side == this.turn;
+  }
+
+  // Initiate the game by choosing a square for the knight:
+  doClick(square) {
+    if (this.movesCount > 0) return null;
+    return new Move({
+      appear: [
+        new PiPo({ x: square[0], y: square[1], c: 'w', p: V.KNIGHT })
+      ],
+      vanish: [],
+      start: { x: -1, y: -1 }
+    });
+  }
+
+  getAllPotentialMoves() {
+    if (this.movesCount == 0) {
+      return [...Array(64).keys()].map(k => {
+        const i = k % 8;
+        const j = (k - i) / 8;
+        return new Move({
+          appear: [
+            new PiPo({ x: i, y: j, c: 'w', p: V.KNIGHT })
+          ],
+          vanish: [],
+          start: { x: -1, y: -1 }
+        });
+      });
+    }
+    for (let i=0; i<8; i++) {
+      for (let j=0; j<8; j++) {
+        if (!([V.EMPTY, V.HOLE].includes(this.board[i][j])))
+          return this.getPotentialKnightMoves([i, j]);
+      }
+    }
+    return [];
+  }
+
+  getPotentialKnightMoves([x, y]) {
+    return (
+      V.steps[V.KNIGHT].filter(
+        s => {
+          const [i, j] = [x + s[0], y + s[1]];
+          return (V.OnBoard(i, j) && this.board[i][j] != V.HOLE);
+        }
+      ).map(s => {
+        return this.getBasicMove([x, y], [x + s[0], y + s[1]]);
+      })
+    );
+  }
+
+  atLeastOneMove() {
+    if (this.movesCount == 0) return true;
+    for (let i=0; i<8; i++) {
+      for (let j=0; j<8; j++) {
+        if (!([V.EMPTY, V.HOLE].includes(this.board[i][j])))
+          return this.getPotentialKnightMoves([i, j]).length > 0;
+      }
+    }
+    return false;
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  static PlayOnBoard(board, move) {
+    if (move.vanish.length > 0)
+      board[move.vanish[0].x][move.vanish[0].y] = V.HOLE;
+    for (let psq of move.appear) board[psq.x][psq.y] = psq.c + psq.p;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    if (this.atLeastOneMove()) return "*";
+    // No valid move: I lose
+    return this.turn == "w" ? "0-1" : "1-0";
+  }
+
+  getComputerMove() {
+    const moves = this.getAllValidMoves();
+    // Just a random mover for now...
+    return moves[randInt(moves.length)];
+  }
+
+  getNotation(move) {
+    if (move.vanish.length > 0) return super.getNotation(move);
+    // First game move:
+    return "N@" + V.CoordsToSquare(move.end);
+  }
+};