update
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 23 Dec 2023 22:35:15 +0000 (23:35 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 23 Dec 2023 22:35:15 +0000 (23:35 +0100)
TODO
variants.js
variants/Coronation/class.js [new file with mode: 0644]
variants/Coronation/rules.html [new file with mode: 0644]
variants/Coronation/style.css [new file with mode: 0644]
variants/Crossing/class.js [new file with mode: 0644]
variants/Crossing/rules.html [new file with mode: 0644]
variants/Crossing/style.css [new file with mode: 0644]
variants/Cwda/class.js [new file with mode: 0644]

diff --git a/TODO b/TODO
index 275ed22..fac46e3 100644 (file)
--- a/TODO
+++ b/TODO
@@ -4,3 +4,6 @@ Dark Racing Kings ? Checkered-Teleport ?
 Hmm... non ? -->
 Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element with 2 SVG or SVG + number
 ==> plus simple : deux classes, images superposées.
+
+https://fr.wikipedia.org/wiki/Unlur
+Yoxii ?
index bc77daf..9ff40a7 100644 (file)
@@ -35,9 +35,9 @@ const variants = [
   {name: 'Convert', desc: 'Convert enemy pieces'},
   {name: 'Copycat', desc: 'Borrow powers'},
   {name: 'Coregal', desc: 'Two royal pieces'},
-//  {name: 'Coronation', desc: 'Long live the Queen'},
+  {name: 'Coronation', desc: 'Long live the Queen'},
   {name: 'Crazyhouse', desc: 'Captures reborn'},
-//  {name: 'Crossing', desc: 'Cross the river'},
+  {name: 'Crossing', desc: 'Cross the river'},
   {name: 'Cylinder', desc: 'Neverending rows'},
 //  {name: 'Cwda', desc: 'New teams', disp: 'Different armies'},
   {name: 'Dark', desc: 'In the shadow'},
diff --git a/variants/Coronation/class.js b/variants/Coronation/class.js
new file mode 100644 (file)
index 0000000..bf3684a
--- /dev/null
@@ -0,0 +1,40 @@
+import ChessRules from "/base_rules.js";
+
+export default class CoronationRules extends ChessRules {
+
+  get hasSelfCaptures() {
+    return true;
+  }
+
+  canSelfTake([x1, y1], [x2, y2]) {
+    const c = this.getColor(x1, y1);
+    if (
+      this.board.some(row =>
+        row.some(square =>
+          square[0] == c && square[1] == 'q')
+      )
+    ) {
+      // Already a queen on the board: no coronation
+      return false;
+    }
+    const [p1, p2] = [this.getPiece(x1, y1), this.getPiece(x2, y2)];
+    return ((p1 == 'r' && p2 == 'b') || (p1 == 'b' && p2 == 'r'));
+  }
+
+  getPotentialMovesOf(piece, [x, y]) {
+    const res = super.getPotentialMovesOf(piece, [x, y]);
+    if (['r', 'b'].includes(piece)) {
+      res.forEach(m => {
+        if (
+          m.vanish.length == 2 &&
+          m.appear.length == 1 &&
+          m.vanish[1].c == m.vanish[0].c
+        ) {
+          m.appear[0].p = 'q';
+        }
+      });
+    }
+    return res;
+  }
+
+};
diff --git a/variants/Coronation/rules.html b/variants/Coronation/rules.html
new file mode 100644 (file)
index 0000000..a050dcb
--- /dev/null
@@ -0,0 +1,10 @@
+<p>
+  If a side lacks a queen, then a special fusion move is allowed by
+  "capturing" a rook with a bishop (and conversely). The result of the
+  capture is a queen.
+</p>
+
+<p class="author">
+  Variant mentioned
+  <a href="https://www.chessvariants.com/play/erf/CoronatC.html">here</a>.
+</p>
diff --git a/variants/Coronation/style.css b/variants/Coronation/style.css
new file mode 100644 (file)
index 0000000..290a6f4
--- /dev/null
@@ -0,0 +1 @@
+@import url("/base_pieces.css")
diff --git a/variants/Crossing/class.js b/variants/Crossing/class.js
new file mode 100644 (file)
index 0000000..75665a5
--- /dev/null
@@ -0,0 +1,34 @@
+import ChessRules from "/base_rules.js";
+
+export default class CrossingRules extends ChessRules {
+
+  getSvgChessboard() {
+    let svg = super.getSvgChessboard();
+    return (
+      svg.slice(0, -6) +
+      '<line x1="0" y1="40" x2="80" y2="40" ' +
+      'style="stroke:black;stroke-width:0.2"/></svg>'
+    );
+  }
+
+  getCurrentScore(move_s) {
+    const res = super.getCurrentScore(move_s);
+    if (res != "*")
+      return res;
+    // Turn has changed:
+    const color = V.GetOppTurn(this.turn);
+    const secondHalf = (color == 'w' ? [0, 1, 2, 3] : [4, 5, 6, 7]);
+    for (let move of move_s) {
+      if (
+        move.appear.length >= 1 &&
+        move.appear[0].p == 'k' &&
+        secondHalf.includes(move.appear[0].x)
+      ) {
+        // Half-board is crossed
+        return color == "w" ? "1-0" : "0-1";
+      }
+    }
+    return "*";
+  }
+
+};
diff --git a/variants/Crossing/rules.html b/variants/Crossing/rules.html
new file mode 100644 (file)
index 0000000..c5bb1ab
--- /dev/null
@@ -0,0 +1 @@
+<p>Win by reaching the fifth rank with the king.</p>
diff --git a/variants/Crossing/style.css b/variants/Crossing/style.css
new file mode 100644 (file)
index 0000000..a3550bc
--- /dev/null
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
diff --git a/variants/Cwda/class.js b/variants/Cwda/class.js
new file mode 100644 (file)
index 0000000..393e407
--- /dev/null
@@ -0,0 +1,360 @@
+import ChessRules from "/base_rules.js";
+
+export default class CwdaRules extends ChessRules {
+
+  static get Options() {
+    return {
+      select: ChessRules.Options.select.concat([
+        {
+          label: "Army 1",
+          variable: "army1",
+          defaut: 'C',
+          options: [
+            { label: "Colorbound Clobberers", value: 'C' },
+            { label: "Nutty Knights", value: 'N' },
+            { label: "Remarkable Rookies", value: 'R' },
+            { label: "Fide", value: 'F' }
+          ]
+        },
+        {
+          label: "Army 2",
+          variable: "army2",
+          defaut: 'C',
+          options: [
+            { label: "Colorbound Clobberers", value: 'C' },
+            { label: "Nutty Knights", value: 'N' },
+            { label: "Remarkable Rookies", value: 'R' },
+            { label: "Fide", value: 'F' }
+          ]
+        }
+      ]),
+      input: ChessRules.Options.input,
+      styles: ChessRules.Options.styles
+    };
+  }
+
+  static get PiecesMap() {
+    return {
+      // Colorbound Clobberers
+      'C': {
+        'r': 'd',
+        'n': 'w',
+        'b': 'f',
+        'q': 'c',
+        'k': 'm',
+        'p': 'z'
+      },
+      // Nutty Knights
+      'N': {
+        'r': 'g',
+        'n': 'i',
+        'b': 't',
+        'q': 'l',
+        'k': 'e',
+        'p': 'v'
+      },
+      // Remarkable Rookies
+      'R': {
+        'r': 's',
+        'n': 'y',
+        'b': 'h',
+        'q': 'o',
+        'k': 'a',
+        'p': 'u'
+      }
+    };
+  }
+
+  genRandInitBaseFen() {
+    let s = FenUtil.setupPieces(
+      ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
+      {
+        randomness: this.options["randomness"],
+        between: [{p1: 'k', p2: 'r'}],
+        diffCol: ['b'],
+        flags: ['r', 'k']
+      }
+    );
+    let pawnLines = {
+      w: "pppppppp",
+      b: "pppppppp"
+    };
+    for (const c of ['w', 'b']) {
+      const army = "army" + (c == 'w' ? "1" : "2");
+      if (this.options[army] != 'F') {
+        for (let obj of [s, pawnLines]) {
+          obj[c] = obj[c].split("")
+            .map(p => V.PiecesMap[this.options[army]][p]).join("");
+        }
+      }
+    }
+    return {
+      fen: s.b.join("") + "/" +
+           pawnLines['b'] + "/8/8/8/8/" + pawnLines['w'].toUpperCase() +
+           "/" + s.w.join("").toUpperCase(),
+      o: {flags: s.flags}
+    };
+  }
+
+  getPartFen(o) {
+    return Object.assign(
+      { "armies": this.options["army1"] + this.options["army2"] },
+      super.getPartFen(o)
+    );
+  }
+
+  setOtherVariables(fenParsed) {
+    super.setOtherVariables(fenParsed);
+    this.army1 = fenParsed.armies.charAt(0);
+    this.army2 = fenParsed.armies.charAt(1);
+  }
+
+  isKing(x, y, p) {
+    if (!p)
+      p = this.getPiece(x, y);
+    return (super.isKing(x, y, p) || ['a', 'e', 'm'].includes(p));
+  }
+
+  // Helper to describe pieces movements
+  static get steps() {
+    return {
+      // Dabbabah
+      'd': [
+        [-2, 0],
+        [0, -2],
+        [2, 0],
+        [0, 2]
+      ],
+      // Alfil
+      'a': [
+        [2, 2],
+        [2, -2],
+        [-2, 2],
+        [-2, -2]
+      ],
+      // Ferz
+      'f': [
+        [1, 1],
+        [1, -1],
+        [-1, 1],
+        [-1, -1]
+      ],
+      // Wazir
+      'w': [
+        [-1, 0],
+        [0, -1],
+        [1, 0],
+        [0, 1]
+      ],
+      // Threeleaper
+      '$3': [
+        [-3, 0],
+        [0, -3],
+        [3, 0],
+        [0, 3]
+      ],
+      // Narrow knight
+      '$n': [
+        [-2, -1],
+        [-2, 1],
+        [2, -1],
+        [2, 1]
+      ]
+    };
+  }
+
+  pieces(color, x, y) {
+    const res = super.pieces(color, x, y);
+    return Object.assign(
+      {
+        'd': {
+          "class": "c_rook",
+          both: [
+            {steps: V.steps.b},
+            {steps: V.steps.d, range: 1}
+          ]
+        },
+        'w': {
+          "class": "c_knight",
+          both: [
+            {steps: V.steps.a, range: 1},
+            {steps: V.steps.r, range: 1}
+          ]
+        },
+        'f': {
+          "class": "c_bishop",
+          both: [
+            {steps: V.steps.d, range: 1},
+            {steps: V.steps.a, range: 1},
+            {steps: V.steps.b, range: 1}
+          ]
+        },
+        'c': {
+          "class": "c_queen",
+          both: [
+            {steps: V.steps.b},
+            {steps: V.steps.n, range: 1}
+          ]
+        },
+        'm': { moveas: 'k' },
+        'z': { moveas: 'p' },
+        'g': {
+
+        },
+        'i': {
+
+        },
+        't': {
+
+        },
+        'l': {
+
+        },
+        'e': { moveas: 'k' },
+        'v': { moveas: 'p' },
+        's': {
+
+        },
+        'y': {
+
+        },
+        'h': {
+
+        },
+        'o': {
+
+        },
+        'a': { moveas: 'k' },
+        'u': { moveas: 'p' }
+      },
+      res
+    );
+
+
+
+
+
+  getPotentialN_rookMoves(sq) {
+    const c = this.turn;
+    const rookSteps = [ [0, -1], [0, 1], [c == 'w' ? -1 : 1, 0] ];
+    const backward = (c == 'w' ? 1 : -1);
+    const kingSteps = [ [backward, -1], [backward, 0], [backward, 1] ];
+    return (
+      this.getSlideNJumpMoves(sq, rookSteps).concat(
+      this.getSlideNJumpMoves(sq, kingSteps, 1))
+    );
+  }
+
+  getPotentialN_knightMoves(sq) {
+    return (
+      this.getSlideNJumpMoves(sq, V.steps.$n, 1).concat(
+      this.getSlideNJumpMoves(sq, V.steps.f, 1))
+    );
+  }
+
+  getPotentialN_bishopMoves(sq) {
+    const backward = (this.turn == 'w' ? 1 : -1);
+    const kingSteps = [
+      [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
+    ];
+    const forward = -backward;
+    const knightSteps = [
+      [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
+    ];
+    return (
+      this.getSlideNJumpMoves(sq, knightSteps, 1).concat(
+      this.getSlideNJumpMoves(sq, kingSteps, 1))
+    );
+  }
+
+  getPotentialN_queenMoves(sq) {
+    const backward = (this.turn == 'w' ? 1 : -1);
+    const forward = -backward;
+    const kingSteps = [
+      [forward, -1], [forward, 1],
+      [backward, -1], [backward, 0], [backward, 1]
+    ];
+    const knightSteps = [
+      [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
+    ];
+    const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
+    return (
+      this.getSlideNJumpMoves(sq, rookSteps).concat(
+      this.getSlideNJumpMoves(sq, kingSteps, 1)).concat(
+      this.getSlideNJumpMoves(sq, knightSteps, 1))
+    );
+  }
+
+  getPotentialR_rookMoves(sq) {
+    return this.getSlideNJumpMoves(sq, V.steps.r, 4);
+  }
+
+  getPotentialR_knightMoves(sq) {
+    return (
+      this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
+      this.getSlideNJumpMoves(sq, V.steps.w, 1))
+    );
+  }
+
+  getPotentialR_bishopMoves(sq) {
+    return (
+      this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
+      this.getSlideNJumpMoves(sq, V.steps.f, 1)).concat(
+      this.getSlideNJumpMoves(sq, V.steps.$3, 1))
+    );
+  }
+
+  getPotentialR_queenMoves(sq) {
+    return (
+      this.getSlideNJumpMoves(sq, V.steps.r).concat(
+      this.getSlideNJumpMoves(sq, V.steps.n, 1))
+    );
+  }
+
+      case V.PAWN: {
+        // Can promote in anything from the two current armies
+        let promotions = [];
+        for (let army of ["army1", "army2"]) {
+          if (army == "army2" && this.army2 == this.army1) break;
+          switch (this[army]) {
+            case 'C': {
+              Array.prototype.push.apply(promotions,
+                [V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN]);
+              break;
+            }
+            case 'N': {
+              Array.prototype.push.apply(promotions,
+                [V.N_ROOK, V.N_KNIGHT, V.N_BISHOP, V.N_QUEEN]);
+              break;
+            }
+            case 'R': {
+              Array.prototype.push.apply(promotions,
+                [V.R_ROOK, V.R_KNIGHT, V.R_BISHOP, V.R_QUEEN]);
+              break;
+            }
+            case 'F': {
+              Array.prototype.push.apply(promotions,
+                [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]);
+              break;
+            }
+          }
+        }
+        return super.getPotentialPawnMoves(sq, promotions);
+      }
+      default: return super.getPotentialMovesFrom(sq);
+    }
+
+  getCastleMoves([x, y]) {
+    const color = this.getColor(x, y);
+    let finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
+    if (
+      (color == 'w' && this.army1 == 'C') ||
+      (color == 'b' && this.army2 == 'C')
+    ) {
+      // Colorbound castle long in an unusual way:
+      finalSquares[0] = [1, 2];
+    }
+    return super.getCastleMoves([x, y], finalSquares);
+  }
+
+};