From c583ef1c1dfd19aee88b22c2175202fbdf4dc1c0 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 17 Mar 2020 13:39:28 +0100
Subject: [PATCH] Add Antiking v1

---
 TODO                                          |   9 +-
 client/src/base_rules.js                      |   5 +-
 client/src/translations/en.js                 |   3 +-
 client/src/translations/es.js                 |   3 +-
 client/src/translations/fr.js                 |   3 +-
 .../src/translations/rules/Antiking1/en.pug   |  51 ++++
 .../src/translations/rules/Antiking1/es.pug   |  53 ++++
 .../src/translations/rules/Antiking1/fr.pug   |  55 ++++
 .../rules/{Antiking => Antiking2}/en.pug      |  26 +-
 .../rules/{Antiking => Antiking2}/es.pug      |  23 +-
 .../rules/{Antiking => Antiking2}/fr.pug      |  28 +-
 client/src/variants/Antiking1.js              | 270 ++++++++++++++++++
 .../variants/{Antiking.js => Antiking2.js}    |  24 +-
 client/src/variants/Baroque.js                |  12 +-
 client/src/variants/Benedict.js               |  29 --
 client/src/variants/Circular.js               |   8 -
 client/src/variants/Hidden.js                 |  14 +-
 client/src/variants/Royalrace.js              |  13 +-
 client/src/variants/Shatranj.js               |  12 +-
 client/src/variants/Suicide.js                |  13 +-
 client/src/variants/Upsidedown.js             |  11 +-
 server/db/populate.sql                        |   3 +-
 22 files changed, 502 insertions(+), 166 deletions(-)
 create mode 100644 client/src/translations/rules/Antiking1/en.pug
 create mode 100644 client/src/translations/rules/Antiking1/es.pug
 create mode 100644 client/src/translations/rules/Antiking1/fr.pug
 rename client/src/translations/rules/{Antiking => Antiking2}/en.pug (58%)
 rename client/src/translations/rules/{Antiking => Antiking2}/es.pug (61%)
 rename client/src/translations/rules/{Antiking => Antiking2}/fr.pug (52%)
 create mode 100644 client/src/variants/Antiking1.js
 rename client/src/variants/{Antiking.js => Antiking2.js} (91%)

diff --git a/TODO b/TODO
index 6916bdf3..094de496 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,8 @@
+# Improvements
+
+Find a way to generalize getCastleMoves and getPotentialPawnMoves,
+to limit code duplication.
+
 # New variants
 Landing pieces from empty board:
 https://www.chessvariants.com/diffsetup.dir/unachess.html
@@ -6,10 +11,6 @@ Rugby http://www.echecspourtous.com/?page_id=7945
 
 Cannibal Chess with forced captures.
 
-Knightrelay: implement "official" version as Knightrelay v1
-
-Antiking: implement v1 (deterministic) https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html
-
 S-chess https://en.wikipedia.org/wiki/Seirawan_chess
 
 Generator variant, called "Matrix" ?
