From fe234391b05ffef5e3236e82ca1391adcb784b45 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Mon, 20 Jun 2022 23:19:55 +0200
Subject: [PATCH] Add Suction

---
 TODO                        |   6 +-
 variants.js                 |   2 +-
 variants/Chakart/class.js   |   2 +-
 variants/Suction/class.js   | 113 ++++++++++++++++++++++++++++++++++++
 variants/Suction/rules.html |   5 ++
 variants/Suction/style.css  |   1 +
 6 files changed, 125 insertions(+), 4 deletions(-)
 create mode 100644 variants/Suction/class.js
 create mode 100644 variants/Suction/rules.html
 create mode 100644 variants/Suction/style.css

diff --git a/TODO b/TODO
index a17d5c3..d879b34 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,7 @@
-add variants : Dark Racing Kings ?
-Checkered-Teleport ?
+add variants :
+Ambiguous
+Refusal
+Dark Racing Kings ? Checkered-Teleport ?
 
 Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element with 2 SVG or SVG + number
 réfléchir aux animations par variante (reversi, Chakart, Atomic, ...)
diff --git a/variants.js b/variants.js
index dc7540e..eb7203e 100644
--- a/variants.js
+++ b/variants.js
@@ -137,7 +137,7 @@ const variants = [
 //  {name: 'Spartan', desc: 'Spartan versus Persians'},
 //  {name: 'Squatter', desc: 'Squat last rank'},
 //  {name: 'Stealthbomb', desc: 'Beware the bomb'},
-//  {name: 'Suction', desc: 'Attract opposite king'},
+  {name: 'Suction', desc: 'Attract opposite king'},
 //  {name: 'Swap', desc: 'Dangerous captures'},
 //  {name: 'Switching', desc: "Exchange pieces' positions"},
 //  {name: 'Synchrone', desc: 'Play at the same time'},
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 61a8ee2..c527b8c 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -117,7 +117,7 @@ export default class ChakartRules extends ChessRules {
 
   genRandInitFen(seed) {
     const gr = new GiveawayRules(
-      {mode: "suicide", options: {}, genFenOnly: true});
+      {mode: "suicide", options: this.options, genFenOnly: true});
     // Add Peach + mario flags
     return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}';
   }
diff --git a/variants/Suction/class.js b/variants/Suction/class.js
new file mode 100644
index 0000000..db10e58
--- /dev/null
+++ b/variants/Suction/class.js
@@ -0,0 +1,113 @@
+import ChessRules from "/base_rules.js";
+import GiveawayRules from "/variants/Giveaway/class.js";
+import PiPo from "/utils/PiPo.js";
+import Move from "/utils/Move.js";
+
+export default class SuctionRules extends ChessRules {
+
+  static get Options() {
+    return {
+      select: C.Options.select,
+      styles: [
+        "balance",
+        "capture",
+        "cylinder",
+        "dark",
+        "doublemove",
+        "madrasi",
+        "progressive",
+        "teleport"
+      ]
+    };
+  }
+
+  get pawnPromotions() {
+    return ['p']; //no promotions
+  }
+
+  get hasFlags() {
+    return false;
+  }
+
+  setOtherVariables(fenParsed) {
+    super.setOtherVariables(fenParsed);
+    this.cmove = null;
+    const cmove_str = fenParsed.cmove;
+    if (cmove_str != "-") {
+      this.cmove = {
+        start: C.SquareToCoords(cmove_str.substr(0, 2)),
+        end: C.SquareToCoords(cmove_str.substr(2))
+      };
+    }
+  }
+
+  genRandInitFen(seed) {
+    const gr = new GiveawayRules(
+      {mode: "suicide", options: this.options, genFenOnly: true});
+    // Add empty cmove:
+    return (
+      gr.genRandInitFen(seed).slice(0, -17) + '{"enpassant":"-","cmove":"-"}');
+  }
+
+  getFen() {
+    const cmoveFen = !this.cmove
+      ? "-"
+      : C.CoordsToSquare(this.cmove.start) + C.CoordsToSquare(this.cmove.end);
+    return super.getFen().slice(0, -1) + ',"' + cmoveFen + '"}';
+  }
+
+  getBasicMove([sx, sy], [ex, ey]) {
+    let move = super.getBasicMove([sx, sy], [ex, ey]);
+    if (move.vanish.length == 2) {
+      move.appear.push(
+        new PiPo({
+          x: sx,
+          y: sy,
+          c: move.vanish[1].c,
+          p: move.vanish[1].p
+        })
+      );
+    }
+    return move;
+  }
+
+  canIplay(x, y) {
+    return this.getPiece(x, y) != 'k' && super.canIplay(x, y);
+  }
+
+  // Does m2 un-do m1 ? (to disallow undoing captures)
+  oppositeMoves(m1, m2) {
+    return (
+      !!m1 &&
+      m2.vanish.length == 2 &&
+      m1.start.x == m2.start.x &&
+      m1.end.x == m2.end.x &&
+      m1.start.y == m2.start.y &&
+      m1.end.y == m2.end.y
+    );
+  }
+
+  filterValid(moves) {
+    return moves.filter(m => !this.oppositeMoves(this.cmove, m));
+  }
+
+  postPlay(move) {
+    super.postPlay(move);
+    this.cmove =
+      (move.vanish.length == 2 ? {start: move.start, end: move.end} : null);
+  }
+
+  atLeastOneMove() {
+    return true;
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    const kingPos = super.searchKingPos(color);
+    if (color == "w" && kingPos[0] == 0) return "0-1";
+    if (color == "b" && kingPos[0] == this.size.x - 1) return "1-0";
+    // King is not on the opposite edge: game not over
+    return "*";
+  }
+
+};
diff --git a/variants/Suction/rules.html b/variants/Suction/rules.html
new file mode 100644
index 0000000..09a70cb
--- /dev/null
+++ b/variants/Suction/rules.html
@@ -0,0 +1,5 @@
+<p>Pieces are swapped after captures. Kings cannot move except by being captured.</p>
+
+<p>Win by bringing the enemy king on your first rank.</p>
+
+<p class="author">Nathaniel Virgo (2018).</p>
diff --git a/variants/Suction/style.css b/variants/Suction/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Suction/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
-- 
2.44.0