From da9e846e41622111a4f877742dc0b35406635b5c Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sun, 10 Jan 2021 00:49:50 +0100
Subject: [PATCH] Add Relayup

---
 client/src/base_rules.js                     |  3 +-
 client/src/translations/en.js                |  1 +
 client/src/translations/es.js                |  1 +
 client/src/translations/fr.js                |  1 +
 client/src/translations/rules/Relayup/en.pug | 32 ++++++++++
 client/src/translations/rules/Relayup/es.pug | 33 ++++++++++
 client/src/translations/rules/Relayup/fr.pug | 33 ++++++++++
 client/src/translations/variants/en.pug      |  1 +
 client/src/translations/variants/es.pug      |  1 +
 client/src/translations/variants/fr.pug      |  1 +
 client/src/variants/Pacosako.js              | 22 +++++++
 client/src/variants/Relayup.js               | 66 ++++++++++++++++++--
 server/db/populate.sql                       |  1 +
 13 files changed, 189 insertions(+), 7 deletions(-)
 create mode 100644 client/src/translations/rules/Relayup/en.pug
 create mode 100644 client/src/translations/rules/Relayup/es.pug
 create mode 100644 client/src/translations/rules/Relayup/fr.pug

diff --git a/client/src/base_rules.js b/client/src/base_rules.js
index 6502679a..f1049b05 100644
--- a/client/src/base_rules.js
+++ b/client/src/base_rules.js
@@ -1073,8 +1073,7 @@ console.log("ddd");
         V.OnBoard(rx, ry) &&
         this.board[rx][ry] != V.EMPTY &&
         this.getPiece(rx, ry) == piece &&
-        this.getColor(rx, ry) == color &&
-        this.canTake([rx, ry], [x, y]) //for Paco-Sako (TODO: necessary?)
+        this.getColor(rx, ry) == color
       ) {
         return true;
       }
diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index a2f1eaf2..5f8d341f 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -292,6 +292,7 @@ export const translations = {
   "Two kings": "Two kings",
   "Two royal pieces": "Two royal pieces",
   "Unidentified pieces": "Unidentified pieces",
+  "Upgrade pieces": "Upgrade pieces",
   "Walk on a graph": "Walk on a graph",
   "White move twice": "White move twice",
   "Win by castling long": "Win by castling long",
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 97dbec11..be0974aa 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -292,6 +292,7 @@ export const translations = {
   "Two kings": "Dos reyes",
   "Two royal pieces": "Dos piezas reales",
   "Unidentified pieces": "Piezas no identificadas",
+  "Upgrade pieces": "Aumentar piezas",
   "Walk on a graph": "Camino en un gráfico",
   "White move twice": "Las blancas juegan dos veces",
   "Win by castling long": "Ganar jugando al enroque largo",
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index 98706b91..4b6a2f77 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -291,6 +291,7 @@ export const translations = {
   "Two kings": "Deux rois",
   "Two royal pieces": "Deux pièces royales",
   "Unidentified pieces": "Pièces non identifiées",
+  "Upgrade pieces": "Augmenter les pièces",
   "Walk on a graph": "Marche sur un graphe",
   "White move twice": "Les blancs jouent deux fois",
   "Win by castling long": "Gagnez en jouant grand roque",
diff --git a/client/src/translations/rules/Relayup/en.pug b/client/src/translations/rules/Relayup/en.pug
new file mode 100644
index 00000000..a45fc581
--- /dev/null
+++ b/client/src/translations/rules/Relayup/en.pug
@@ -0,0 +1,32 @@
+p.boxed
+  | Pieces inherit the movement of friendly pieces observing them.
+
+p
+  | This variant is based on 
+  a(href="https://www.chessvariants.com/rules/relay-chess") Relay Chess
+  | &nbsp;from chessvariants.com by Johnny Luken (2012).
+
+p.
+  As in Relay Chess, there is no castle or en passant captures, and pawns
+  can only advance one square forward (by themselves).
+
+p However, I made two substantial changes:
+ul
+  li.
+    Being relayed by a friendly piece adds moving options, without canceling
+    the base movements of a piece. Thus the relay is an "upgrade", explaining
+    the chosen name "Relayup".
+  li.
+    Pawns relayed by any friendly piece (except pawns) gain the ability to
+    move and capture like it, but by one step only.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR c3,d4,d3,e3,f4,f3,g3:
+  figcaption.
+    Movements of the e2 pawn, relayed by knight and queen (+ king and bishop).
+
+p.
+  Finally, since stalemate counts as a win and (I believe) no fully blocked
+  positions can be reached, the goal is now to capture the enemy king.
+  You can theoretically go or remain into check.
diff --git a/client/src/translations/rules/Relayup/es.pug b/client/src/translations/rules/Relayup/es.pug
new file mode 100644
index 00000000..7281e622
--- /dev/null
+++ b/client/src/translations/rules/Relayup/es.pug
@@ -0,0 +1,33 @@
+p.boxed
+  | Las piezas heredan los movimientos de las piezas amigas observándolas.
+
+p
+  | Esta variante se basa en 
+  a(href="https://www.chessvariants.com/rules/relay-chess") Relay Chess
+  | &nbsp;de chessvariants.com por Johnny Luken (2012).
+
+p.
+  Como en Relay Chess, no hay enroque ni captura en passant, y los peones
+  solo pueden avanzar una casilla (por sí mismos).
+
+p Sin embargo, hice dos cambios sustanciales:
+ul
+  li.
+    Ser transmitido por una parte amiga agrega capacidades de movimiento, sin
+    cancelar los movimientos básicos de una pieza. Entonces el relé
+    corresponde a un "upgrade", explicando el nombre elegido "Relayup".
+  li.
+    Peones transmitidos por cualquier pieza amiga (excepto los peones)
+    ganar la oportunidad de moverse y capturar como ellos, pero
+    con solo un paso.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR c3,d4,d3,e3,f4,f3,g3:
+  figcaption.
+    Movimientos del peón e2, transmitidos por caballo y dama (+ rey y alfil).
+
+p.
+  Finalmente, dado que el empate cuenta como una victoria y (creo)
+  no hay configuraciones completamente bloqueadas, el objetivo es capturar
+  el rey. Tiene derecho a ir o permanecer en jaque.
diff --git a/client/src/translations/rules/Relayup/fr.pug b/client/src/translations/rules/Relayup/fr.pug
new file mode 100644
index 00000000..d3e21ac2
--- /dev/null
+++ b/client/src/translations/rules/Relayup/fr.pug
@@ -0,0 +1,33 @@
+p.boxed
+  | Les pièces héritent des mouvements des pièces amies les observant.
+
+p
+  | Cette variante est basée sur 
+  a(href="https://www.chessvariants.com/rules/relay-chess") Relay Chess
+  | &nbsp;de chessvariants.com par Johnny Luken (2012).
+
+p.
+  Comme dans Relay Chess, il n'y a ni roque ni prise en passant, et les pions
+  ne peuvent avancer que d'une case vers l'avant (par eux-mêmes).
+
+p Cependant, j'ai effectué deux changements substantiels :
+ul
+  li.
+    Être relayé par une pièce amie ajoute des capacités de déplacement, sans
+    annuler les mouvements de base d'une pièce. Ainsi le relai correspond à
+    un "upgrade", expliquant le nom choisi "Relayup".
+  li.
+    Les pions relayés par n'importe quelle pièce amie (sauf les pions)
+    gagnent la possibilité de se déplacer et capturer comme elles, mais
+    d'un pas seulement.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR c3,d4,d3,e3,f4,f3,g3:
+  figcaption.
+    Déplacements du pion e2, relayé par cavalier et dame (+ roi et fou).
+
+p.
+  Enfin, puisque le pat compte comme une victoire et que (je crois) il
+  n'existe pas de configurations complètement bloquées, le but est de capturer
+  le roi. Vous avez le droit d'aller ou de rester en échec.
diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug
index 0ac43461..53f210e7 100644
--- a/client/src/translations/variants/en.pug
+++ b/client/src/translations/variants/en.pug
@@ -409,6 +409,7 @@ p.
     "Otage",
     "Pacosako",
     "Parachute",
+    "Relayup",
     "Screen",
     "Takenmake",
     "Titan",
diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug
index 761b7398..c1123f7a 100644
--- a/client/src/translations/variants/es.pug
+++ b/client/src/translations/variants/es.pug
@@ -420,6 +420,7 @@ p.
     "Otage",
     "Pacosako",
     "Parachute",
+    "Relayup",
     "Screen",
     "Takenmake",
     "Titan",
diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug
index 232691d6..c0b0ee7c 100644
--- a/client/src/translations/variants/fr.pug
+++ b/client/src/translations/variants/fr.pug
@@ -419,6 +419,7 @@ p.
     "Otage",
     "Pacosako",
     "Parachute",
+    "Relayup",
     "Screen",
     "Takenmake",
     "Titan",
diff --git a/client/src/variants/Pacosako.js b/client/src/variants/Pacosako.js
index 9f0abf88..b67d429b 100644
--- a/client/src/variants/Pacosako.js
+++ b/client/src/variants/Pacosako.js
@@ -666,6 +666,28 @@ export class PacosakoRules extends ChessRules {
     return res;
   }
 
+  isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
+    for (let step of steps) {
+      let rx = x + step[0],
+          ry = y + step[1];
+      while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
+        rx += step[0];
+        ry += step[1];
+      }
+      if (
+        V.OnBoard(rx, ry) &&
+        this.board[rx][ry] != V.EMPTY &&
+        this.getPiece(rx, ry) == piece &&
+        this.getColor(rx, ry) == color &&
+        this.canTake([rx, ry], [x, y]) //TODO: necessary line?
+                                       //If not, generic method is OK
+      ) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   // Do not consider checks, except to forbid castling
   getCheckSquares() {
     return [];
diff --git a/client/src/variants/Relayup.js b/client/src/variants/Relayup.js
index 4b85fde1..446c9273 100644
--- a/client/src/variants/Relayup.js
+++ b/client/src/variants/Relayup.js
@@ -1,6 +1,6 @@
 import { ChessRules } from "@/base_rules";
 
-// Pawns relayed by one square at a time (but with relaying pioece movements)
+// Pawns relayed by one square at a time (but with relaying piece movements)
 // diff from https://www.chessvariants.com/rules/relay-chess ==> new name
 export class RelayupRules extends ChessRules {
 
@@ -22,13 +22,69 @@ export class RelayupRules extends ChessRules {
 
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
+    // Expand potential moves if guarded by friendly pieces.
+    // NOTE: pawns cannot be promoted through a relaying move
+    const piece = this.getPiece(x,y);
+    const color = this.turn;
+    const lastRank = (color == 'w' ? 0 : 7);
+    const sq = [x, y];
+    const oneStep = (piece == V.PAWN);
+    let guardedBy = {};
+    if (piece != V.ROOK && super.isAttackedByRook(sq, color))
+      guardedBy[V.ROOK] = true;
+    if (piece != V.KNIGHT && super.isAttackedByKnight(sq, color))
+      guardedBy[V.KNIGHT] = true;
+    if (piece != V.BISHOP && super.isAttackedByBishop(sq, color))
+      guardedBy[V.BISHOP] = true;
+    if (piece != V.QUEEN && super.isAttackedByQueen(sq, color))
+      guardedBy[V.QUEEN] = true;
+    if (piece != V.KING && super.isAttackedByKing(sq, color))
+      guardedBy[V.KING] = true;
+    Object.keys(guardedBy).forEach(pg => {
+      let steps = null;
+      if ([V.ROOK, V.KNIGHT, V.BISHOP].includes(pg)) steps = V.steps[pg];
+      else steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+      const extraMoves =
+        super.getSlideNJumpMoves(
+          sq, steps, oneStep || [V.KNIGHT, V.KING].includes(pg))
+        .filter(m => {
+          return (
+            (piece != V.PAWN || m.end.x != lastRank) &&
+            (moves.every(mv => mv.end.x != m.end.x || mv.end.y != m.end.y))
+          );
+        });
+      moves = moves.concat(extraMoves);
+    });
+    return moves;
+  }
 
-    // Expand possible moves if guarded by friendly pieces:
-    // --> Pawns cannot be promoted through a relaying move (thus 8th rank forbidden)
-    // TODO
-
+  filterValid(moves) {
     return moves;
   }
+  getCheckSquares() {
+    return [];
+  }
+
+  postPlay(move) {
+    super.postPlay(move);
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      this.kingPos[move.vanish[1].c] = [-1, -1];
+  }
+
+  postUndo(move) {
+    super.postUndo(move);
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING) {
+      const v = move.vanish[1];
+      this.kingPos[v.c] = [v.x, v.y];
+    }
+  }
+
+  getCurrentScore() {
+    const c = this.turn;
+    if (this.kingPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0");
+    // It seems that there is always a possible move (TODO: check this)
+    return "*";
+  }
 
   getNotation(move) {
     // Translate final and initial square
diff --git a/server/db/populate.sql b/server/db/populate.sql
index 56571e56..fc814642 100644
--- a/server/db/populate.sql
+++ b/server/db/populate.sql
@@ -107,6 +107,7 @@ insert or ignore into Variants (name, description) values
   ('Queenpawns', 'Queen versus pawns'),
   ('Racingkings', 'Kings cross the 8x8 board'),
   ('Rampage', 'Move under cover'),
+  ('Relayup', 'Upgrade pieces'),
   ('Rifle', 'Shoot pieces'),
   ('Recycle', 'Reuse pieces'),
   ('Rococo', 'Capture on the edge'),
-- 
2.44.0