Add Rampage Chess
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 2 May 2020 12:27:48 +0000 (14:27 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 2 May 2020 12:27:48 +0000 (14:27 +0200)
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Rampage/en.pug [new file with mode: 0644]
client/src/translations/rules/Rampage/es.pug [new file with mode: 0644]
client/src/translations/rules/Rampage/fr.pug [new file with mode: 0644]
client/src/variants/Rampage.js
server/db/populate.sql

index a72cf61..8b7daa7 100644 (file)
@@ -210,6 +210,7 @@ export const translations = {
   "Mongolian Horde": "Mongolian Horde",
   "Move like a knight (v1)": "Move like a knight (v1)",
   "Move like a knight (v2)": "Move like a knight (v2)",
   "Mongolian Horde": "Mongolian Horde",
   "Move like a knight (v1)": "Move like a knight (v1)",
   "Move like a knight (v2)": "Move like a knight (v2)",
+  "Move under cover": "Move under cover",
   "Neverending rows": "Neverending rows",
   "No-check mode": "No-check mode",
   "Non-conformism and utopia": "Non-conformism and utopia",
   "Neverending rows": "Neverending rows",
   "No-check mode": "No-check mode",
   "Non-conformism and utopia": "Non-conformism and utopia",
index 10e5422..4b5c275 100644 (file)
@@ -210,6 +210,7 @@ export const translations = {
   "Mongolian Horde": "Horda mongol",
   "Move like a knight (v1)": "Moverse como un caballo (v1)",
   "Move like a knight (v2)": "Moverse como un caballo (v2)",
   "Mongolian Horde": "Horda mongol",
   "Move like a knight (v1)": "Moverse como un caballo (v1)",
   "Move like a knight (v2)": "Moverse como un caballo (v2)",
+  "Move under cover": "Ir bajo cubierta",
   "Neverending rows": "Filas interminables",
   "No-check mode": "Modo sin jaque",
   "Non-conformism and utopia": "No-conformismo y utopía",
   "Neverending rows": "Filas interminables",
   "No-check mode": "Modo sin jaque",
   "Non-conformism and utopia": "No-conformismo y utopía",
index c4de144..8684b28 100644 (file)
@@ -210,6 +210,7 @@ export const translations = {
   "Mongolian Horde": "Horde mongole",
   "Move like a knight (v1)": "Bouger comme un cavalier (v1)",
   "Move like a knight (v2)": "Bouger comme un cavalier (v2)",
   "Mongolian Horde": "Horde mongole",
   "Move like a knight (v1)": "Bouger comme un cavalier (v1)",
   "Move like a knight (v2)": "Bouger comme un cavalier (v2)",
+  "Move under cover": "Avancez à couvert",
   "Neverending rows": "Rangées sans fin",
   "No-check mode": "Mode sans échec",
   "Non-conformism and utopia": "Non-conformisme et utopie",
   "Neverending rows": "Rangées sans fin",
   "No-check mode": "Mode sans échec",
   "Non-conformism and utopia": "Non-conformisme et utopie",
diff --git a/client/src/translations/rules/Rampage/en.pug b/client/src/translations/rules/Rampage/en.pug
new file mode 100644 (file)
index 0000000..a7ddca7
--- /dev/null
@@ -0,0 +1,29 @@
+p.boxed
+  | Units may move to any square that is guarded by more friendly units
+  | than enemy units.
+
+p.
+  In addition to their usual abilities, pieces may jump to controlled squares,
+  that is to say squares attacked by more friendly pieces than enemy pieces.
+  The king in check may only move as in orthodox chess, but "rampage" moves
+  of other pieces are allowed to cover the check.
+
+p.
+  Attacks on the same row, file or diagonal are cumulative. So,
+  on the next diagram the marked square (e5) is guarded twice by each side:
+  "rampage" moves to that square are impossible.
+
+figure.diagram-container
+  .diagram
+    | fen:k7/p7/2n1r3/8/8/8/4R2P/4Q2K e5:
+  figcaption No repositioning moves to e5.
+
+h3 Source
+
+p
+  | This variant is 
+  a(href="https://www.chessvariants.com/play/erf/RampageC.html")
+    | mentioned here
+  | .
+
+p Inventor: Bruce R. Trose (1976)
diff --git a/client/src/translations/rules/Rampage/es.pug b/client/src/translations/rules/Rampage/es.pug
new file mode 100644 (file)
index 0000000..4fedc77
--- /dev/null
@@ -0,0 +1,30 @@
+p.boxed
+  | Las piezas pueden moverse en cualquier espacio defendido por más de
+  | piezas amigas que piezas enemigas.
+
+p.
+  Además de sus capacidades habituales, las piezas pueden saltar a
+  espacios controlados, es decir, espacios atacados por más piezas del
+  mismo lado que las piezas opuestas.
+  El rey en jaque solo puede hacer movimientos normales, pero las jugadas
+  "rampage" de las otras piezas pueden contrarrestar el jaque.
+
+p.
+  Los ataques a la misma columna, fila o diagonal son acumulativos. Entonces
+  en el siguiente diagrama, la casilla marcada (e5) es defendida dos veces por
+  cada lado: los movimientos "rampage" hacia esta casilla son imposibles.
+
+figure.diagram-container
+  .diagram
+    | fen:k7/p7/2n1r3/8/8/8/4R2P/4Q2K e5:
+  figcaption No movimientos de reposicionamiento a e5.
+
+h3 Fuente
+
+p
+  | Esta variante es 
+  a(href="https://www.chessvariants.com/play/erf/RampageC.html")
+    | mencionada aquí
+  | .
+
+p Inventor: Bruce R. Trose (1976)
diff --git a/client/src/translations/rules/Rampage/fr.pug b/client/src/translations/rules/Rampage/fr.pug
new file mode 100644 (file)
index 0000000..d0a57f5
--- /dev/null
@@ -0,0 +1,30 @@
+p.boxed
+  | Les pièces peuvent se déplacer sur toute case défendue par plus de
+  | pièces amies que de pièces ennemies.
+
+p.
+  En plus de leurs capacités habituelles, les pièces peuvent sauter vers des
+  cases controlées, c'est-à-dire des cases attaquées par plus de pièces du
+  même camp que de pièces adverses.
+  Le roi en échec ne peut effectuer que des coups normaux, mais les coups
+  "rampage" des autres pièces sont autorisés pour parer l'échec.
+
+p.
+  Les attaques sur la même colonne, rangée ou diagonale se cumulent. Ainsi,
+  sur le diagramme suivant la case marquée (e5) est défendue deux fois par
+  chaque camp : les coups "rampage" vers cette case sont impossibles.
+
+figure.diagram-container
+  .diagram
+    | fen:k7/p7/2n1r3/8/8/8/4R2P/4Q2K e5:
+  figcaption Pas de coups de repositionnement vers e5.
+
+h3 Source
+
+p
+  | Cette variante est 
+  a(href="https://www.chessvariants.com/play/erf/RampageC.html")
+    | mentionnée ici
+  | .
+
+p Inventeur : Bruce R. Trose (1976)
index 31d4f35..2fe96db 100644 (file)
@@ -1,10 +1,81 @@
 import { ChessRules } from "@/base_rules";
 
 import { ChessRules } from "@/base_rules";
 
-// Plan : garder intact isAttacked,
-// ajouter "guarded" qui somme les attaques blanches et soustrait les attaques noires sur une case
-// (regarder toutes directions, OK)
-// --> boulot à faire sur chaque case vide, après chaque getPotentialMoves()
-// --> sauf si le roi est en échec (avant de jouer).
-
 export class RampageRules extends ChessRules {
 export class RampageRules extends ChessRules {
+  // Sum white pieces attacking a square, and remove black pieces count.
+  sumAttacks([x, y]) {
+    const getSign = (color) => {
+      return (color == 'w' ? 1 : -1);
+    };
+    let res = 0;
+    // Knights:
+    V.steps[V.KNIGHT].forEach(s => {
+      const [i, j] = [x + s[0], y + s[1]];
+      if (V.OnBoard(i, j) && this.getPiece(i, j) == V.KNIGHT)
+        res += getSign(this.getColor(i, j));
+    });
+    // Kings:
+    V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(s => {
+      const [i, j] = [x + s[0], y + s[1]];
+      if (V.OnBoard(i, j) && this.getPiece(i, j) == V.KING)
+        res += getSign(this.getColor(i, j));
+    });
+    // Pawns:
+    for (let c of ['w', 'b']) {
+      for (let shift of [-1, 1]) {
+        const sign = getSign(c);
+        const [i, j] = [x + sign, y + shift];
+        if (
+          V.OnBoard(i, j) &&
+          this.getPiece(i, j) == V.PAWN &&
+          this.getColor(i, j) == c
+        ) {
+          res += sign;
+        }
+      }
+    }
+    // Other pieces (sliders):
+    V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(s => {
+      let [i, j] = [x + s[0], y + s[1]];
+      let compatible = [V.QUEEN];
+      compatible.push(s[0] == 0 || s[1] == 0 ? V.ROOK : V.BISHOP);
+      let firstCol = undefined;
+      while (V.OnBoard(i, j)) {
+        if (this.board[i][j] != V.EMPTY) {
+          if (!(compatible.includes(this.getPiece(i, j)))) break;
+          const colIJ = this.getColor(i, j);
+          if (!firstCol) firstCol = colIJ;
+          if (colIJ == firstCol) res += getSign(colIJ);
+          else break;
+        }
+        i += s[0];
+        j += s[1];
+      }
+    });
+    return res;
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    let moves = super.getPotentialMovesFrom([x, y]);
+    const color = this.turn;
+    if (this.getPiece(x, y) == V.KING && this.underCheck(color))
+      // The king under check can only move as usual
+      return moves;
+    // Remember current final squares to not add moves twice:
+    const destinations = {};
+    moves.forEach(m => destinations[m.end.x + "_" + m.end.y] = true);
+    for (let i=0; i<8; i++) {
+      for (let j=0; j<8; j++) {
+        if (this.board[i][j] == V.EMPTY && !destinations[i + "_" + j]) {
+          const sa = this.sumAttacks([i, j]);
+          if ((color == 'w' && sa > 0) || (color == 'b' && sa < 0))
+            moves.push(this.getBasicMove([x, y], [i, j]));
+        }
+      }
+    }
+    return moves;
+  }
+
+  static get SEARCH_DEPTH() {
+    return 1;
+  }
 };
 };
index 4941967..97f78bc 100644 (file)
@@ -70,6 +70,7 @@ insert or ignore into Variants (name, description) values
   ('Parachute', 'Landing on the board'),
   ('Perfect', 'Powerful pieces'),
   ('Racingkings', 'Kings cross the 8x8 board'),
   ('Parachute', 'Landing on the board'),
   ('Perfect', 'Powerful pieces'),
   ('Racingkings', 'Kings cross the 8x8 board'),
+  ('Rampage', 'Move under cover'),
   ('Rifle', 'Shoot pieces'),
   ('Recycle', 'Reuse pieces'),
   ('Rococo', 'Capture on the edge'),
   ('Rifle', 'Shoot pieces'),
   ('Recycle', 'Reuse pieces'),
   ('Rococo', 'Capture on the edge'),