Add Stealthbomb
authorBenjamin Auder <benjamin.auder@somewhere>
Wed, 24 Mar 2021 18:04:16 +0000 (19:04 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Wed, 24 Mar 2021 18:04:16 +0000 (19:04 +0100)
27 files changed:
client/public/images/pieces/Stealthbomb/bc.svg [new symlink]
client/public/images/pieces/Stealthbomb/bl.svg [new symlink]
client/public/images/pieces/Stealthbomb/bo.svg [new symlink]
client/public/images/pieces/Stealthbomb/bs.svg [new symlink]
client/public/images/pieces/Stealthbomb/bt.svg [new symlink]
client/public/images/pieces/Stealthbomb/bu.svg [new symlink]
client/public/images/pieces/Stealthbomb/wc.svg [new symlink]
client/public/images/pieces/Stealthbomb/wl.svg [new symlink]
client/public/images/pieces/Stealthbomb/wo.svg [new symlink]
client/public/images/pieces/Stealthbomb/ws.svg [new symlink]
client/public/images/pieces/Stealthbomb/wt.svg [new symlink]
client/public/images/pieces/Stealthbomb/wu.svg [new symlink]
client/src/translations/about/en.pug
client/src/translations/about/es.pug
client/src/translations/about/fr.pug
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Stealthbomb/en.pug [new file with mode: 0644]
client/src/translations/rules/Stealthbomb/es.pug [new file with mode: 0644]
client/src/translations/rules/Stealthbomb/fr.pug [new file with mode: 0644]
client/src/translations/variants/en.pug
client/src/translations/variants/es.pug
client/src/translations/variants/fr.pug
client/src/variants/Bario.js
client/src/variants/Stealthbomb.js [new file with mode: 0644]
server/db/populate.sql

diff --git a/client/public/images/pieces/Stealthbomb/bc.svg b/client/public/images/pieces/Stealthbomb/bc.svg
new file mode 120000 (symlink)
index 0000000..b30a26a
--- /dev/null
@@ -0,0 +1 @@
+../Alice/bc.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/bl.svg b/client/public/images/pieces/Stealthbomb/bl.svg
new file mode 120000 (symlink)
index 0000000..a10d9e0
--- /dev/null
@@ -0,0 +1 @@
+../Alice/bl.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/bo.svg b/client/public/images/pieces/Stealthbomb/bo.svg
new file mode 120000 (symlink)
index 0000000..1200186
--- /dev/null
@@ -0,0 +1 @@
+../Alice/bo.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/bs.svg b/client/public/images/pieces/Stealthbomb/bs.svg
new file mode 120000 (symlink)
index 0000000..e8cf23a
--- /dev/null
@@ -0,0 +1 @@
+../Alice/bs.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/bt.svg b/client/public/images/pieces/Stealthbomb/bt.svg
new file mode 120000 (symlink)
index 0000000..c517549
--- /dev/null
@@ -0,0 +1 @@
+../Alice/bt.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/bu.svg b/client/public/images/pieces/Stealthbomb/bu.svg
new file mode 120000 (symlink)
index 0000000..09e6ea3
--- /dev/null
@@ -0,0 +1 @@
+../Alice/bu.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/wc.svg b/client/public/images/pieces/Stealthbomb/wc.svg
new file mode 120000 (symlink)
index 0000000..d23af91
--- /dev/null
@@ -0,0 +1 @@
+../Alice/wc.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/wl.svg b/client/public/images/pieces/Stealthbomb/wl.svg
new file mode 120000 (symlink)
index 0000000..51c1893
--- /dev/null
@@ -0,0 +1 @@
+../Alice/wl.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/wo.svg b/client/public/images/pieces/Stealthbomb/wo.svg
new file mode 120000 (symlink)
index 0000000..4a85712
--- /dev/null
@@ -0,0 +1 @@
+../Alice/wo.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/ws.svg b/client/public/images/pieces/Stealthbomb/ws.svg
new file mode 120000 (symlink)
index 0000000..659b2de
--- /dev/null
@@ -0,0 +1 @@
+../Alice/ws.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/wt.svg b/client/public/images/pieces/Stealthbomb/wt.svg
new file mode 120000 (symlink)
index 0000000..447fc4f
--- /dev/null
@@ -0,0 +1 @@
+../Alice/wt.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Stealthbomb/wu.svg b/client/public/images/pieces/Stealthbomb/wu.svg
new file mode 120000 (symlink)
index 0000000..c1403b3
--- /dev/null
@@ -0,0 +1 @@
+../Alice/wu.svg
\ No newline at end of file
index f1e0c93..4979575 100644 (file)
@@ -44,7 +44,8 @@ h3 Related links
 #links
   a(href="https://www.chessvariants.com/") chessvariants.com
   a(href="https://greenchess.net/") greenchess.net
-  a(href="http://pychess-variants.herokuapp.com/") pychess-variants.com
+  a(href="https://www.pychess.org/") pychess.org
+  a(href="https://fishrandom.io/") fishrandom.io
   a(href="https://glukkazan.github.io/") Dagaz demo + server
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
index bcc6ff0..4b08b48 100644 (file)
@@ -43,7 +43,8 @@ h3 Enlaces relacionados
 #links
   a(href="https://www.chessvariants.com/") chessvariants.com
   a(href="https://greenchess.net/") greenchess.net
-  a(href="http://pychess-variants.herokuapp.com/") pychess-variants.com
+  a(href="https://www.pychess.org/") pychess.org
+  a(href="https://fishrandom.io/") fishrandom.io
   a(href="https://glukkazan.github.io/") Dagaz demo + servidor
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
index 537a79b..cf58ca7 100644 (file)
@@ -44,7 +44,8 @@ h3 Liens connexes
 #links
   a(href="https://www.chessvariants.com/") chessvariants.com
   a(href="https://greenchess.net/") greenchess.net
-  a(href="http://pychess-variants.herokuapp.com/") pychess-variants.com
+  a(href="https://www.pychess.org/") pychess.org
+  a(href="https://fishrandom.io/") fishrandom.io
   a(href="https://glukkazan.github.io/") Dagaz demo + serveur
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
index 604fbf8..a86b6e1 100644 (file)
@@ -181,6 +181,7 @@ export const translations = {
   "Augmented Queens": "Augmented Queens",
   "Balanced sliders & leapers": "Balanced sliders & leapers",
   "Baroque Music": "Baroque Music",
+  "Beware the bomb": "Beware the bomb",
   "Big board": "Big board",
   "Bishop versus pawns": "Bishop versus pawns",
   "Board upside down": "Board upside down",
index 9a099a2..a5cd67b 100644 (file)
@@ -181,6 +181,7 @@ export const translations = {
   "Augmented Queens": "Damas aumentadas",
   "Balanced sliders & leapers": "Modos de desplazamiento equilibrados",
   "Baroque Music": "Música Barroca",
+  "Beware the bomb": "Cuidado con la bomba",
   "Big board": "Gran tablero",
   "Bishop versus pawns": "Alfil contra peones",
   "Board upside down": "Tablero al revés",
index 5bd4aea..9ca4717 100644 (file)
@@ -181,6 +181,7 @@ export const translations = {
   "Augmented Queens": "Dames augmentées",
   "Balanced sliders & leapers": "Modes de déplacement équilibrés",
   "Baroque Music": "Musique Baroque",
+  "Beware the bomb": "Attention à la bombe",
   "Big board": "Grand échiquier",
   "Bishop versus pawns": "Fou contre pions",
   "Board upside down": "Échiquier à l'envers",
diff --git a/client/src/translations/rules/Stealthbomb/en.pug b/client/src/translations/rules/Stealthbomb/en.pug
new file mode 100644 (file)
index 0000000..09ba3bd
--- /dev/null
@@ -0,0 +1,31 @@
+p.boxed.
+  One piece of each side hides a bomb,
+  which can be triggered instead of playing a move.
+
+p.
+  Each player secretly decides of a piece which will carry a bomb.
+  (Click on any piece at move 1).
+
+p.
+  At any time, instead of playing a move you may detonate the bomb:
+  bring the loaded piece onto your king.
+  On the diagrams below, bombs are placed at c2 and e4.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/2N5/PPoP1PPP/R1BK2NR:
+  .diagram.diag22
+    | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/8/P4PPP/R5NR:
+  figcaption Not checkmate: explosion on c2 follows.
+
+p.
+  An explosion destroys everything a king step away from the bomb,
+  including the loaded piece. Explosion has priority over checkmate.
+
+h3 Source
+
+p
+  a(href="https://en.wikipedia.org/wiki/Beirut_chess") Beirut Chess
+  | . This variant is Stealthbomber on 
+  a(href="fishrandom.io") fishrandom.io
+  | , and "Stealthbomb" here... why not :-)
diff --git a/client/src/translations/rules/Stealthbomb/es.pug b/client/src/translations/rules/Stealthbomb/es.pug
new file mode 100644 (file)
index 0000000..758e7e9
--- /dev/null
@@ -0,0 +1,31 @@
+p.boxed.
+  Una pieza de cada campamento esconde una bomba,
+  que se puede activar en lugar de ejecutar un movimiento.
+
+p.
+  Cada jugador decide en secreto qué pieza llevará una bomba.
+  (Haga clic en una pieza en el primer movimiento).
+
+p.
+  En cualquier momento, puede detonar la bomba en lugar de hacer una jugada:
+  lleva la pieza cargada a tu rey.
+  En los siguientes diagramas, las bombas se colocan en c2 y e4.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/2N5/PPoP1PPP/R1BK2NR:
+  .diagram.diag22
+    | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/8/P4PPP/R5NR:
+  figcaption No jaque mate: sigue la explosión en c2.
+
+p.
+  Una explosión destruye todo a un paso de la bomba, incluyendo
+  la pieza cargada. La explosión tiene prioridad sobre el jaque mate.
+
+h3 Fuente
+
+p
+  a(href="https://en.wikipedia.org/wiki/Beirut_chess") Beirut Chess
+  | . Esta variante es Stealthbomber en 
+  a(href="fishrandom.io") fishrandom.io
+  | , y "Stealthbomb" aquí... ¿Por qué no? :-)
diff --git a/client/src/translations/rules/Stealthbomb/fr.pug b/client/src/translations/rules/Stealthbomb/fr.pug
new file mode 100644 (file)
index 0000000..ecf61cc
--- /dev/null
@@ -0,0 +1,31 @@
+p.boxed.
+  Une pièce de chaque camp cache une bombe,
+  qui peut être déclenchée au lieu de jouer un coup.
+
+p.
+  Chaque joueur décide secrètement d'une pièce qui portera une bombe.
+  (Cliquez sur une pièce au premier coup).
+
+p.
+  À tout moment, vous pouvez faire exploser la bombe au lieu de jouer un coup :
+  amenez la pièce chargée sur votre roi.
+  Dans les diagrammes ci-dessous, les bombes sont placées en c2 et e4.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/2N5/PPoP1PPP/R1BK2NR:
+  .diagram.diag22
+    | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/8/P4PPP/R5NR:
+  figcaption Pas mat : l'explosion en c2 suit.
+
+p.
+  Une explosion détruit tout à un pas de roi depuis la bombe, incluant
+  la pièce chargée. L'explosion a priorité sur le mat.
+
+h3 Source
+
+p
+  a(href="https://en.wikipedia.org/wiki/Beirut_chess") Beirut Chess
+  | . Cette variante est Stealthbomber sur 
+  a(href="fishrandom.io") fishrandom.io
+  | , et "Stealthbomb" ici... pourquoi pas :-)
index 5c13108..c933d9c 100644 (file)
@@ -222,6 +222,7 @@ p.
     "Dark",
     "Hidden",
     "Hiddenqueen",
+    "Stealthbomb",
     "Synchrone1",
     "Synchrone2"
   ]
