From dc10e429231932c19da6d1ff2ce98c7a042829ab Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Wed, 6 Jul 2022 13:35:48 +0200
Subject: [PATCH] Draft Bario (unfinished)

---
 base_rules.js                                 |  25 +-
 pieces/{Atarigo => Go}/CREDITS                |   0
 pieces/{Atarigo => Go}/black_stone.svg        |   0
 pieces/{Atarigo => Go}/white_stone.svg        |   0
 .../mystery_black.svg => black_mystery.svg}   |   0
 .../mystery_white.svg => white_mystery.svg}   |   0
 variants.js                                   |   3 +-
 variants/Alapo/class.js                       |   5 +-
 variants/Antiking1/class.js                   |   2 +-
 variants/Apocalypse/class.js                  |   2 +-
 variants/Atarigo/style.css                    |   4 +-
 variants/Avalam/class.js                      |   7 +-
 variants/Avalanche/class.js                   |   1 -
 variants/Bario/class.js                       | 238 ++++++++++++++++++
 variants/Bario/rules.html                     |   1 +
 variants/Bario/style.css                      |   8 +
 variants/Chakart/style.css                    |   8 +-
 variants/Giveaway/class.js                    |   5 +-
 variants/Hex/class.js                         |   2 +-
 19 files changed, 275 insertions(+), 36 deletions(-)
 rename pieces/{Atarigo => Go}/CREDITS (100%)
 rename pieces/{Atarigo => Go}/black_stone.svg (100%)
 rename pieces/{Atarigo => Go}/white_stone.svg (100%)
 rename pieces/{Chakart/mystery_black.svg => black_mystery.svg} (100%)
 rename pieces/{Chakart/mystery_white.svg => white_mystery.svg} (100%)
 create mode 100644 variants/Bario/class.js
 create mode 100644 variants/Bario/rules.html
 create mode 100644 variants/Bario/style.css

diff --git a/base_rules.js b/base_rules.js
index 8eed3d3..dc25e54 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -208,7 +208,7 @@ export default class ChessRules {
     baseFen.o = Object.assign({init: true}, baseFen.o);
     const parts = this.getPartFen(baseFen.o);
     return (
-      baseFen.fen +
+      baseFen.fen + " w 0" +
       (Object.keys(parts).length > 0 ? (" " + JSON.stringify(parts)) : "")
     );
   }
