From b21e0e3a8f4af5c04b39c034d13bbb4e1e2abf18 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Thu, 18 Mar 2021 10:44:26 +0100
Subject: [PATCH] Add Copycat, reduce Bario image size

---
 client/src/translations/en.js                |   1 +
 client/src/translations/es.js                |   1 +
 client/src/translations/fr.js                |   1 +
 client/src/translations/rules/Bario/en.pug   |   2 +-
 client/src/translations/rules/Bario/es.pug   |   2 +-
 client/src/translations/rules/Bario/fr.pug   |   2 +-
 client/src/translations/rules/Copycat/en.pug |  28 ++++
 client/src/translations/rules/Copycat/es.pug |  28 ++++
 client/src/translations/rules/Copycat/fr.pug |  28 ++++
 client/src/translations/variants/en.pug      |   1 +
 client/src/translations/variants/es.pug      |   1 +
 client/src/translations/variants/fr.pug      |   1 +
 client/src/variants/Copycat.js               | 144 +++++++++++++++++++
 server/db/populate.sql                       |   1 +
 14 files changed, 238 insertions(+), 3 deletions(-)
 create mode 100644 client/src/translations/rules/Copycat/en.pug
 create mode 100644 client/src/translations/rules/Copycat/es.pug
 create mode 100644 client/src/translations/rules/Copycat/fr.pug
 create mode 100644 client/src/variants/Copycat.js

diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index 50f648a7..555606b5 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -183,6 +183,7 @@ export const translations = {
   "Big board": "Big board",
   "Bishop versus pawns": "Bishop versus pawns",
   "Board upside down": "Board upside down",
+  "Borrow powers": "Borrow powers",
   "Both sides of the mirror": "Both sides of the mirror",
   "Build towers (v1)": "Build towers (v1)",
   "Build towers (v2)": "Build towers (v2)",
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 74414b94..d64bda16 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -183,6 +183,7 @@ export const translations = {
   "Big board": "Gran tablero",
   "Bishop versus pawns": "Alfil contra peones",
   "Board upside down": "Tablero al revés",
+  "Borrow powers": "Pedir prestado poderes",
   "Both sides of the mirror": "Ambos lados del espejo",
   "Build towers (v1)": "Construir torres (v1)",
   "Build towers (v2)": "Construir torres (v2)",
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index 521df052..c516cb21 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -183,6 +183,7 @@ export const translations = {
   "Big board": "Grand échiquier",
   "Bishop versus pawns": "Fou contre pions",
   "Board upside down": "Échiquier à l'envers",
+  "Borrow powers": "Empruntez les pouvoir",
   "Both sides of the mirror": "Les deux côté du miroir",
   "Build towers (v1)": "Bâtissez des tours (v1)",
   "Build towers (v2)": "Bâtissez des tours (v2)",
diff --git a/client/src/translations/rules/Bario/en.pug b/client/src/translations/rules/Bario/en.pug
index 0b76180f..93ecc9d7 100644
--- a/client/src/translations/rules/Bario/en.pug
+++ b/client/src/translations/rules/Bario/en.pug
@@ -12,7 +12,7 @@ p
   | .
 
 figure
-  img.img-center(src="/variants/Bario/chessboard2.jpg")
+  img.img-center(src="/variants/Bario/chessboard2.jpg" style="width:75%")
   figcaption.text-center.
     [With author's permission]
     Undefined pieces on first ranks.
diff --git a/client/src/translations/rules/Bario/es.pug b/client/src/translations/rules/Bario/es.pug
index 19c339eb..733a3f5d 100644
--- a/client/src/translations/rules/Bario/es.pug
+++ b/client/src/translations/rules/Bario/es.pug
@@ -12,7 +12,7 @@ p
   | .
 
 figure
-  img.img-center(src="/variants/Bario/chessboard2.jpg")
+  img.img-center(src="/variants/Bario/chessboard2.jpg" style="width:75%")
   figcaption.text-center.
     [Con permiso del autor]
     Piezas no definidas en las primeras filas.
diff --git a/client/src/translations/rules/Bario/fr.pug b/client/src/translations/rules/Bario/fr.pug
index a8bb3b36..e1126c00 100644
--- a/client/src/translations/rules/Bario/fr.pug
+++ b/client/src/translations/rules/Bario/fr.pug
@@ -12,7 +12,7 @@ p
   | .
 
 figure
-  img.img-center(src="/variants/Bario/chessboard2.jpg")
+  img.img-center(src="/variants/Bario/chessboard2.jpg" style="width:75%")
   figcaption.text-center.
     [Avec la permission de l'auteur]
     Pièces indéfinies sur les premières rangées.
diff --git a/client/src/translations/rules/Copycat/en.pug b/client/src/translations/rules/Copycat/en.pug
new file mode 100644
index 00000000..15765b01
--- /dev/null
+++ b/client/src/translations/rules/Copycat/en.pug
@@ -0,0 +1,28 @@
+p.boxed
+  | If A attacks B, then A can moves like B.
+
+p.
+  This game follows the orthodox chess rules, with one addition:
+  if a piece attacks another friendly one, it borrows powers from it and
+  can move (and capture) like it. This does not apply to pawns and kings,
+  which can neither move like another piece or share their abilities.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:1r3k1r/2p3pp/2P5/p2p4/Pp1Q13/4N3/1PP1PP1P/4K3:
+  .diagram.diag22
+    | fen:1r3k1r/2p3pp/2P1Q3/p2p4/Pp6/4N3/1PP1PP1P/4K3:
+  figcaption Before and after Qe6#
+
+p.
+  On the example above, the queen is attacking the knight and can therefore
+  move like it on both diagrams, so this is checkmate after Qe6.
+
+h3 More information
+
+p
+  | Join the Copycat 
+  a(href="https://discord.gg/tv9hVkQKa9") Discord server
+  | &nbsp;:-)
+
+p Inventors: students at xxNarcissus school.
diff --git a/client/src/translations/rules/Copycat/es.pug b/client/src/translations/rules/Copycat/es.pug
new file mode 100644
index 00000000..089b3389
--- /dev/null
+++ b/client/src/translations/rules/Copycat/es.pug
@@ -0,0 +1,28 @@
+p.boxed
+  | Si A ataca a B, entonces A puede moverse como B.
+
+p.
+  Este juego sigue las reglas del ajedrez ortodoxo, con una adición: si una
+  pieza ataca a otra pieza amiga, toma prestados sus poderes y puede ser
+  mover (y capturar) como ella. Esto no se aplica a peones y reyes,
+  que no pueden imitar otras piezas ni compartir sus habilidades.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:1r3k1r/2p3pp/2P5/p2p4/Pp1Q13/4N3/1PP1PP1P/4K3:
+  .diagram.diag22
+    | fen:1r3k1r/2p3pp/2P1Q3/p2p4/Pp6/4N3/1PP1PP1P/4K3:
+  figcaption Antes y después Qe6#
+
+p.
+  En el ejemplo anterior, la reina ataca al caballo y luego puede moverse
+  como él en los dos diagramas: por lo tanto, es mate después de Qe6.
+
+h3 Más información
+
+p
+  | Únete al 
+  a(href="https://discord.gg/tv9hVkQKa9") servidor Discord
+  | &nbsp;Copycat :-)
+
+p Inventores: alumnos de la escuela de xxNarcissus.
diff --git a/client/src/translations/rules/Copycat/fr.pug b/client/src/translations/rules/Copycat/fr.pug
new file mode 100644
index 00000000..e29e35b3
--- /dev/null
+++ b/client/src/translations/rules/Copycat/fr.pug
@@ -0,0 +1,28 @@
+p.boxed
+  | Si A attaque B, alors A peut bouger comme B.
+
+p.
+  Ce jeu suit les règles des échecs orthodoxes, à un ajout près : si une
+  pièce attaque une autre pièce amie, elle emprunte ses pouvoirs et peut se
+  déplacer (et capturer) comme elle. Cela ne s'applique pas aux pions et rois,
+  qui ne peuvent pas imiter d'autres pièces ni partager leurs capacités.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:1r3k1r/2p3pp/2P5/p2p4/Pp1Q13/4N3/1PP1PP1P/4K3:
+  .diagram.diag22
+    | fen:1r3k1r/2p3pp/2P1Q3/p2p4/Pp6/4N3/1PP1PP1P/4K3:
+  figcaption Avant et après Qe6#
+
+p.
+  Sur l'exemple ci-dessus, la dame attaque le cavalier et peut alors se
+  déplacer comme lui sur les deux diagrammes : c'est donc mat après Qe6.
+
+h3 Plus d'information
+
+p
+  | Rejoignez le 
+  a(href="https://discord.gg/tv9hVkQKa9") serveur Discord
+  | &nbsp;Copycat :-)
+
+p Inventeurs : étudiants à l'école de xxNarcissus.
diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug
index c384bab9..6432c80e 100644
--- a/client/src/translations/variants/en.pug
+++ b/client/src/translations/variants/en.pug
@@ -465,6 +465,7 @@ p.
     "Bario",
     "Bicolour",
     "Convert",
+    "Copycat",
     "Evolution",
     "Forward",
     "Fusion",
diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug
index d8151ef2..ebf9d74c 100644
--- a/client/src/translations/variants/es.pug
+++ b/client/src/translations/variants/es.pug
@@ -475,6 +475,7 @@ p.
     "Bario",
     "Bicolour",
     "Convert",
+    "Copycat",
     "Evolution",
     "Forward",
     "Fusion",
diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug
index 19a799dd..7eb09e90 100644
--- a/client/src/translations/variants/fr.pug
+++ b/client/src/translations/variants/fr.pug
@@ -473,6 +473,7 @@ p.
     "Bario",
     "Bicolour",
     "Convert",
+    "Copycat",
     "Evolution",
     "Forward",
     "Fusion",
diff --git a/client/src/variants/Copycat.js b/client/src/variants/Copycat.js
new file mode 100644
index 00000000..089b92f8
--- /dev/null
+++ b/client/src/variants/Copycat.js
@@ -0,0 +1,144 @@
+import { ChessRules } from "@/base_rules";
+
+export class CopycatRules extends ChessRules {
+
+  getPotentialMovesFrom([x, y]) {
+    let moves = super.getPotentialMovesFrom([x, y]);
+    // Expand potential moves if attacking friendly pieces.
+    const piece = this.getPiece(x,y);
+    if ([V.PAWN, V.KING].includes(piece)) return moves;
+    const color = this.turn;
+    const oneStep = (piece == V.PAWN);
+    let movements = {},
+        steps = [];
+    if (piece == V.QUEEN) steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+    else steps = V.steps[piece];
+    steps.forEach(s => {
+      let [i, j] = [x + s[0], y + s[1]];
+      while (
+        V.OnBoard(i, j) &&
+        this.board[i][j] == V.EMPTY &&
+        piece != V.KNIGHT
+      ) {
+        i += s[0];
+        j += s[1];
+      }
+      if (V.OnBoard(i, j) && this.getColor(i, j) == color) {
+        const attacked = this.getPiece(i, j);
+        if ([V.ROOK, V.BISHOP, V.KNIGHT].includes(attacked)) {
+          if (!movements[attacked]) movements[attacked] = true;
+        }
+        else if (attacked == V.QUEEN) {
+          if (!movements[V.ROOK]) movements[V.ROOK] = true;
+          if (!movements[V.BISHOP]) movements[V.BISHOP] = true;
+        }
+      }
+    });
+    Object.keys(movements).forEach(type => {
+      if (
+        (piece != V.QUEEN && type != piece) ||
+        (piece == V.QUEEN && type == V.KNIGHT)
+      ) {
+        Array.prototype.push.apply(moves,
+          this.getSlideNJumpMoves([x, y], V.steps[type], type == V.KNIGHT));
+      }
+    });
+    return moves;
+  }
+
+  // Detect indirect attacks:
+  isAttackedBy_aux(
+    [x, y], color, steps1, oneStep1, piece1, steps2, oneStep2, pieces2)
+  {
+    for (let s1 of steps1) {
+      let i = x + s1[0],
+          j = y + s1[1];
+      while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY && !oneStep1) {
+        i += s1[0];
+        j += s1[1];
+      }
+      if (
+        V.OnBoard(i, j) &&
+        this.board[i][j] != V.EMPTY &&
+        this.getPiece(i, j) == piece1 &&
+        this.getColor(i, j) == color
+      ) {
+        // Continue to detect "copycat" attacks
+        for (let s2 of steps2) {
+          let ii = i + s2[0],
+              jj = j + s2[1];
+          while (
+            V.OnBoard(ii, jj) &&
+            this.board[ii][jj] == V.EMPTY &&
+            !oneStep2
+          ) {
+            ii += s2[0];
+            jj += s2[1];
+          }
+          if (
+            V.OnBoard(ii, jj) &&
+            this.board[ii][jj] != V.EMPTY &&
+            pieces2.includes(this.getPiece(ii, jj)) &&
+            this.getColor(ii, jj) == color
+          ) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  isAttackedByKnight(sq, color) {
+    if (super.isAttackedByKnight(sq, color)) return true;
+    return (
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.ROOK], false, V.KNIGHT,
+        V.steps[V.KNIGHT], true, [V.ROOK, V.QUEEN]
+      ) ||
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.BISHOP], false, V.KNIGHT,
+        V.steps[V.KNIGHT], true, [V.BISHOP, V.QUEEN]
+      )
+    );
+  }
+
+  isAttackedByRook(sq, color) {
+    if (super.isAttackedByRook(sq, color)) return true;
+    return (
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.KNIGHT], true, V.ROOK,
+        V.steps[V.ROOK], false, [V.KNIGHT]
+      ) ||
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.BISHOP], false, V.ROOK,
+        V.steps[V.ROOK], false, [V.BISHOP, V.QUEEN]
+      )
+    );
+  }
+
+  isAttackedByBishop(sq, color) {
+    if (super.isAttackedByBishop(sq, color)) return true;
+    return (
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.KNIGHT], true, V.BISHOP,
+        V.steps[V.BISHOP], false, [V.KNIGHT]
+      ) ||
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.ROOK], false, V.BISHOP,
+        V.steps[V.BISHOP], false, [V.ROOK, V.QUEEN]
+      )
+    );
+  }
+
+  isAttackedByQueen(sq, color) {
+    if (super.isAttackedByQueen(sq, color)) return true;
+    return (
+      this.isAttackedBy_aux(sq, color,
+        V.steps[V.KNIGHT], true, V.QUEEN,
+        V.steps[V.ROOK].concat(V.steps[V.BISHOP]), false, [V.KNIGHT]
+      )
+    );
+  }
+
+};
diff --git a/server/db/populate.sql b/server/db/populate.sql
index aa0dace2..56347720 100644
--- a/server/db/populate.sql
+++ b/server/db/populate.sql
@@ -49,6 +49,7 @@ insert or ignore into Variants (name, description) values
   ('Clorange', 'A Clockwork Orange'),
   ('Colorbound', 'The colorbound clobberers'),
   ('Convert', 'Convert enemy pieces'),
+  ('Copycat', 'Borrow powers'),
   ('Coregal', 'Two royal pieces'),
   ('Coronation', 'Long live the Queen'),
   ('Crazyhouse', 'Captures reborn'),
-- 
2.44.0