From 5212758164eaa08e382b7bc281f87999c8af352b Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sat, 30 Dec 2023 20:58:25 +0100
Subject: [PATCH] Draft Diamond variant

---
 base_rules.js               | 15 ++++--
 variants.js                 |  2 +-
 variants/Diamond/class.js   | 96 +++++++++++++++++++++++++++++++++++++
 variants/Diamond/rules.html | 13 +++++
 variants/Diamond/style.css  |  1 +
 5 files changed, 122 insertions(+), 5 deletions(-)
 create mode 100644 variants/Diamond/class.js
 create mode 100644 variants/Diamond/rules.html
 create mode 100644 variants/Diamond/style.css

diff --git a/base_rules.js b/base_rules.js
index 757707d..0f89cc4 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -579,11 +579,18 @@ export default class ChessRules {
 
   // Get SVG board (background, no pieces)
   getSvgChessboard() {
-    const flipped = this.flippedBoard;
     let board = `
       <svg
         viewBox="0 0 ${10*this.size.y} ${10*this.size.x}"
         class="chessboard_SVG">`;
+    board += this.getBaseSvgChessboard();
+    board += "</svg>";
+    return board;
+  }
+
+  getBaseSvgChessboard() {
+    let board = "";
+    const flipped = this.flippedBoard;
     for (let i=0; i < this.size.x; i++) {
       for (let j=0; j < this.size.y; j++) {
         if (!this.onBoard(i, j))
@@ -605,7 +612,6 @@ export default class ChessRules {
           />`;
       }
     }
-    board += "</svg>";
     return board;
   }
 
@@ -2341,10 +2347,11 @@ export default class ChessRules {
     if (this.options["teleport"]) {
       if (
         this.subTurnTeleport == 1 &&
-        move.vanish.length > move.appear.length &&
+        move.vanish.length == 2 &&
+        move.appear.length == 1 &&
         move.vanish[1].c == this.turn
       ) {
-        const v = move.vanish[move.vanish.length - 1];
+        const v = move.vanish[1];
         this.captured = {x: v.x, y: v.y, c: v.c, p: v.p};
         this.subTurnTeleport = 2;
         return;
diff --git a/variants.js b/variants.js
index 524538c..c9cdc98 100644
--- a/variants.js
+++ b/variants.js
@@ -41,7 +41,7 @@ const variants = [
   {name: 'Cylinder', desc: 'Neverending rows'},
   {name: 'Cwda', desc: 'New teams', disp: 'Different armies'},
   {name: 'Dark', desc: 'In the shadow'},
-//  {name: 'Diamond', desc: 'Rotating board'},
+  {name: 'Diamond', desc: 'Rotating board'},
 //  {name: 'Dice', desc: 'Roll the dice'},
 //  {name: 'Discoduel', desc: 'Enter the disco', disp: 'Disco Duel'},
 //  {name: 'Dobutsu', desc: "Let's catch the Lion!"},
diff --git a/variants/Diamond/class.js b/variants/Diamond/class.js
new file mode 100644
index 0000000..5145184
--- /dev/null
+++ b/variants/Diamond/class.js
@@ -0,0 +1,96 @@
+import ChessRules from "/base_rules.js";
+import {ArrayFun} from "/utils/array.js";
+import {Random} from "/utils/alea.js";
+
+export default class DiamondRules extends ChessRules {
+
+  get hasFlags() {
+    return false;
+  }
+
+  get hasEnpassant() {
+    return false;
+  }
+
+  getSvgChessboard() {
+    const diagonal = 10 * this.size.y * Math.sqrt(2);
+    const halfDiag = 0.5 * diagonal;
+    const deltaTrans = 10 * this.size.y * (Math.sqrt(2) - 1) / 2;
+    let board = `
+      <svg
+        viewBox="0 0 ${diagonal} ${diagonal}"
+        class="chessboard_SVG">`;
+    board += `<g transform="rotate(45 ${halfDiag} ${halfDiag})
+                 translate(${deltaTrans} ${deltaTrans})">`;
+    board += this.getBaseSvgChessboard();
+    board += "</g></svg>";
+    return board;
+  }
+
+  getPieceWidth(rwidth) {
+    return (0.95 * rwidth / (Math.sqrt(2) * this.size.y));
+  }
+
+  getPixelPosition(i, j, r) {
+    if (i < 0 || j < 0 || typeof i == "string")
+      return super.getPixelPosition(i, j, r);
+    const sqSize = this.getPieceWidth(r.width) / 0.95;
+    const flipped = this.flippedBoard;
+    i = (flipped ? this.size.x - 1 - i : i);
+    j = (flipped ? this.size.y - 1 - j : j);
+    const sq2 = Math.sqrt(2);
+    const shift = [- sqSize / 2, sqSize * (sq2 - 1) / 2];
+    const x = (j - i) * sqSize / sq2 + shift[0] + r.width / 2;
+    const y = (i + j) * sqSize / sq2 + shift[1];
+    return [r.x + x, r.y + y];
+  }
+
+  genRandInitBaseFen() {
+    if (this.options["randomness"] == 0) {
+      return {
+        fen: "krbp4/rqnp4/nbpp4/pppp4/4PPPP/4PPBN/4PNQR/4PBRK",
+        o: {}
+      };
+    }
+    let pieces = { w: new Array(8), b: new Array(8) };
+    for (let c of ["w", "b"]) {
+      if (c == 'b' && options.randomness == 1) {
+        pieces['b'] = pieces['w'];
+        break;
+      }
+      // Get random squares for every piece, totally freely
+      let positions = Random.shuffle(ArrayFun.range(8));
+      const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
+      const rem2 = positions[0] % 2;
+      if (rem2 == positions[1] % 2) {
+        // Fix bishops (on different colors)
+        for (let i=2; i<8; i++) {
+          if (positions[i] % 2 != rem2) {
+            [positions[1], positions[i]] = [positions[i], positions[1]];
+            break;
+          }
+        }
+      }
+      for (let i = 0; i < 8; i++)
+        pieces[c][positions[i]] = composition[i];
+    }
+    const fen = (
+      pieces["b"].slice(0, 3).join("") + "p4/" +
+      pieces["b"].slice(3, 6).join("") + "p4/" +
+      pieces["b"].slice(6, 8).join("") + "pp4/" +
+      "pppp4/4PPPP/" +
+      "4PP" + pieces["w"].slice(6, 8).reverse().join("").toUpperCase() + "/" +
+      "4P" + pieces["w"].slice(3, 6).reverse().join("").toUpperCase() + "/" +
+      "4P" + pieces["w"].slice(0, 3).reverse().join("").toUpperCase());
+    return { fen: fen, o: {} };
+  }
+
+  pieces(color, x, y) {
+    let res = super.pieces(color, x, y);
+    const pawnShift = this.getPawnShift(color || 'w');
+    res['p'].moves = [{steps: [[pawnShift, pawnShift]], range: 1}];
+    res['p'].attack = [{steps: [[0, pawnShift], [pawnShift, 0]], range: 1}];
+    return res;
+  }
+
+};
diff --git a/variants/Diamond/rules.html b/variants/Diamond/rules.html
new file mode 100644
index 0000000..9f172b1
--- /dev/null
+++ b/variants/Diamond/rules.html
@@ -0,0 +1,13 @@
+<p>The board is rotated by 45°, and then the game follow usual rules.</p>
+
+<p>Pawns move forward "one diagonal", and capture forward "orthogonally".</p>
+
+<p>
+  See
+  <a href="https://www.chessvariants.com/rules/diamond-chess">
+    Diamond Chess
+  </a>
+  on chessvariants.com.
+</p>
+
+<p class="author">James Alexander Porterfield Rynd (1886).</p>
diff --git a/variants/Diamond/style.css b/variants/Diamond/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Diamond/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
-- 
2.44.0