Draft Bario (unfinished)
[xogo.git] / variants / Bario / class.js
diff --git a/variants/Bario/class.js b/variants/Bario/class.js
new file mode 100644 (file)
index 0000000..80a3cce
--- /dev/null
@@ -0,0 +1,238 @@
+import ChessRules from "/base_rules.js";
+import PiPo from "/utils/PiPo.js";
+import Move from "/utils/Move.js";
+
+export default class BarioRules extends ChessRules {
+
+  static get Options() {
+    return {
+      // TODO: Zen too?
+      styles: [
+        "atomic", "cannibal", "capture", "cylinder",
+        "dark", "madrasi", "rifle", "teleport"
+      ]
+    };
+  }
+
+  // Does not really seem necessary (although the author mention it)
+  // Instead, first move = pick a square for the king.
+  get hasFlags() {
+    return false;
+  }
+  get hasReserve() {
+    return true;
+  }
+
+  pieces(color, x, y) {
+    return Object.assign(
+      {
+        'u': {
+          "class": "undefined",
+          moves: []
+        }
+      },
+      super.pieces(color, x, y)
+    );
+  }
+
+  get onlyClick() {
+    return this.movesCount <= 1;
+  }
+
+  // Initiate the game by choosing a square for the king:
+  doClick(coords) {
+    const color = this.turn;
+    if (
+      this.movesCount <= 1 &&
+      (
+        (color == 'w' && coords.x == this.size.x - 1) ||
+        (color == 'b' && coords.x == 0)
+      )
+    ) {
+      return new Move({
+        appear: [ new PiPo({x: coords.x, y: coords.y, c: color, p: 'k' }) ],
+        vanish: [ new PiPo({x: coords.x, y: coords.y, c: color, p: 'u' }) ]
+      });
+    }
+    return null;
+  }
+
+  genRandInitBaseFen() {
+    return {
+      fen: "uuuuuuuu/pppppppp/8/8/8/8/PPPPPPPP/UUUUUUUU",
+      o: {}
+    }
+  }
+
+  getPartFen(o) {
+    return Object.assign(
+      {
+        captureUndef: (o.init || !this.captureUndef)
+          ? "-"
+          : C.CoordsToSquare(this.captureUndef)
+      },
+      super.getPartFen(o)
+    );
+  }
+
+  getReserveFen(o) {
+    if (o.init)
+      return "22212221";
+    return (
+      ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("")
+    );
+  }
+
+  initReserves(reserveStr) {
+    super.initReserves(reserveStr, ['r', 'n', 'b', 'q']);
+  }
+
+  setOtherVariables(fenParsed) {
+    super.setOtherVariables(fenParsed);
+    this.captureUndef = fenParsed.captureUndef == '-'
+      ? null :
+      C.SquareToCoords(fenParsed.captureUndef);
+    this.definition = null;
+  }
+
+  canDrop([c, p], [i, j]) {
+    switch (this.subTurn) {
+      case 0:
+        return i == this.captureUndef.x && j == this.captureUndef.y;
+      case 1:
+        return this.getPiece(i, j) == 'u' && c == this.getColor(i, j);
+    }
+    return false; //never reached
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    if (this.movesCount <= 1)
+      return [];
+    let moves = [];
+    switch (this.subTurn) {
+      case 0:
+        if (typeof x == "string")
+          moves = this.getDropMovesFrom([x, y]);
+        break;
+      case 1:
+        // Both normal move (from defined piece) and definition allowed
+        if (typeof x == "string")
+          moves = this.getDropMovesFrom([x, y]);
+        else if (this.getPiece(x, y) != 'u')
+          moves = super.getPotentialMovesFrom([x, y]);
+        break;
+      case 2:
+        // We can only move the just-defined piece
+        if (x == this.definition.x && y == this.definition.y)
+          moves = super.getPotentialMovesFrom([x, y]);
+        break;
+    }
+    return moves;
+  }
+
+  filterValid(moves) {
+    if (this.movesCount <= 1 || this.subTurn == 0)
+      return moves;
+    if (this.subTurn == 1) {
+      // Remove defining moves with un-movable def piece
+      moves = moves.filter(m => {
+        if (m.vanish.length >= 2 || m.vanish[0].p != 'u')
+          return true;
+        this.playOnBoard(m);
+        const canMove = super.filterValid(
+          super.getPotentialMovesFrom([m.end.x, m.end.y])).length >= 1;
+        this.undoOnBoard(m);
+        return canMove;
+      });
+    }
+    return super.filterValid(moves);
+  }
+
+  atLeastOneMove(color) {
+    if (this.subTurn != 1)
+      return true;
+    return super.atLeastOneMove(color);
+  }
+
+  // TODO: this method fails to detect undefined checks
+  underCheck(square_s, oppCol) {
+    if (super.underCheck(square_s, oppCol))
+      return true;
+    // Check potential specializations of undefined using reserve:
+    const allAttacks = Array.prototype.concat.apply(
+      ['r', 'n', 'b', 'q'].map(p => this.pieces()[p].moves[0]));
+    const [x, y] = [square_s[0], square_s[1]];
+    for (let i=0; i<this.size.x; i++) {
+      for (let j=0; j<this.size.y; j++) {
+        if (
+          this.board[i][j] != "" &&
+          this.getColor(i, j) == oppCol &&
+          this.getPiece(i, j) == 'u'
+        ) {
+          for (let stepDef of allAttacks) {
+            for (let s of stepDef.steps) {
+              if (!super.compatibleStep([i, j], [x, y], s, stepDef.range))
+                continue;
+              if (
+                super.findDestSquares(
+                  [i, j],
+                  {
+                    captureTarget: [x, y],
+                    captureSteps: [{steps: [s], range: stepDef.range}],
+                    segments: false,
+                    attackOnly: true,
+                    one: false
+                  }
+                )
+              ) {
+                return true;
+              }
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  // TODO: missing "undefined reset" check (is everything defined? If yes, reset if enough variety)
+  postPlay(move) {
+    const color = this.turn;
+    const toNextPlayer = () => {
+      this.turn = C.GetOppCol(color);
+      this.movesCount++;
+    };
+    if (this.movesCount <= 1) {
+      toNextPlayer();
+      return;
+    }
+    const captureUndef = (
+      move.vanish.length == 2 &&
+      move.vanish[1].c != color &&
+      move.vanish[1].p == 'u'
+    );
+    if (typeof move.start.x == "number" && !captureUndef)
+      // Normal move (including Teleport)
+      super.postPlay(move);
+    else if (typeof move.start.x == "string") {
+      this.reserve[color][move.appear[0].p]--;
+      if (move.vanish.length == 1 && move.vanish[0].p == 'u')
+        this.definition = move.end;
+      this.subTurn++;
+    }
+    else {
+      this.subTurn = 0;
+      this.captureUndef = move.end;
+      toNextPlayer();
+    }
+  }
+
+  isLastMove() {
+    return true; //called only on normal moves (not Teleport)
+  }
+
+  getCurrentScore(move_s) {
+    return (this.movesCount <= 2 ? "*" : super.getCurrentScore(move_s));
+  }
+
+};