@@ -218,7 +218,7 @@ export default class ChessRules {
     let fen, flags = "0707";
     if (!this.options.randomness)
       // Deterministic:
-      fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0";
+      fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
 
     else {
       // Randomize
@@ -271,8 +271,7 @@ export default class ChessRules {
       fen = (
         pieces["b"].join("") +
         "/pppppppp/8/8/8/8/PPPPPPPP/" +
-        pieces["w"].join("").toUpperCase() +
-        " w 0"
+        pieces["w"].join("").toUpperCase()
       );
     }
     return { fen: fen, o: {flags: flags} };
@@ -477,17 +476,15 @@ export default class ChessRules {
   }
 
   // ordering as in pieces() p,r,n,b,q,k
-  initReserves(reserveStr) {
+  initReserves(reserveStr, pieceArray) {
+    if (!pieceArray)
+      pieceArray = ['p', 'r', 'n', 'b', 'q', 'k'];
     const counts = reserveStr.split("").map(c => parseInt(c, 36));
-    this.reserve = { w: {}, b: {} };
-    const pieceName = ['p', 'r', 'n', 'b', 'q', 'k'];
-    const L = pieceName.length;
-    for (let i of ArrayFun.range(2 * L)) {
-      if (i < L)
-        this.reserve['w'][pieceName[i]] = counts[i];
-      else
-        this.reserve['b'][pieceName[i-L]] = counts[i];
-    }
+    const L = pieceArray.length;
+    this.reserve = {
+      w: ArrayFun.toObject(pieceArray, counts.slice(0, L)),
+      b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L))
+    };
   }
 
   initIspawn(ispawnStr) {
diff --git a/pieces/Atarigo/CREDITS b/pieces/Go/CREDITS
similarity index 100%
rename from pieces/Atarigo/CREDITS
rename to pieces/Go/CREDITS
diff --git a/pieces/Atarigo/black_stone.svg b/pieces/Go/black_stone.svg
similarity index 100%
rename from pieces/Atarigo/black_stone.svg
rename to pieces/Go/black_stone.svg
diff --git a/pieces/Atarigo/white_stone.svg b/pieces/Go/white_stone.svg
similarity index 100%
rename from pieces/Atarigo/white_stone.svg
rename to pieces/Go/white_stone.svg
diff --git a/pieces/Chakart/mystery_black.svg b/pieces/black_mystery.svg
similarity index 100%
rename from pieces/Chakart/mystery_black.svg
rename to pieces/black_mystery.svg
diff --git a/pieces/Chakart/mystery_white.svg b/pieces/white_mystery.svg
similarity index 100%
rename from pieces/Chakart/mystery_white.svg
rename to pieces/white_mystery.svg
diff --git a/variants.js b/variants.js
index b65a564..8fe9d95 100644
--- a/variants.js
+++ b/variants.js
@@ -14,9 +14,8 @@ const variants = [
   {name: 'Atomic', desc: 'Explosive captures'},
   {name: 'Avalam', desc: 'Build towers'},
   {name: 'Avalanche', desc: 'Pawnfalls'},
-//  {name: 'Ball', desc: 'Score a goal'},
 //  {name: 'Balaklava', desc: 'Meet the Mammoth'},
-//  {name: 'Bario', desc: 'A quantum story'},
+  {name: 'Bario', desc: 'A quantum story'},
   {name: "Balanced", desc: "balanced chess"},
 //  {name: 'Baroque', desc: 'Exotic captures'},*/
   {name: "Benedict", desc: "Change colors"},
diff --git a/variants/Alapo/class.js b/variants/Alapo/class.js
index 1c7c048..199a169 100644
--- a/variants/Alapo/class.js
+++ b/variants/Alapo/class.js
@@ -31,7 +31,7 @@ export default class AlapoRules extends ChessRules {
   genRandInitBaseFen() {
     let fen = "";
     if (this.options["randomness"] == 0)
-      fen = "rbqqbr/tcssct/6/6/TCSSCT/RBQQBR w 0";
+      fen = "rbqqbr/tcssct/6/6/TCSSCT/RBQQBR";
     else {
       const piece2pawn = {
         r: 't',
@@ -75,8 +75,7 @@ export default class AlapoRules extends ChessRules {
         pieces["b"].map(p => piece2pawn[p]).join("") +
         "/6/6/" +
         pieces["w"].map(p => piece2pawn[p].toUpperCase()).join("") + "/" +
-        pieces["w"].join("").toUpperCase() +
-        " w 0"
+        pieces["w"].join("").toUpperCase()
       );
     }
     return { fen: fen, o: {} };
diff --git a/variants/Antiking1/class.js b/variants/Antiking1/class.js
index 9d981e8..323b826 100644
--- a/variants/Antiking1/class.js
+++ b/variants/Antiking1/class.js
@@ -31,7 +31,7 @@ export default class Antiking1Rules extends AbstractAntikingRules {
   genRandInitBaseFen() {
     // Always deterministic setup
     return {
-      fen: "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0",
+      fen: "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2",
       o: {"flags": "KAka"}
     };
   }
diff --git a/variants/Apocalypse/class.js b/variants/Apocalypse/class.js
index 0600991..5f1e7e2 100644
--- a/variants/Apocalypse/class.js
+++ b/variants/Apocalypse/class.js
@@ -40,7 +40,7 @@ export default class ApocalypseRules extends ChessRules {
 
   genRandInitBaseFen() {
     return {
-      fen: "npppn/p3p/5/P3P/NPPPN w 0",
+      fen: "npppn/p3p/5/P3P/NPPPN",
       o: {}
     };
   }
diff --git a/variants/Atarigo/style.css b/variants/Atarigo/style.css
index 6ce83d8..c72abda 100644
--- a/variants/Atarigo/style.css
+++ b/variants/Atarigo/style.css
@@ -3,9 +3,9 @@
 }
 
 piece.white.stone {
-  background-image: url('/pieces/Atarigo/black_stone.svg');
+  background-image: url('/pieces/Go/black_stone.svg');
 }
 
 piece.black.stone {
-  background-image: url('/pieces/Atarigo/white_stone.svg');
+  background-image: url('/pieces/Go/white_stone.svg');
 }
diff --git a/variants/Avalam/class.js b/variants/Avalam/class.js
index f35df83..95993e0 100644
--- a/variants/Avalam/class.js
+++ b/variants/Avalam/class.js
@@ -66,10 +66,10 @@ export default class AvalamRules extends ChessRules {
   genRandInitBaseFen() {
     let fen = "";
     if (this.freefill)
-      fen = "9/".repeat(8) + "9 w 0";
+      fen = "9/".repeat(8) + "9";
     else if (this.options["randomness"] == 0) {
       fen = "2Bb5/1BbBb4/1bBbBbB2/1BbBbBbBb/BbBb1bBbB/" +
-            "bBbBbBbB1/2BbBbBb1/4bBbB1/5bB2 w 0";
+            "bBbBbBbB1/2BbBbBb1/4bBbB1/5bB2";
     }
     else {
       const pieces = ('B'.repeat(24) + 'b'.repeat(24)).split("");
@@ -79,8 +79,7 @@ export default class AvalamRules extends ChessRules {
         "4/1" + a.substr(6, 6) + "2/1" + a.substr(12, 8) +
         "/" + a.substr(20, 4) + "1" + a.substr(24, 4) +
         "/" + a.substr(28, 8) + "1/2" + a.substr(36, 6) +
-        "1/4" + a.substr(42, 4) + "1/5" + a.substr(46, 2) +
-        "2 w 0"
+        "1/4" + a.substr(42, 4) + "1/5" + a.substr(46, 2) + "2"
       );
     }
     return { fen: fen, o: {} };
diff --git a/variants/Avalanche/class.js b/variants/Avalanche/class.js
index ea2b9dd..a1d4d54 100644
--- a/variants/Avalanche/class.js
+++ b/variants/Avalanche/class.js
@@ -1,5 +1,4 @@
 import ChessRules from "/base_rules.js";
-import {Random} from "/utils/alea.js";
 import PiPo from "/utils/PiPo.js";
 import Move from "/utils/Move.js";
 
diff --git a/variants/Bario/class.js b/variants/Bario/class.js
new file mode 100644
index 0000000..80a3cce
--- /dev/null
+++ b/variants/Bario/class.js
@@ -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));
+  }
+
+};
diff --git a/variants/Bario/rules.html b/variants/Bario/rules.html
new file mode 100644
index 0000000..c65158e
--- /dev/null
+++ b/variants/Bario/rules.html
@@ -0,0 +1 @@
+<p>TODO</p>
diff --git a/variants/Bario/style.css b/variants/Bario/style.css
new file mode 100644
index 0000000..76aab16
--- /dev/null
+++ b/variants/Bario/style.css
@@ -0,0 +1,8 @@
+@import url("/base_pieces.css");
+
+piece.white.undefined {
+  background-image: url('/pieces/white_mystery.svg');
+}
+piece.black.undefined {
+  background-image: url('/pieces/black_mystery.svg');
+}
diff --git a/variants/Chakart/style.css b/variants/Chakart/style.css
index 91ad633..f1a38bc 100644
--- a/variants/Chakart/style.css
+++ b/variants/Chakart/style.css
@@ -32,11 +32,11 @@ piece.remote-capture {
   background-image: url('/pieces/Chakart/shell.svg');
 }
 
-piece.mystery.white {
-  background-image: url('/pieces/Chakart/mystery_white.svg');
+piece.white.mystery {
+  background-image: url('/pieces/white_mystery.svg');
 }
-piece.mystery.black {
-  background-image: url('/pieces/Chakart/mystery_black.svg');
+piece.black.mystery {
+  background-image: url('/pieces/black_mystery.svg');
 }
 
 div.bonus-text {
diff --git a/variants/Giveaway/class.js b/variants/Giveaway/class.js
index 15a8998..39f586c 100644
--- a/variants/Giveaway/class.js
+++ b/variants/Giveaway/class.js
@@ -42,7 +42,7 @@ export default class GiveawayRules extends ChessRules {
 
     let fen = "";
     if (this.options["randomness"] == 0)
-      fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0";
+      fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
     else {
       let pieces = { w: new Array(8), b: new Array(8) };
       for (let c of ["w", "b"]) {
@@ -69,8 +69,7 @@ export default class GiveawayRules extends ChessRules {
       fen = (
         pieces["b"].join("") +
         "/pppppppp/8/8/8/8/PPPPPPPP/" +
-        pieces["w"].join("").toUpperCase() +
-        " w 0"
+        pieces["w"].join("").toUpperCase()
       );
     }
     return { fen: fen, o: {} };
diff --git a/variants/Hex/class.js b/variants/Hex/class.js
index b82af41..774de17 100644
--- a/variants/Hex/class.js
+++ b/variants/Hex/class.js
@@ -78,7 +78,7 @@ export default class HexRules extends AbstractClickFillRules {
     // NOTE: size.x == size.y (square boards)
     const emptyCount = C.FenEmptySquares(this.size.x);
     return {
-      fen: (emptyCount + "/").repeat(this.size.x).slice(0, -1) + " w 0",
+      fen: (emptyCount + "/").repeat(this.size.x - 1) + emptyCount,
       o: {}
     };
   }
-- 
2.44.0