index fa4bab9..81fad93 100644 (file)
@@ -229,6 +229,7 @@ p.
     "Dark",
     "Hidden",
     "Hiddenqueen",
+    "Stealthbomb",
     "Synchrone1",
     "Synchrone2"
   ]
index a2dccae..6743ce2 100644 (file)
@@ -228,6 +228,7 @@ p.
     "Dark",
     "Hidden",
     "Hiddenqueen",
+    "Stealthbomb",
     "Synchrone1",
     "Synchrone2"
   ]
index e10bb3d..3564e74 100644 (file)
@@ -73,7 +73,7 @@ export class BarioRules extends ChessRules {
       vanish: [
         new PiPo({ x: square[0], y: square[1], c: c, p: V.UNDEFINED })
       ],
-      start: { x: -1, y: -1 },
+      start: { x: -1, y: -1 }
     });
   }
 
diff --git a/client/src/variants/Stealthbomb.js b/client/src/variants/Stealthbomb.js
new file mode 100644 (file)
index 0000000..ee73ef5
--- /dev/null
@@ -0,0 +1,228 @@
+import { ChessRules, Move, PiPo } from "@/base_rules";
+
+export class StealthbombRules extends ChessRules {
+
+  static get CanAnalyze() {
+    return false;
+  }
+
+  static get SomeHiddenMoves() {
+    return true;
+  }
+
+  static get BOMB_DECODE() {
+    return {
+      s: "p",
+      t: "q",
+      u: "r",
+      c: "b",
+      o: "n",
+      l: "k"
+    };
+  }
+  static get BOMB_CODE() {
+    return {
+      p: "s",
+      q: "t",
+      r: "u",
+      b: "c",
+      n: "o",
+      k: "l"
+    };
+  }
+
+  static get PIECES() {
+    return ChessRules.PIECES.concat(Object.keys(V.BOMB_DECODE));
+  }
+
+  getPiece(i, j) {
+    const piece = this.board[i][j].charAt(1);
+    if (
+      ChessRules.PIECES.includes(piece) ||
+      // 'side' is used to determine what I see: normal or "loaded" piece?
+      this.getColor(i, j) == this.side
+    ) {
+      return piece;
+    }
+    // Loaded piece, but no right to view it
+    return V.BOMB_DECODE[piece];
+  }
+
+  getPpath(b, color, score) {
+    if (Object.keys(V.BOMB_DECODE).includes(b[1])) {
+      // Supposed to be hidden.
+      if (score == "*" && (!color || color != b[0]))
+        return b[0] + V.BOMB_DECODE[b[1]];
+      return "Stealthbomb/" + b;
+    }
+    return b;
+  }
+
+  hoverHighlight([x, y]) {
+    const c = this.turn;
+    return (
+      this.movesCount <= 1 &&
+      (
+        (c == 'w' && x >= 6) ||
+        (c == 'b' && x <= 1)
+      )
+    );
+  }
+
+  onlyClick([x, y]) {
+    return (
+      this.movesCount <= 1 ||
+      // TODO: next line theoretically shouldn't be required...
+      (this.movesCount == 2 && this.getColor(x, y) != this.turn)
+    );
+  }
+
+  // Initiate the game by choosing a square for the bomb:
+  doClick(square) {
+    const c = this.turn;
+    if (
+      this.movesCount >= 2 ||
+      (
+        (c == 'w' && square[0] < 6) ||
+        (c == 'b' && square[0] > 2)
+      )
+    ) {
+      return null;
+    }
+    const [x, y] = square;
+    const piece = super.getPiece(x, y);
+    return new Move({
+      appear: [ new PiPo({ x: x, y: y, c: c, p: V.BOMB_CODE[piece] }) ],
+      vanish: [ new PiPo({ x: x, y: y, c: c, p: piece }) ],
+      start: { x: -1, y: -1 }
+    });
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    if (this.movesCount <= 1) {
+      const setup = this.doClick([x, y]);
+      return (!setup ? [] : [setup]);
+    }
+    let moves = super.getPotentialMovesFrom([x, y]);
+    const c = this.turn;
+    // Add bomb explosion
+    if (Object.keys(V.BOMB_DECODE).includes(this.board[x][y][1])) {
+      let mv = new Move({
+        appear: [ ],
+        vanish: [ new PiPo({ x: x, y: y, c: c, p: this.board[x][y][1] }) ],
+        end: { x: this.kingPos[c][0], y: this.kingPos[c][1] }
+      });
+      for (let s of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
+        let [i, j] = [x + s[0], y + s[1]];
+        if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) {
+          mv.vanish.push(
+            new PiPo({
+              x: i,
+              y: j,
+              c: this.getColor(i, j),
+              p: this.board[i][j][1]
+            })
+          );
+        }
+      }
+      moves.push(mv);
+    }
+    return moves;
+  }
+
+  // NOTE: a lot of copy-paste from Atomic from here.
+  postPlay(move) {
+    if (this.movesCount >= 3) {
+      super.postPlay(move);
+      if (move.appear.length == 0) {
+        // Explosion
+        const firstRank = { w: 7, b: 0 };
+        for (let c of ["w", "b"]) {
+          // Did we explode king of color c ?
+          if (
+            Math.abs(this.kingPos[c][0] - move.start.x) <= 1 &&
+            Math.abs(this.kingPos[c][1] - move.start.y) <= 1
+          ) {
+            this.kingPos[c] = [-1, -1];
+            this.castleFlags[c] = [8, 8];
+          }
+          else {
+            // Now check if init rook(s) exploded
+            if (Math.abs(move.start.x - firstRank[c]) <= 1) {
+              if (Math.abs(move.start.y - this.castleFlags[c][0]) <= 1)
+                this.castleFlags[c][0] = 8;
+              if (Math.abs(move.start.y - this.castleFlags[c][1]) <= 1)
+                this.castleFlags[c][1] = 8;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  postUndo(move) {
+    if (this.movesCount >= 2) {
+      super.postUndo(move);
+      const c = this.turn;
+      const oppCol = V.GetOppCol(c);
+      if ([this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => e < 0)) {
+        // Last move exploded some king..
+        for (let psq of move.vanish) {
+          if (psq.p == "k")
+            this.kingPos[psq.c == c ? c : oppCol] = [psq.x, psq.y];
+        }
+      }
+    }
+  }
+
+  underCheck(color) {
+    const oppCol = V.GetOppCol(color);
+    let res = undefined;
+    // If our king disappeared, move is not valid
+    if (this.kingPos[color][0] < 0) res = true;
+    // If opponent king disappeared, move is valid
+    else if (this.kingPos[oppCol][0] < 0) res = false;
+    // Otherwise, if we remain under check, move is not valid
+    else res = this.isAttacked(this.kingPos[color], oppCol);
+    return res;
+  }
+
+  getCheckSquares() {
+    const color = this.turn;
+    let res = [];
+    if (
+      this.kingPos[color][0] >= 0 && //king might have exploded
+      this.isAttacked(this.kingPos[color], V.GetOppCol(color))
+    ) {
+      res = [JSON.parse(JSON.stringify(this.kingPos[color]))];
+    }
+    return res;
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    const kp = this.kingPos[color];
+    if (kp[0] < 0)
+      // King disappeared
+      return color == "w" ? "0-1" : "1-0";
+    if (this.atLeastOneMove()) return "*";
+    if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2";
+    return color == "w" ? "0-1" : "1-0"; //checkmate
+  }
+
+  getNotation(move) {
+    if (this.movesCount <= 1) return "Bomb?";
+    const c = this.turn;
+    if (move.end.x == this.kingPos[c][0] && move.end.y == this.kingPos[c][1])
+      return V.CoordsToSquare(move.start) + "~X";
+    if (Object.keys(V.BOMB_DECODE).includes(move.vanish[0].p)) {
+      let cpMove = JSON.parse(JSON.stringify(move));
+      cpMove.vanish[0].p = V.BOMB_DECODE[move.vanish[0].p];
+      if (Object.keys(V.BOMB_DECODE).includes(move.appear[0].p))
+        cpMove.appear[0].p = V.BOMB_DECODE[move.appear[0].p];
+      return super.getNotation(cpMove);
+    }
+    return super.getNotation(move);
+  }
+
+};
index 1a13014..13a4fb8 100644 (file)
@@ -156,6 +156,7 @@ insert or ignore into Variants (name, description) values
   ('Spartan', 'Spartan versus Persians'),
   ('Squatter1', 'Squat last rank (v1)'),
   ('Squatter2', 'Squat last rank (v2)'),
+  ('Stealthbomb', 'Beware the bomb'),
   ('Suicide', 'Lose all pieces'),
   ('Suction', 'Attract opposite king'),
   ('Swap', 'Dangerous captures'),