Add Antiking v1
authorBenjamin Auder <benjamin.auder@somewhere>
Tue, 17 Mar 2020 12:39:28 +0000 (13:39 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Tue, 17 Mar 2020 12:39:28 +0000 (13:39 +0100)
22 files changed:
TODO
client/src/base_rules.js
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Antiking1/en.pug [new file with mode: 0644]
client/src/translations/rules/Antiking1/es.pug [new file with mode: 0644]
client/src/translations/rules/Antiking1/fr.pug [new file with mode: 0644]
client/src/translations/rules/Antiking2/en.pug [moved from client/src/translations/rules/Antiking/en.pug with 58% similarity]
client/src/translations/rules/Antiking2/es.pug [moved from client/src/translations/rules/Antiking/es.pug with 61% similarity]
client/src/translations/rules/Antiking2/fr.pug [moved from client/src/translations/rules/Antiking/fr.pug with 52% similarity]
client/src/variants/Antiking1.js [new file with mode: 0644]
client/src/variants/Antiking2.js [moved from client/src/variants/Antiking.js with 91% similarity]
client/src/variants/Baroque.js
client/src/variants/Benedict.js
client/src/variants/Circular.js
client/src/variants/Hidden.js
client/src/variants/Royalrace.js
client/src/variants/Shatranj.js
client/src/variants/Suicide.js
client/src/variants/Upsidedown.js
server/db/populate.sql

diff --git a/TODO b/TODO
index 6916bdf..094de49 100644 (file)
--- 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
 # 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.
 
 
 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" ?
 S-chess https://en.wikipedia.org/wiki/Seirawan_chess
 
 Generator variant, called "Matrix" ?
index fad1853..29b3af6 100644 (file)
@@ -750,12 +750,13 @@ export const ChessRules = class ChessRules {
   // What are the king moves from square x,y ?
   getPotentialKingMoves(sq) {
     // Initialize with normal moves
   // 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"
     );
       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]) {
   }
 
   getCastleMoves([x, y]) {
index 593d68b..4267fa4 100644 (file)
@@ -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",
   "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",
   "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",
index 84a3295..fe6509e 100644 (file)
@@ -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",
   "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",
   "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",
index 15575cb..b29ccb7 100644 (file)
@@ -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",
   "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",
   "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 (file)
index 0000000..50f183a
--- /dev/null
@@ -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 (file)
index 0000000..894559e
--- /dev/null
@@ -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 (file)
index 0000000..c8da8ce
--- /dev/null
@@ -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)
@@ -3,38 +3,28 @@ p.boxed.
   must always stay under check. Antiking captures his own kind.
 
 p.
   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.
   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.
 
 
 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
 
 h3 End of the game
 
-p There are two ways to win:
+p There are three ways to win:
 ol
 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
 
 figure.diagram-container
   .diagram
@@ -4,39 +4,28 @@ p.boxed.
   piezas de su color.
 
 p.
   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.
   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.
 
 
 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
 
 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
 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
 
 figure.diagram-container
   .diagram
@@ -4,40 +4,28 @@ p.boxed.
   Ce dernier capture les pièces de sa couleur.
 
 p.
   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.
 
 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.
 
 
 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
 
 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
 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
 
 figure.diagram-container
   .diagram
diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js
new file mode 100644 (file)
index 0000000..42450d1
--- /dev/null
@@ -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;
+  }
+};
similarity index 91%
rename from client/src/variants/Antiking.js
rename to client/src/variants/Antiking2.js
index 94c8af6..3be41af 100644 (file)
@@ -2,7 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 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";
   }
   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" &&
     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) {
   }
 
   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]),
     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;
   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) {
   }
 
   isAttackedByAntiking([x, y], color) {
@@ -108,8 +105,11 @@ export const VariantRules = class AntikingRules extends ChessRules {
   }
 
   getCheckSquares(color) {
   }
 
   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;
   }
       res.push(JSON.parse(JSON.stringify(this.antikingPos[color])));
     return res;
   }
index c7c100c..a19d09e 100644 (file)
@@ -7,6 +7,10 @@ export const VariantRules = class BaroqueRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
@@ -352,14 +356,6 @@ export const VariantRules = class BaroqueRules extends ChessRules {
     return super.getPotentialQueenMoves(sq);
   }
 
     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) {
   // isAttacked() is OK because the immobilizer doesn't take
 
   isAttackedByPawn([x, y], color) {
index 73a5508..f0d4f2b 100644 (file)
@@ -103,35 +103,6 @@ export const VariantRules = class BenedictRules extends ChessRules {
     return moves;
   }
 
     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);
   // No "under check" verifications:
   getCastleMoves([x, y]) {
     const c = this.getColor(x, y);
index 5468dc5..61997f2 100644 (file)
@@ -158,14 +158,6 @@ export const VariantRules = class CircularRules extends ChessRules {
     return moves;
   }
 
     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:
   filterValid(moves) {
     const filteredMoves = super.filterValid(moves);
     // If at least one full move made, everything is allowed:
index 79c3dfb..8a5858e 100644 (file)
@@ -7,6 +7,10 @@ export const VariantRules = class HiddenRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
@@ -143,16 +147,6 @@ export const VariantRules = class HiddenRules extends ChessRules {
     return mv;
   }
 
     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;
   }
   filterValid(moves) {
     return moves;
   }
index 346ca5f..c909555 100644 (file)
@@ -7,6 +7,10 @@ export const VariantRules = class RoyalraceRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     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]);
   }
 
     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;
   filterValid(moves) {
     if (moves.length == 0) return [];
     const color = this.turn;
index 969b819..45a34c0 100644 (file)
@@ -5,6 +5,10 @@ export const VariantRules = class ShatranjRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   static get HasEnpassant() {
     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,
   isAttackedByBishop(sq, color) {
     return this.isAttackedBySlideNJump(
       sq,
index 24e8ef2..4bb5d36 100644 (file)
@@ -7,6 +7,10 @@ export const VariantRules = class SuicideRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  static get HasCastle() {
+    return false;
+  }
+
   getPotentialPawnMoves([x, y]) {
     let moves = super.getPotentialPawnMoves([x, y]);
 
   getPotentialPawnMoves([x, y]) {
     let moves = super.getPotentialPawnMoves([x, y]);
 
@@ -44,15 +48,6 @@ export const VariantRules = class SuicideRules extends ChessRules {
     return moves;
   }
 
     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);
   // Trim all non-capturing moves (not the most efficient, but easy)
   static KeepCaptures(moves) {
     return moves.filter(m => m.vanish.length == 2);
index 10bb83d..e824200 100644 (file)
@@ -7,17 +7,12 @@ export const VariantRules = class UpsidedownRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasEnpassant() {
+  static get HasCastle() {
     return false;
   }
 
     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) {
   }
 
   static GenRandInitFen(randomness) {
index 72dff2e..c37709b 100644 (file)
@@ -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)'),
   ('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'),
   ('Antimatter', 'Dangerous collisions'),
   ('Arena', 'Middle battle'),
   ('Atomic', 'Explosive captures'),