diff --git a/client/src/base_rules.js b/client/src/base_rules.js
index fad1853a..29b3af60 100644
--- a/client/src/base_rules.js
+++ b/client/src/base_rules.js
@@ -750,12 +750,13 @@ export const ChessRules = class ChessRules {
   // What are the king moves from square x,y ?
   getPotentialKingMoves(sq) {
     // Initialize with normal moves
-    const moves = this.getSlideNJumpMoves(
+    let moves = this.getSlideNJumpMoves(
       sq,
       V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
       "oneStep"
     );
-    return moves.concat(this.getCastleMoves(sq));
+    if (V.HasCastle) moves = moves.concat(this.getCastleMoves(sq));
+    return moves;
   }
 
   getCastleMoves([x, y]) {
diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index 593d68be..4267fa41 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -169,7 +169,8 @@ export const translations = {
   "In the shadow": "In the shadow",
   "Get strong at self-mate": "Get strong at self-mate",
   "Give three checks": "Give three checks",
-  "Keep antiking in check": "Keep antiking in check",
+  "Keep antiking in check (v1)": "Keep antiking in check (v1)",
+  "Keep antiking in check (v2)": "Keep antiking in check (v2)",
   "Kings cross the 8x8 board": "Kings cross the 8x8 board",
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
   "Laws of attraction": "Laws of attraction",
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 84a32951..fe6509e9 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -169,7 +169,8 @@ export const translations = {
   "In the shadow": "En la sombra",
   "Get strong at self-mate": "Progreso en mates asistidos",
   "Give three checks": "Dar tres jaques",
-  "Keep antiking in check": "Mantener el antirey en jaque",
+  "Keep antiking in check (v1)": "Mantener el antirey en jaque (v1)",
+  "Keep antiking in check (v2)": "Mantener el antirey en jaque (v2)",
   "Kings cross the 8x8 board": "Los reyes cruzan el 8x8 tablero",
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
   "Laws of attraction": "Las leyes de las atracciones",
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index 15575cb7..b29ccb7a 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -169,7 +169,8 @@ export const translations = {
   "In the shadow": "Dans l'ombre",
   "Get strong at self-mate": "Progressez en mats aidés",
   "Give three checks": "Donnez trois échecs",
-  "Keep antiking in check": "Gardez l'antiroi en échec",
+  "Keep antiking in check (v1)": "Gardez l'antiroi en échec (v1)",
+  "Keep antiking in check (v2)": "Gardez l'antiroi en échec (v2)",
   "Kings cross the 8x8 board": "Les rois traversent l'échiquier 8x8",
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
   "Laws of attraction": "Les lois de l'attraction",
diff --git a/client/src/translations/rules/Antiking1/en.pug b/client/src/translations/rules/Antiking1/en.pug
new file mode 100644
index 00000000..50f183a5
--- /dev/null
+++ b/client/src/translations/rules/Antiking1/en.pug
@@ -0,0 +1,51 @@
+p.boxed.
+  You have a king and an antiking. King cannot be let in check, but antiking
+  must always stay under check. Antiking captures his own kind.
+
+p.
+  A new piece is introduced: the antiking, noted by the letter 'A'.
+  This piece must always remain under (orthodox) check: it is
+  considered in (anti-)check when not attacked by any enemy piece. In such a
+  situation, the antiking must move immediately to an attacked square.
+
+p.
+  The antiking is a royal figure, and thus cannot be captured.
+  It captures only the pieces of his color.
+  Antikings don't give check, and kings do not attack antikings.
+
+figure.diagram-container
+  .diagram
+    | fen:2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2:
+  figcaption Initial setup: antikings stand in the corners.
+
+h3 Unusual moves
+
+ul
+  li.
+    Pawns are Berolina Pawns, which move diagonally forward one square
+    without capturing, or move one square straight forward to capture.
+  li.
+    If a King or Anti-King has not yet moved, it may make as its first
+    move a Knight's leap to an empty square.
+
+h3 End of the game
+
+p There are three ways to win:
+ol
+  li Checkmate opponent king
+  li Anti-checkmate opponent antiking
+  li Give a double check, as on the following diagram
+
+figure.diagram-container
+  .diagram
+    | fen:2pr3A/2pb1qbr/2p1pkQp/2pnp1n1/1PPN4/PPB1PPN1/1K2RP2/a2BRP2:
+  figcaption After Qb1xg6#
+
+h3 More information
+
+p
+  a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html")
+    | Antiking chess
+  | &nbsp;on chessvariants.com.
+
+p Inventor: Peter Aronson (2002)
diff --git a/client/src/translations/rules/Antiking1/es.pug b/client/src/translations/rules/Antiking1/es.pug
new file mode 100644
index 00000000..894559eb
--- /dev/null
+++ b/client/src/translations/rules/Antiking1/es.pug
@@ -0,0 +1,53 @@
+p.boxed.
+  Tienes un rey y un "antirey". El rey no puede estar en jaque al final de un
+  turno, pero el antirey siempre debe estar en jaque. Este último captura las
+  piezas de su color.
+
+p.
+  Se presenta una nueva pieza: el antirey, denotado por la letra 'A'.
+  Esta pieza debe estar siempre en jaque (ortodoxo): se
+  considera en (anti-)jaque cuando no es atacada por ninguna pieza enemiga.
+  En este caso, el antirey debe unirse inmediatamente a una casilla atacada.
+
+p.
+  El antirey es una pieza real, por lo que no puede ser capturado.
+  Ella captura solo las piezas de su color.
+  Los antireyes no dan jaque, y los reyes no atacan a los antireyes.
+
+figure.diagram-container
+  .diagram
+    | fen:2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2:
+  figcaption Posición inicial: los antireyes están en las esquinas.
+
+h3 Jugadas inusuales
+
+ul
+  li.
+    Los peones son peones Berolina, que se mueven diagonalmente una
+    casilla y captura avanzando verticalmente.
+  li.
+    Si un rey o anti-rey aún no se ha movido, puede realizar
+    un salto de caballo a una casilla vacía.
+
+h3 Fin de la partida
+
+p Hay tres maneras de ganar:
+ol
+  li Mater el rey contrario
+  li "Anti-mater" el antirey contrario
+  li Dar un doble jaque, como en el siguiente diagrama
+
+figure.diagram-container
+  .diagram
+    | fen:2pr3A/2pb1qbr/2p1pkQp/2pnp1n1/1PPN4/PPB1PPN1/1K2RP2/a2BRP2:
+  figcaption Después de Qb1xg6#
+
+h3 Más información
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html")
+    | variante Antiking
+  | &nbsp;en chessvariants.com.
+
+p Inventor: Peter Aronson (2002)
diff --git a/client/src/translations/rules/Antiking1/fr.pug b/client/src/translations/rules/Antiking1/fr.pug
new file mode 100644
index 00000000..c8da8ce1
--- /dev/null
+++ b/client/src/translations/rules/Antiking1/fr.pug
@@ -0,0 +1,55 @@
+p.boxed.
+  Vous disposez d'un roi et d'un "antiroi". Le roi ne peut pas se retrouver
+  en échec à la fin d'un tour, mais l'antiroi doit lui toujours être en échec.
+  Ce dernier capture les pièces de sa couleur.
+
+p.
+  Une nouvelle pièce fait son apparition : l'antiroi, noté par la lettre 'A'.
+  Cette pièce doit toujour rester en échec (orthodoxe) :
+  elle est considérée en (anti-)échec lorsqu'elle n'est attaquée
+  par aucune pièce ennemie. Dans ce cas, l'antiroi doit immédiatement
+  rejoindre une case attaquée.
+
+p.
+  L'antiroi est une pièce royale, elle ne peut donc pas être capturée.
+  Elle capture seulement les pièces de sa couleur.
+  Les antirois ne donnent pas échec, et les rois n'attaquent pas les antirois.
+
+
+figure.diagram-container
+  .diagram
+    | fen:2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2:
+  figcaption Position initiale : les antirois sont dans les coins.
+
+h3 Coups inhabituels
+
+ul
+  li.
+    Les pions sont des pions Berolina, qui se déplacent en diagonale d'une
+    case et capturent en avançant verticalement.
+  li.
+    Si un roi ou antiroi n'a pas encore bougé, il peut effectuer
+    un saut de cavalier vers une case vide.
+
+h3 Fin de partie
+
+p Il y a trois façons de gagner :
+ol
+  li Mater le roi adverse
+  li "Anti-mater" l'anti-roi adverse
+  li Donner un échec double, comme sur le diagramme suivant
+
+figure.diagram-container
+  .diagram
+    | fen:2pr3A/2pb1qbr/2p1pkQp/2pnp1n1/1PPN4/PPB1PPN1/1K2RP2/a2BRP2:
+  figcaption Après Qb1xg6#
+
+h3 Plus d'information
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html")
+    | variante Antiking
+  | &nbsp;sur chessvariants.com.
+
+p Inventeur : Peter Aronson (2002)
diff --git a/client/src/translations/rules/Antiking/en.pug b/client/src/translations/rules/Antiking2/en.pug
similarity index 58%
rename from client/src/translations/rules/Antiking/en.pug
rename to client/src/translations/rules/Antiking2/en.pug
index 0c10c012..b7bc4320 100644
--- a/client/src/translations/rules/Antiking/en.pug
+++ b/client/src/translations/rules/Antiking2/en.pug
@@ -3,38 +3,28 @@ p.boxed.
   must always stay under check. Antiking captures his own kind.
 
 p.
-  A new piece is introduced: the antiking, noted by the letter 'A' in diagrams
-  and PGNs. This piece must always remain under (orthodox) check: it is
+  A new piece is introduced: the antiking, noted by the letter 'A'.
+  This piece must always remain under (orthodox) check: it is
   considered in (anti-)check when not attacked by any enemy piece. In such a
   situation, the antiking must move immediately to an attacked square.
 
 p.
   The antiking is a royal figure, and thus cannot be captured.
-  It captures only the pieces of his color (to help checkmating the opponent's
-  antiking, but this also complicates standard checkmate).
+  It captures only the pieces of his color.
+  Antikings don't give check, and kings do not attack antikings.
 
 figure.diagram-container
   .diagram
     | fen:rnbqkbnr/pppppppp/3A4/8/8/3a4/PPPPPPPP/RNBQKBNR c5,d5,e5:
   figcaption Marked squares are not allowed antiking moves.
 
-ul
-  li.
-    Although antiking captures his color, it doesn't check his king - it
-    doesn't check the opponent's king either.
-  li.
-    Since it would allow a basic tactic (keep antiking touching opponent's
-    king), kings do not attack antikings.
-
 h3 End of the game
 
-p There are two ways to win:
+p There are three ways to win:
 ol
-  li Checkmate opponent king
-  li Anti-checkmate opponent antiking
-p.
-  ...Or do both at the same time, as on the following diagram after 3.Qxf7#
-  (the black antiking was on g3).
+  li Checkmate the opponent king
+  li "Anti-checkmate" the opponent antiking
+  li Give a double check, as on the following diagram
 
 figure.diagram-container
   .diagram
diff --git a/client/src/translations/rules/Antiking/es.pug b/client/src/translations/rules/Antiking2/es.pug
similarity index 61%
rename from client/src/translations/rules/Antiking/es.pug
rename to client/src/translations/rules/Antiking2/es.pug
index 647eb9f6..c2d9dc34 100644
--- a/client/src/translations/rules/Antiking/es.pug
+++ b/client/src/translations/rules/Antiking2/es.pug
@@ -4,39 +4,28 @@ p.boxed.
   piezas de su color.
 
 p.
-  Se presenta una nueva pieza: el antirey, denotado por la letra 'A' en los
-  diagramas y PGNs. Esta pieza debe estar siempre en jaque (ortodoxo): se
+  Se presenta una nueva pieza: el antirey, denotado por la letra 'A'.
+  Esta pieza debe estar siempre en jaque (ortodoxo): se
   considera en (anti-)jaque cuando no es atacada por ninguna pieza enemiga.
   En este caso, el antirey debe unirse inmediatamente a una casilla atacada.
 
 p.
   El antirey es una pieza real, por lo que no puede ser capturado.
-  Ella captura solo las piezas de su color (esto ayuda a mate el anti-rey del
-  oponente, pero complica el mate ortodoxo).
+  Ella captura solo las piezas de su color.
+  Los antireyes no dan jaque, y los reyes no atacan a los antireyes.
 
 figure.diagram-container
   .diagram
     | fen:rnbqkbnr/pppppppp/3A4/8/8/3a4/PPPPPPPP/RNBQKBNR c5,d5,e5:
   figcaption Las casillas mostradas no son legales jugadas de antirey.
 
-ul
-  li.
-    Aunque el antirey captura las piezas de su color, no da jaque a su rey,
-    ni tampoco al rey contrario.
-  li.
-    Los reyes no atacan a los antireyes (de color opuesto). De hecho, esto
-    permitiría aplicar un método simple para nunca estar en "anti-jaque":
-    permanecer pegado al rey contrario.
-
 h3 Fin de la partida
 
-p Hay dos maneras de ganar:
+p Hay tres maneras de ganar:
 ol
   li Mater el rey contrario
   li "Anti-mater" el antirey contrario
-p.
-  ...O haga ambas cosas al mismo tiempo, como se muestra en el siguiente
-  diagrama después de 3.Qxf7# (el antirey negro estaba en g3).
+  li Dar un doble jaque, como en el siguiente diagrama
 
 figure.diagram-container
   .diagram
diff --git a/client/src/translations/rules/Antiking/fr.pug b/client/src/translations/rules/Antiking2/fr.pug
similarity index 52%
rename from client/src/translations/rules/Antiking/fr.pug
rename to client/src/translations/rules/Antiking2/fr.pug
index 7c058b2d..d3be1b1c 100644
--- a/client/src/translations/rules/Antiking/fr.pug
+++ b/client/src/translations/rules/Antiking2/fr.pug
@@ -4,40 +4,28 @@ p.boxed.
   Ce dernier capture les pièces de sa couleur.
 
 p.
-  Une nouvelle pièce fait son apparition : l'antiroi, noté par la lettre 'A'
-  sur les diagrammes et PGNs. Cette pièce doit toujour rester en échec
-  (orthodoxe) : elle est considérée en (anti-)échec lorsqu'elle n'est attaquée
-  par aucune pièce ennemie. Dans ce cas, l'antiroi doit immédiatement
-  rejoindre une case attaquée.
+  Une nouvelle pièce fait son apparition : l'antiroi, noté par la lettre 'A'.
+  Cette pièce doit toujour rester en échec (orthodoxe) : elle est considérée
+  en (anti-)échec lorsqu'elle n'est attaquée par aucune pièce ennemie.
+  Dans ce cas, l'antiroi doit immédiatement rejoindre une case attaquée.
 
 p.
   L'antiroi est une pièce royale, elle ne peut donc pas être capturée.
-  Elle capture seulement les pièces de sa couleur (cela aide à mater
-  l'anti-roi adverse, mais complique le mat orthodoxe).
+  Elle capture seulement les pièces de sa couleur.
+  Les antirois ne donnent pas échec, et les rois n'attaquent pas les antirois.
 
 figure.diagram-container
   .diagram
     | fen:rnbqkbnr/pppppppp/3A4/8/8/3a4/PPPPPPPP/RNBQKBNR c5,d5,e5:
   figcaption Les cases indiquées ne sont pas des coups d'anti-roi légaux.
 
-ul
-  li.
-    Bien que l'antiroi capture les pièces de son camp, il ne met pas
-    en échec son roi - ni d'ailleurs le roi adverse.
-  li.
-    Les rois n'attaquent pas les antirois (de couleur opposée).
-    En effet ceci permettrait d'appliquer une méthode simple pour ne jamais
-    être en "anti-échec" : rester collé au roi adverse.
-
 h3 Fin de partie
 
-p Il y a deux façons de gagner :
+p Il y a trois façons de gagner :
 ol
   li Mater le roi adverse
   li "Anti-mater" l'anti-roi adverse
-p.
-  ...Ou faire les deux en même temps, comme sur le diagramme suivant après
-  3.Qxf7# (l'antiroi noir était en g3).
+  li Donner un échec double, comme sur le diagramme suivant
 
 figure.diagram-container
   .diagram
diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js
new file mode 100644
index 00000000..42450d17
--- /dev/null
+++ b/client/src/variants/Antiking1.js
@@ -0,0 +1,270 @@
+import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
+export const VariantRules = class Antiking1Rules extends ChessRules {
+  static get HasEnpassant() {
+    return false;
+  }
+
+  static get HasCastle() {
+    return false;
+  }
+
+  static get ANTIKING() {
+    return "a";
+  }
+
+  static get PIECES() {
+    return ChessRules.PIECES.concat([V.ANTIKING]);
+  }
+
+  getPpath(b) {
+    return b[1] == "a" ? "Antiking/" + b : b;
+  }
+
+  setOtherVariables(fen) {
+    super.setOtherVariables(fen);
+    this.antikingPos = { w: [-1, -1], b: [-1, -1] };
+    const rows = V.ParseFen(fen).position.split("/");
+    for (let i = 0; i < rows.length; i++) {
+      let k = 0;
+      for (let j = 0; j < rows[i].length; j++) {
+        switch (rows[i].charAt(j)) {
+          case "a":
+            this.antikingPos["b"] = [i, k];
+            break;
+          case "A":
+            this.antikingPos["w"] = [i, k];
+            break;
+          default: {
+            const num = parseInt(rows[i].charAt(j));
+            if (!isNaN(num)) k += num - 1;
+          }
+        }
+        k++;
+      }
+    }
+  }
+
+  // (Anti)King flags at 1 (true) if they can knight-jump
+  setFlags(fenflags) {
+    this.kingFlags = {
+      // King then antiking
+      w: [...Array(2).fill(false)],
+      b: [...Array(2).fill(false)]
+    };
+    for (let c of ["w", "b"]) {
+      for (let i = 0; i < 2; i++)
+        this.kingFlags[c][i] = fenflags.charAt((c == "w" ? 0 : 2) + i) == "1";
+    }
+  }
+
+  aggregateFlags() {
+    return this.kingFlags;
+  }
+
+  disaggregateFlags(flags) {
+    this.kingFlags = flags;
+  }
+
+  getFlagsFen() {
+    // Return kings flags
+    let flags = "";
+    for (let c of ["w", "b"]) {
+      for (let i = 0; i < 2; i++) flags += this.kingFlags[c][i] ? "1" : "0";
+    }
+    return flags;
+  }
+
+  canTake([x1, y1], [x2, y2]) {
+    const piece1 = this.getPiece(x1, y1);
+    const piece2 = this.getPiece(x2, y2);
+    const color1 = this.getColor(x1, y1);
+    const color2 = this.getColor(x2, y2);
+    return (
+      piece2 != "a" &&
+      ((piece1 != "a" && color1 != color2) ||
+        (piece1 == "a" && color1 == color2))
+    );
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    let moves = [];
+    let addKnightJumps = false;
+    const piece = this.getPiece(x, y);
+    const color = this.getColor(x, y);
+    if (piece == V.ANTIKING) {
+      moves = this.getPotentialAntikingMoves([x, y]);
+      addKnightJumps = this.kingFlags[color][1];
+    } else {
+      moves = super.getPotentialMovesFrom([x, y]);
+      if (piece == V.KING) addKnightJumps = this.kingFlags[color][0];
+    }
+    if (addKnightJumps) {
+      // Add potential knight jump to (anti)kings
+      const knightJumps = super.getPotentialKnightMoves([x, y]);
+      // Remove captures (TODO: could be done more efficiently...)
+      moves = moves.concat(knightJumps.filter(m => m.vanish.length == 1));
+    }
+    return moves;
+  }
+
+  getPotentialPawnMoves([x, y]) {
+    const color = this.turn;
+    let moves = [];
+    const [sizeX, sizeY] = [V.size.x, V.size.y];
+    const shiftX = color == "w" ? -1 : 1;
+    const startRank = color == "w" ? sizeX - 2 : 1;
+    const lastRank = color == "w" ? 0 : sizeX - 1;
+    const finalPieces =
+      x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
+
+    // One square diagonally
+    for (let shiftY of [-1, 1]) {
+      if (this.board[x + shiftX][y + shiftY] == V.EMPTY) {
+        for (let piece of finalPieces) {
+          moves.push(
+            this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+              c: color,
+              p: piece
+            })
+          );
+        }
+      }
+    }
+    // Capture
+    if (
+      this.board[x + shiftX][y] != V.EMPTY &&
+      this.canTake([x, y], [x + shiftX, y])
+    ) {
+      for (let piece of finalPieces)
+        moves.push(
+          this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+        );
+    }
+
+    return moves;
+  }
+
+  getPotentialAntikingMoves(sq) {
+    // The antiking moves like a king (only captured colors differ)
+    return this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
+  }
+
+  isAttacked(sq, color) {
+    return (
+      super.isAttacked(sq, color) ||
+      this.isAttackedByAntiking(sq, color)
+    );
+  }
+
+  isAttackedByPawn([x, y], color) {
+    let pawnShift = (color == "w" ? 1 : -1);
+    if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+      if (
+        this.getPiece(x + pawnShift, y) == V.PAWN &&
+        this.getColor(x + pawnShift, y) == color
+      ) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  isAttackedByKing([x, y], color) {
+    // Antiking is not attacked by king:
+    if (this.getPiece(x, y) == V.ANTIKING) return false;
+    return this.isAttackedBySlideNJump(
+      [x, y],
+      color,
+      V.KING,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
+  }
+
+  isAttackedByAntiking([x, y], color) {
+    // (Anti)King is not attacked by antiking
+    if ([V.KING, V.ANTIKING].includes(this.getPiece(x, y))) return false;
+    return this.isAttackedBySlideNJump(
+      [x, y],
+      color,
+      V.ANTIKING,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
+  }
+
+  underCheck(color) {
+    const oppCol = V.GetOppCol(color);
+    let res =
+      this.isAttacked(this.kingPos[color], oppCol) ||
+      !this.isAttacked(this.antikingPos[color], oppCol);
+    return res;
+  }
+
+  getCheckSquares(color) {
+    let res = [];
+    const oppCol = V.GetOppCol(color);
+    if (this.isAttacked(this.kingPos[color], oppCol))
+      res.push(JSON.parse(JSON.stringify(this.kingPos[color])));
+    if (!this.isAttacked(this.antikingPos[color], oppCol))
+      res.push(JSON.parse(JSON.stringify(this.antikingPos[color])));
+    return res;
+  }
+
+  postPlay(move) {
+    super.postPlay(move);
+    const piece = move.vanish[0].p;
+    const c = move.vanish[0].c;
+    // Update antiking position, and kings flags
+    if (piece == V.ANTIKING) {
+      this.antikingPos[c][0] = move.appear[0].x;
+      this.antikingPos[c][1] = move.appear[0].y;
+      this.kingFlags[c][1] = false;
+    } else if (piece == V.KING) this.kingFlags[c][0] = false;
+  }
+
+  postUndo(move) {
+    super.postUndo(move);
+    const c = move.vanish[0].c;
+    if (move.vanish[0].p == V.ANTIKING)
+      this.antikingPos[c] = [move.start.x, move.start.y];
+  }
+
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      return "*";
+
+    const color = this.turn;
+    const oppCol = V.GetOppCol(color);
+    if (
+      !this.isAttacked(this.kingPos[color], oppCol) &&
+      this.isAttacked(this.antikingPos[color], oppCol)
+    ) {
+      return "1/2";
+    }
+    return color == "w" ? "0-1" : "1-0";
+  }
+
+  static get VALUES() {
+    return Object.assign(
+      { a: 1000 },
+      ChessRules.VALUES
+    );
+  }
+
+  static GenRandInitFen() {
+    // Always deterministic setup
+    return "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0 1111";
+  }
+
+  static get SEARCH_DEPTH() {
+    return 2;
+  }
+};
diff --git a/client/src/variants/Antiking.js b/client/src/variants/Antiking2.js
similarity index 91%
rename from client/src/variants/Antiking.js
rename to client/src/variants/Antiking2.js
index 94c8af6d..3be41af5 100644
--- a/client/src/variants/Antiking.js
+++ b/client/src/variants/Antiking2.js
@@ -2,7 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
-export const VariantRules = class AntikingRules extends ChessRules {
+export const VariantRules = class Antiking2Rules extends ChessRules {
   static get ANTIKING() {
     return "a";
   }
@@ -46,8 +46,10 @@ export const VariantRules = class AntikingRules extends ChessRules {
     const color2 = this.getColor(x2, y2);
     return (
       piece2 != "a" &&
-      ((piece1 != "a" && color1 != color2) ||
-        (piece1 == "a" && color1 == color2))
+      (
+        (piece1 != "a" && color1 != color2) ||
+        (piece1 == "a" && color1 == color2)
+      )
     );
   }
 
@@ -61,6 +63,7 @@ export const VariantRules = class AntikingRules extends ChessRules {
   }
 
   getPotentialAntikingMoves(sq) {
+    // The antiking moves like a king (only captured colors differ)
     return this.getSlideNJumpMoves(
       sq,
       V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
@@ -78,13 +81,7 @@ export const VariantRules = class AntikingRules extends ChessRules {
   isAttackedByKing([x, y], color) {
     // Antiking is not attacked by king:
     if (this.getPiece(x, y) == V.ANTIKING) return false;
-    return this.isAttackedBySlideNJump(
-      [x, y],
-      color,
-      V.KING,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
+    return super.isAttackedByKing([x, y], color);
   }
 
   isAttackedByAntiking([x, y], color) {
@@ -108,8 +105,11 @@ export const VariantRules = class AntikingRules extends ChessRules {
   }
 
   getCheckSquares(color) {
-    let res = super.getCheckSquares(color);
-    if (!this.isAttacked(this.antikingPos[color], V.GetOppCol(color)))
+    let res = [];
+    const oppCol = V.GetOppCol(color);
+    if (this.isAttacked(this.kingPos[color], oppCol))
+      res.push(JSON.parse(JSON.stringify(this.kingPos[color])));
+    if (!this.isAttacked(this.antikingPos[color], oppCol))
       res.push(JSON.parse(JSON.stringify(this.antikingPos[color])));
     return res;
   }
diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js
index c7c100c6..a19d09e5 100644
--- a/client/src/variants/Baroque.js
+++ b/client/src/variants/Baroque.js
@@ -7,6 +7,10 @@ export const VariantRules = class BaroqueRules extends ChessRules {
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     return false;
   }
@@ -352,14 +356,6 @@ export const VariantRules = class BaroqueRules extends ChessRules {
     return super.getPotentialQueenMoves(sq);
   }
 
-  getPotentialKingMoves(sq) {
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
   // isAttacked() is OK because the immobilizer doesn't take
 
   isAttackedByPawn([x, y], color) {
diff --git a/client/src/variants/Benedict.js b/client/src/variants/Benedict.js
index 73a55088..f0d4f2ba 100644
--- a/client/src/variants/Benedict.js
+++ b/client/src/variants/Benedict.js
@@ -103,35 +103,6 @@ export const VariantRules = class BenedictRules extends ChessRules {
     return moves;
   }
 
-  getPotentialRookMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
-  }
-
-  getPotentialKnightMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
-  }
-
-  getPotentialBishopMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
-  }
-
-  getPotentialQueenMoves(sq) {
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP])
-    );
-  }
-
-  getPotentialKingMoves(sq) {
-    // Initialize with normal (non-capturing) moves
-    let noCaptures = this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-    return noCaptures.concat(this.getCastleMoves(sq));
-  }
-
   // No "under check" verifications:
   getCastleMoves([x, y]) {
     const c = this.getColor(x, y);
diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js
index 5468dc54..61997f29 100644
--- a/client/src/variants/Circular.js
+++ b/client/src/variants/Circular.js
@@ -158,14 +158,6 @@ export const VariantRules = class CircularRules extends ChessRules {
     return moves;
   }
 
-  getPotentialKingMoves(sq) {
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
   filterValid(moves) {
     const filteredMoves = super.filterValid(moves);
     // If at least one full move made, everything is allowed:
diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js
index 79c3dfbd..8a5858ea 100644
--- a/client/src/variants/Hidden.js
+++ b/client/src/variants/Hidden.js
@@ -7,6 +7,10 @@ export const VariantRules = class HiddenRules extends ChessRules {
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     return false;
   }
@@ -143,16 +147,6 @@ export const VariantRules = class HiddenRules extends ChessRules {
     return mv;
   }
 
-  // What are the king moves from square x,y ?
-  getPotentialKingMoves(sq) {
-    // No castling:
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
   filterValid(moves) {
     return moves;
   }
diff --git a/client/src/variants/Royalrace.js b/client/src/variants/Royalrace.js
index 346ca5f0..c9095558 100644
--- a/client/src/variants/Royalrace.js
+++ b/client/src/variants/Royalrace.js
@@ -7,6 +7,10 @@ export const VariantRules = class RoyalraceRules extends ChessRules {
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     return false;
   }
@@ -133,15 +137,6 @@ export const VariantRules = class RoyalraceRules extends ChessRules {
     return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT]);
   }
 
-  // What are the king moves from square x,y ?
-  getPotentialKingMoves(sq) {
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
   filterValid(moves) {
     if (moves.length == 0) return [];
     const color = this.turn;
diff --git a/client/src/variants/Shatranj.js b/client/src/variants/Shatranj.js
index 969b8197..45a34c04 100644
--- a/client/src/variants/Shatranj.js
+++ b/client/src/variants/Shatranj.js
@@ -5,6 +5,10 @@ export const VariantRules = class ShatranjRules extends ChessRules {
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     return false;
   }
@@ -90,14 +94,6 @@ export const VariantRules = class ShatranjRules extends ChessRules {
     );
   }
 
-  getPotentialKingMoves(sq) {
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
   isAttackedByBishop(sq, color) {
     return this.isAttackedBySlideNJump(
       sq,
diff --git a/client/src/variants/Suicide.js b/client/src/variants/Suicide.js
index 24e8ef29..4bb5d36b 100644
--- a/client/src/variants/Suicide.js
+++ b/client/src/variants/Suicide.js
@@ -7,6 +7,10 @@ export const VariantRules = class SuicideRules extends ChessRules {
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   getPotentialPawnMoves([x, y]) {
     let moves = super.getPotentialPawnMoves([x, y]);
 
@@ -44,15 +48,6 @@ export const VariantRules = class SuicideRules extends ChessRules {
     return moves;
   }
 
-  getPotentialKingMoves(sq) {
-    // No castle:
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
   // Trim all non-capturing moves (not the most efficient, but easy)
   static KeepCaptures(moves) {
     return moves.filter(m => m.vanish.length == 2);
diff --git a/client/src/variants/Upsidedown.js b/client/src/variants/Upsidedown.js
index 10bb83dd..e8242004 100644
--- a/client/src/variants/Upsidedown.js
+++ b/client/src/variants/Upsidedown.js
@@ -7,17 +7,12 @@ export const VariantRules = class UpsidedownRules extends ChessRules {
     return false;
   }
 
-  static get HasEnpassant() {
+  static get HasCastle() {
     return false;
   }
 
-  getPotentialKingMoves(sq) {
-    // No castle
-    return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
+  static get HasEnpassant() {
+    return false;
   }
 
   static GenRandInitFen(randomness) {
diff --git a/server/db/populate.sql b/server/db/populate.sql
index 72dff2ec..c37709b7 100644
--- a/server/db/populate.sql
+++ b/server/db/populate.sql
@@ -4,7 +4,8 @@ insert or ignore into Variants (name,description) values
   ('Alice', 'Both sides of the mirror'),
   ('Allmate1', 'Mate any piece (v1)'),
   ('Allmate2', 'Mate any piece (v2)'),
-  ('Antiking', 'Keep antiking in check'),
+  ('Antiking1', 'Keep antiking in check (v1)'),
+  ('Antiking2', 'Keep antiking in check (v2)'),
   ('Antimatter', 'Dangerous collisions'),
   ('Arena', 'Middle battle'),
   ('Atomic', 'Explosive captures'),
-- 
2.44.0