Draft Fullcavalry and Atomic2 (pawn removal). Fix Grand
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 3 Dec 2020 22:27:25 +0000 (23:27 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 3 Dec 2020 22:27:25 +0000 (23:27 +0100)
27 files changed:
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Atomic1/en.pug [moved from client/src/translations/rules/Atomic/en.pug with 100% similarity]
client/src/translations/rules/Atomic1/es.pug [moved from client/src/translations/rules/Atomic/es.pug with 100% similarity]
client/src/translations/rules/Atomic1/fr.pug [moved from client/src/translations/rules/Atomic/fr.pug with 100% similarity]
client/src/translations/rules/Atomic2/en.pug [new file with mode: 0644]
client/src/translations/rules/Atomic2/es.pug [new file with mode: 0644]
client/src/translations/rules/Atomic2/fr.pug [new file with mode: 0644]
client/src/translations/rules/Fullcavalry/en.pug [new file with mode: 0644]
client/src/translations/rules/Fullcavalry/es.pug [new file with mode: 0644]
client/src/translations/rules/Fullcavalry/fr.pug [new file with mode: 0644]
client/src/translations/rules/Grand/en.pug
client/src/translations/rules/Grand/es.pug
client/src/translations/rules/Grand/fr.pug
client/src/translations/variants/en.pug
client/src/translations/variants/es.pug
client/src/translations/variants/fr.pug
client/src/variants/Atomic1.js [moved from client/src/variants/Atomic.js with 92% similarity]
client/src/variants/Atomic2.js [new file with mode: 0644]
client/src/variants/Eightpieces.js
client/src/variants/Fullcavalry.js [new file with mode: 0644]
client/src/variants/Grand.js
client/src/variants/Losers.js
client/src/variants/Titan.js
client/src/views/Analyse.vue
server/db/populate.sql

index 5a70547..3cde6ed 100644 (file)
@@ -195,7 +195,8 @@ export const translations = {
   "Enter the disco": "Enter the disco",
   "Exchange pieces' positions": "Exchange pieces' positions",
   "Exotic captures": "Exotic captures",
   "Enter the disco": "Enter the disco",
   "Exchange pieces' positions": "Exchange pieces' positions",
   "Exotic captures": "Exotic captures",
-  "Explosive captures": "Explosive captures",
+  "Explosive captures (v1)": "Explosive captures (v1)",
+  "Explosive captures (v2)": "Explosive captures (v2)",
   "Extra bishops and knights": "Extra bishops and knights",
   "Faster development": "Faster development",
   "Four new pieces": "Four new pieces",
   "Extra bishops and knights": "Extra bishops and knights",
   "Faster development": "Faster development",
   "Four new pieces": "Four new pieces",
@@ -213,6 +214,7 @@ export const translations = {
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
   "Knight in pocket": "Knight in pocket",
   "Knight versus pawns": "Knight versus pawns",
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
   "Knight in pocket": "Knight in pocket",
   "Knight versus pawns": "Knight versus pawns",
+  "Lancers everywhere": "Lancers everywhere",
   "Landing on the board": "Landing on the board",
   "Laws of attraction": "Laws of attraction",
   "Long jumps over pieces": "Long jumps over pieces",
   "Landing on the board": "Landing on the board",
   "Laws of attraction": "Laws of attraction",
   "Long jumps over pieces": "Long jumps over pieces",
index ea64d3f..612b0e3 100644 (file)
@@ -195,7 +195,8 @@ export const translations = {
   "Enter the disco": "Entrar en la discoteca",
   "Exchange pieces' positions": "Intercambiar posiciones de piezas",
   "Exotic captures": "Capturas exóticas",
   "Enter the disco": "Entrar en la discoteca",
   "Exchange pieces' positions": "Intercambiar posiciones de piezas",
   "Exotic captures": "Capturas exóticas",
-  "Explosive captures": "Capturas explosivas",
+  "Explosive captures (v1)": "Capturas explosivas (v1)",
+  "Explosive captures (v2)": "Capturas explosivas (v2)",
   "Extra bishops and knights": "Alfiles y caballos adicionales",
   "Faster development": "Desarrollo acelerado",
   "Four new pieces": "Quatro nuevas piezas",
   "Extra bishops and knights": "Alfiles y caballos adicionales",
   "Faster development": "Desarrollo acelerado",
   "Four new pieces": "Quatro nuevas piezas",
@@ -213,6 +214,7 @@ export const translations = {
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
   "Knight in pocket": "Caballo en bolsillo",
   "Knight versus pawns": "Caballo contra peones",
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
   "Knight in pocket": "Caballo en bolsillo",
   "Knight versus pawns": "Caballo contra peones",
+  "Lancers everywhere": "Lanceros por todas partes",
   "Landing on the board": "Aterrizando en el tablero",
   "Laws of attraction": "Las leyes de las atracciones",
   "Long jumps over pieces": "Saltos largos sobre las piezas",
   "Landing on the board": "Aterrizando en el tablero",
   "Laws of attraction": "Las leyes de las atracciones",
   "Long jumps over pieces": "Saltos largos sobre las piezas",
index ccd82be..6163dc4 100644 (file)
@@ -195,7 +195,8 @@ export const translations = {
   "Enter the disco": "Entrez dans la boîte",
   "Exchange pieces' positions": "Échangez les positions des pièces",
   "Exotic captures": "Captures exotiques",
   "Enter the disco": "Entrez dans la boîte",
   "Exchange pieces' positions": "Échangez les positions des pièces",
   "Exotic captures": "Captures exotiques",
-  "Explosive captures": "Captures explosives",
+  "Explosive captures (v1)": "Captures explosives (v1)",
+  "Explosive captures (v2)": "Captures explosives (v2)",
   "Extra bishops and knights": "Fous et cavaliers supplémentaires",
   "Faster development": "Développement accéléré",
   "Four new pieces": "Quatre nouvelles pièces",
   "Extra bishops and knights": "Fous et cavaliers supplémentaires",
   "Faster development": "Développement accéléré",
   "Four new pieces": "Quatre nouvelles pièces",
@@ -213,6 +214,7 @@ export const translations = {
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
   "Knight in pocket": "Cavalier en poche",
   "Knight versus pawns": "Cavalier contre pions",
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
   "Knight in pocket": "Cavalier en poche",
   "Knight versus pawns": "Cavalier contre pions",
+  "Lancers everywhere": "Lanciers à tous les coins",
   "Landing on the board": "Débarquement sur l'échiquier",
   "Laws of attraction": "Les lois de l'attraction",
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
   "Landing on the board": "Débarquement sur l'échiquier",
   "Laws of attraction": "Les lois de l'attraction",
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
diff --git a/client/src/translations/rules/Atomic2/en.pug b/client/src/translations/rules/Atomic2/en.pug
new file mode 100644 (file)
index 0000000..dc0e87a
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed.
+  Atomic1, but white's first move is a pawn removal.
+
+p.
+  At the very first move of the game, white must remove a pawn,
+  from either side.
+  Black then replies, and the game continues normally.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPP1PPP/RNBQKBNR:
+  figcaption.
+    After 1.e2X, pawn removal at e2.
+
+h3 More information
+
+p
+  | This variant aims at balancing the big white's advantage in Atomic1.
+  | It was suggested (relayed?) and analyzed recently (2020) by a strong
+  | Atomic player (Gannet on Discord). See
+  a(href="https://discord.com/channels/686736099959504907/687076744095858762/762398439043498046")
+    | the messages
+  | &nbsp;on Discord vchess server.
diff --git a/client/src/translations/rules/Atomic2/es.pug b/client/src/translations/rules/Atomic2/es.pug
new file mode 100644 (file)
index 0000000..21203ba
--- /dev/null
@@ -0,0 +1 @@
+p.boxed TODO
diff --git a/client/src/translations/rules/Atomic2/fr.pug b/client/src/translations/rules/Atomic2/fr.pug
new file mode 100644 (file)
index 0000000..21203ba
--- /dev/null
@@ -0,0 +1 @@
+p.boxed TODO
diff --git a/client/src/translations/rules/Fullcavalry/en.pug b/client/src/translations/rules/Fullcavalry/en.pug
new file mode 100644 (file)
index 0000000..6bb88da
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Rooks are replaced by lancers from Eightpieces variant.
+
+p
+  | Everything is the same as in orthodox rules, except that rooks are
+  | replaced by lancers, as defined in the 
+  a(href="/variants/Eightpieces") Eightpieces variant
+  | .
+
+p.
+  White lancers are facing diagonally upward, while black lancers point
+  diagonally down in the deterministic initial setup.
+  In the randomized version, to avoid complex setup rules,
+  all lancers are facing up.
+
+figure.diagram-container
+  .diagram
+    | fen:efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM:
+  figcaption Initial deterministic position.
+
+h3 More information
+
+p TODO
diff --git a/client/src/translations/rules/Fullcavalry/es.pug b/client/src/translations/rules/Fullcavalry/es.pug
new file mode 100644 (file)
index 0000000..21203ba
--- /dev/null
@@ -0,0 +1 @@
+p.boxed TODO
diff --git a/client/src/translations/rules/Fullcavalry/fr.pug b/client/src/translations/rules/Fullcavalry/fr.pug
new file mode 100644 (file)
index 0000000..2605db4
--- /dev/null
@@ -0,0 +1,24 @@
+p.boxed
+  | Les tours sont remplacées par des lanciers de la variante Eightpieces.
+
+p
+  | Tout se déroule comme aux échecs orthodoxes, sauf en ce qui concerne
+  | les tours qui sont remplacées par des lanciers comme défini dans la 
+  a(href="variants/Eightpieces") variante Eightpieces
+  | .
+
+p.
+  Les lanciers blancs regarde vers l'avant en diagonale, tandis que les
+  lanciers noirs pointent vers le sud (toujours en diagonale) dans
+  l'arrangement initial déterministe.
+  Dans la version aléatoire, tous les lanciers regardent droit devant afin
+  d'éviter des règles d'initialisation complexes.
+
+figure.diagram-container
+  .diagram
+    | fen:efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM:
+  figcaption Position initiale déterministe.
+
+h3 Plus d'information
+
+p TODO
index 5154cdf..c97096b 100644 (file)
@@ -15,10 +15,7 @@ figure.diagram-container
 
 h3 Special moves
 
 
 h3 Special moves
 
-p.
-  Castling is possible as in orthodox 8x8 game. The white king move to c1 or
-  i1 (one square to the left of bottom-right corner) for large (resp. small)
-  castle. Same for black on the other side.
+p Castling is not permitted.
 
 p.
   Promotion is mandatory for a pawn reaching the last rank. However, they can
 
 p.
   Promotion is mandatory for a pawn reaching the last rank. However, they can
@@ -30,12 +27,6 @@ p.
   Pawn promotion is possible (but not forced) on the two ranks before last,
   only in an already captured (friendly) piece.
 
   Pawn promotion is possible (but not forced) on the two ranks before last,
   only in an already captured (friendly) piece.
 
-p.
-  Note: I changed the author's starting position, to increase randomness
-  (and uniformize with other variants).
-  Thus the castling rule was introduced compared to the rules described
-  on chessvariants.com.
-
 h3 Source
 
 p
 h3 Source
 
 p
index 3897030..363857c 100644 (file)
@@ -16,11 +16,7 @@ figure.diagram-container
 
 h3 Movimientos especiales
 
 
 h3 Movimientos especiales
 
-p.
-  El enroque es posible como en un juego con un tablero 8x8.
-  El rey blanco va a c1 o i1 (un cuadro a la izquierda de la esquina
-  inferior derecha) para un enroque grande (resp. pequeño).
-  Lo mismo para las negras del otro lado.
+p No se permite el enroque.
 
 p.
   La promoción es obligatoria para los peones que llegan a la última fila,
 
 p.
   La promoción es obligatoria para los peones que llegan a la última fila,
@@ -32,12 +28,6 @@ p.
   La promoción es posible (pero no obligatoria) en las octava y novena
   filas, siempre en una pieza amiga capturada.
 
   La promoción es posible (pero no obligatoria) en las octava y novena
   filas, siempre en una pieza amiga capturada.
 
-p.
-  Nota: cambié la posición inicial planificada por el autor, a
-  aumentar el alea de la situación inicial (y estandarizar con los demás
-  variantes). De repente, la regla de enroque se agregó en comparación con las
-  reglas inicialmente descrito.
-
 h3 Fuente
 
 p
 h3 Fuente
 
 p
index b058b77..c84d1ae 100644 (file)
@@ -16,10 +16,7 @@ figure.diagram-container
 
 h3 Coups spéciaux
 
 
 h3 Coups spéciaux
 
-p.
-  Le roque est possible comme dans une partie sur un échiquier 8x8.
-  Le roi blanc va en c1 ou i1 (une case à gauche du coin inférieur droit)
-  pour un grand (resp. petit) roque. Pareil pour les noirs de l'autre côté.
+p Le roque n'est pas autorisé.
 
 p.
   La promotion est obligatoire pour les pions arrivant sur la dernière rangée,
 
 p.
   La promotion est obligatoire pour les pions arrivant sur la dernière rangée,
@@ -31,12 +28,6 @@ p.
   La promotion est possible (mais non obligatoire) sur les huitième et neuvième
   rangées, toujours en une pièce amie déjà capturée.
 
   La promotion est possible (mais non obligatoire) sur les huitième et neuvième
   rangées, toujours en une pièce amie déjà capturée.
 
-p.
-  Note : j'ai changé la position de départ prévue par l'auteur, pour
-  augmenter l'alea de la situation initiale (et uniformiser avec les autres
-  variantes). Du coup la règle du roque a été ajoutée comparée aux règles
-  décrites initialement.
-
 h3 Source
 
 p
 h3 Source
 
 p
index 2607bcd..e99f97a 100644 (file)
@@ -113,6 +113,7 @@ p.
 -
   var varlist = [
     "Eightpieces",
 -
   var varlist = [
     "Eightpieces",
+    "Fullcavalry",
     "Grand",
     "Grasshopper",
     "Omega",
     "Grand",
     "Grasshopper",
     "Omega",
@@ -264,7 +265,8 @@ p ...Or leading to a very different strategy:
 -
   var varlist = [
     "Antimatter",
 -
   var varlist = [
     "Antimatter",
-    "Atomic",
+    "Atomic1",
+    "Atomic2",
     "Checkless"
   ]
 ul
     "Checkless"
   ]
 ul
index 094a85e..ed3bd7c 100644 (file)
@@ -120,6 +120,7 @@ p.
 -
   var varlist = [
     "Eightpieces",
 -
   var varlist = [
     "Eightpieces",
+    "Fullcavalry",
     "Grand",
     "Grasshopper",
     "Omega",
     "Grand",
     "Grasshopper",
     "Omega",
@@ -273,7 +274,8 @@ p ...O conduce a estrategias muy diferentes:
 -
   var varlist = [
     "Antimatter",
 -
   var varlist = [
     "Antimatter",
-    "Atomic",
+    "Atomic1",
+    "Atomic2",
     "Checkless"
   ]
 ul
     "Checkless"
   ]
 ul
index 591b725..b3e3f77 100644 (file)
@@ -119,6 +119,7 @@ p.
 -
   var varlist = [
     "Eightpieces",
 -
   var varlist = [
     "Eightpieces",
+    "Fullcavalry",
     "Grand",
     "Grasshopper",
     "Omega",
     "Grand",
     "Grasshopper",
     "Omega",
@@ -272,7 +273,8 @@ p ...Ou mène à des stratégies très différentes :
 -
   var varlist = [
     "Antimatter",
 -
   var varlist = [
     "Antimatter",
-    "Atomic",
+    "Atomic1",
+    "Atomic2",
     "Checkless"
   ]
 ul
     "Checkless"
   ]
 ul
similarity index 92%
rename from client/src/variants/Atomic.js
rename to client/src/variants/Atomic1.js
index e2f4bdf..27769a7 100644 (file)
@@ -1,6 +1,6 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
 import { ChessRules, PiPo } from "@/base_rules";
 
-export class AtomicRules extends ChessRules {
+export class Atomic1Rules extends ChessRules {
 
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
 
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
@@ -79,7 +79,8 @@ export class AtomicRules extends ChessRules {
 
   postPlay(move) {
     super.postPlay(move);
 
   postPlay(move) {
     super.postPlay(move);
-    if (move.appear.length == 0) {
+    // NOTE: (harmless) condition on movesCount for Atomic2
+    if (move.appear.length == 0 && this.movesCount >= 2) {
       // Capture
       const firstRank = { w: 7, b: 0 };
       for (let c of ["w", "b"]) {
       // Capture
       const firstRank = { w: 7, b: 0 };
       for (let c of ["w", "b"]) {
@@ -107,7 +108,11 @@ export class AtomicRules extends ChessRules {
     super.postUndo(move);
     const c = this.turn;
     const oppCol = V.GetOppCol(c);
     super.postUndo(move);
     const c = this.turn;
     const oppCol = V.GetOppCol(c);
-    if ([this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => e < 0)) {
+    // NOTE: condition on movesCount for Atomic2
+    if (
+      this.movesCount >= 1 &&
+      [this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => e < 0)
+    ) {
       // There is a chance that last move blowed some king away..
       for (let psq of move.vanish) {
         if (psq.p == "k")
       // There is a chance that last move blowed some king away..
       for (let psq of move.vanish) {
         if (psq.p == "k")
diff --git a/client/src/variants/Atomic2.js b/client/src/variants/Atomic2.js
new file mode 100644 (file)
index 0000000..9ea2296
--- /dev/null
@@ -0,0 +1,56 @@
+import { ChessRules, Move, PiPo } from "@/base_rules";
+import { Atomic1Rules } from "@/variants/Atomic1";
+
+export class Atomic2Rules extends Atomic1Rules {
+
+  getPotentialMovesFrom([x, y]) {
+    if (this.movesCount == 0) {
+      if ([1, 6].includes(x)) {
+        const c = this.getColor(x, y);
+        return [
+          new Move({
+            appear: [],
+            vanish: [
+              new PiPo({ x: x, y: y, p: V.PAWN, c: c })
+            ],
+            start: { x: x, y: y },
+            end: { x: x, y: y }
+          })
+        ];
+      }
+      return [];
+    }
+    return super.getPotentialMovesFrom([x, y]);
+  }
+
+  hoverHighlight(x, y) {
+    return this.movesCount == 0 && [1, 6].includes(x);
+  }
+
+  doClick(square) {
+    if (this.movesCount >= 1) return null;
+    const [x, y] = [square[0], square[1]];
+    if (![1, 6].includes(x)) return null;
+    return new Move({
+      appear: [],
+      vanish: [
+        new PiPo({
+          x: x,
+          y: y,
+          c: this.getColor(x, y),
+          p: V.PAWN
+        })
+      ],
+      start: { x: x, y: y },
+      end: { x: x, y: y }
+    });
+  }
+
+  getNotation(move) {
+    if (move.appear.length == 0 && move.vanish.length == 1)
+      // First move in game
+      return V.CoordsToSquare(move.start) + "X";
+    return super.getNotation(move);
+  }
+
+};
index 3e15500..1fbc5d0 100644 (file)
@@ -1,4 +1,3 @@
-import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 import { randInt } from "@/utils/alea";
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
@@ -170,79 +169,56 @@ export class EightpiecesRules extends ChessRules {
 
   static GenRandInitFen(randomness) {
     if (randomness == 0)
 
   static GenRandInitFen(randomness) {
     if (randomness == 0)
-      // Deterministic:
       return "jfsqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JDSQKBNR w 0 ahah - -";
 
       return "jfsqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JDSQKBNR w 0 ahah - -";
 
-    let pieces = { w: new Array(8), b: new Array(8) };
-    let flags = "";
-    // Shuffle pieces on first (and last rank if randomness == 2)
-    for (let c of ["w", "b"]) {
-      if (c == 'b' && randomness == 1) {
-        const lancerIdx = pieces['w'].findIndex(p => {
-          return Object.keys(V.LANCER_DIRS).includes(p);
-        });
-        pieces['b'] =
-          pieces['w'].slice(0, lancerIdx)
-          .concat(['g'])
-          .concat(pieces['w'].slice(lancerIdx + 1));
-        flags += flags;
-        break;
-      }
-
-      let positions = ArrayFun.range(8);
-
-      // Get random squares for bishop and sentry
-      let randIndex = 2 * randInt(4);
-      let bishopPos = positions[randIndex];
-      // The sentry must be on a square of different color
-      let randIndex_tmp = 2 * randInt(4) + 1;
-      let sentryPos = positions[randIndex_tmp];
-      if (c == 'b') {
-        // Check if white sentry is on the same color as ours.
-        // If yes: swap bishop and sentry positions.
-        // NOTE: test % 2 == 1 because there are 7 slashes.
-        if ((pieces['w'].indexOf('s') - sentryPos) % 2 == 1)
-          [bishopPos, sentryPos] = [sentryPos, bishopPos];
+    const baseFen = ChessRules.GenRandInitFen(randomness);
+    const fenParts = baseFen.split(' ');
+    const posParts = fenParts[0].split('/');
+
+    // Replace one bishop by sentry, so that sentries on different colors
+    // Also replace one random rook by jailer,
+    // and one random knight by lancer (facing north/south)
+
+    // "replaced" array contains -2 initially, then either -1 if skipped,
+    // or (eventually) the index of replacement:
+    let newPos = { 0: "", 7: "" };
+    let sentryOddity = -1;
+    for (let rank of [0, 7]) {
+      let replaced = { 'b': -2, 'n': -2, 'r': -2 };
+      for (let i = 0; i < 8; i++) {
+        const curChar = posParts[rank].charAt(i).toLowerCase();
+        if (['b', 'n', 'r'].includes(curChar)) {
+          if (
+            replaced[curChar] == -1 ||
+            (curChar == 'b' && rank == 7 && i % 2 == sentryOddity) ||
+            (
+              (curChar != 'b' || rank == 0) &&
+              replaced[curChar] == -2 &&
+              randInt(2) == 0
+            )
+          ) {
+            replaced[curChar] = i;
+            if (curChar == 'b') {
+              if (sentryOddity < 0) sentryOddity = i % 2;
+              newPos[rank] += 's';
+            }
+            else if (curChar == 'r') newPos[rank] += 'j';
+            else
+              // Lancer: orientation depends on side
+              newPos[rank] += (rank == 0 ? 'g' : 'c');
+          }
+          else {
+            if (replaced[curChar] == -2) replaced[curChar]++;
+            newPos[rank] += curChar;
+          }
+        }
+        else newPos[rank] += curChar;
       }
       }
-      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
-      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
-      // Get random squares for knight and lancer
-      randIndex = randInt(6);
-      const knightPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-      randIndex = randInt(5);
-      const lancerPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      // Get random square for queen
-      randIndex = randInt(4);
-      const queenPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      // Rook, jailer and king positions are now almost fixed,
-      // only the ordering rook->jailer or jailer->rook must be decided.
-      let rookPos = positions[0];
-      let jailerPos = positions[2];
-      const kingPos = positions[1];
-      flags += V.CoordToColumn(rookPos) + V.CoordToColumn(jailerPos);
-      if (Math.random() < 0.5) [rookPos, jailerPos] = [jailerPos, rookPos];
-
-      pieces[c][rookPos] = "r";
-      pieces[c][knightPos] = "n";
-      pieces[c][bishopPos] = "b";
-      pieces[c][queenPos] = "q";
-      pieces[c][kingPos] = "k";
-      pieces[c][sentryPos] = "s";
-      // Lancer faces north for white, and south for black:
-      pieces[c][lancerPos] = c == 'w' ? 'c' : 'g';
-      pieces[c][jailerPos] = "j";
     }
     }
+
     return (
     return (
-      pieces["b"].join("") +
-      "/pppppppp/8/8/8/8/PPPPPPPP/" +
-      pieces["w"].join("").toUpperCase() +
-      " w 0 " + flags + " - -"
+      newPos[0] + "/" + posParts.slice(1, 7).join('/') + "/" +
+      newPos[7].toUpperCase() + " " + fenParts.slice(1, 5).join(' ') + " -"
     );
   }
 
     );
   }
 
@@ -384,7 +360,8 @@ export class EightpiecesRules extends ChessRules {
         }
         return true;
       });
         }
         return true;
       });
-    } else if (this.subTurn == 2) {
+    }
+    else if (this.subTurn == 2) {
       // Put back the sentinel on board:
       const color = this.turn;
       moves.forEach(m => {
       // Put back the sentinel on board:
       const color = this.turn;
       moves.forEach(m => {
diff --git a/client/src/variants/Fullcavalry.js b/client/src/variants/Fullcavalry.js
new file mode 100644 (file)
index 0000000..c25f340
--- /dev/null
@@ -0,0 +1,268 @@
+import { ArrayFun } from "@/utils/array";
+import { randInt } from "@/utils/alea";
+import { ChessRules, PiPo, Move } from "@/base_rules";
+
+export class FullcavalryRules extends ChessRules {
+
+  static get LANCER() {
+    return "l";
+  }
+
+  static get IMAGE_EXTENSION() {
+    // Temporarily, for the time SVG pieces are being designed:
+    return ".png";
+  }
+
+  // Lancer directions *from white perspective*
+  static get LANCER_DIRS() {
+    return {
+      'c': [-1, 0], //north
+      'd': [-1, 1], //N-E
+      'e': [0, 1], //east
+      'f': [1, 1], //S-E
+      'g': [1, 0], //south
+      'h': [1, -1], //S-W
+      'm': [0, -1], //west
+      'o': [-1, -1] //N-W
+    };
+  }
+
+  static get PIECES() {
+    return ChessRules.PIECES.concat(Object.keys(V.LANCER_DIRS));
+  }
+
+  getPiece(i, j) {
+    const piece = this.board[i][j].charAt(1);
+    // Special lancer case: 8 possible orientations
+    if (Object.keys(V.LANCER_DIRS).includes(piece)) return V.LANCER;
+    return piece;
+  }
+
+  getPpath(b, color, score, orientation) {
+    if (Object.keys(V.LANCER_DIRS).includes(b[1])) {
+      if (orientation == 'w') return "Eightpieces/tmp_png/" + b;
+      // Find opposite direction for adequate display:
+      let oppDir = '';
+      switch (b[1]) {
+        case 'c':
+          oppDir = 'g';
+          break;
+        case 'g':
+          oppDir = 'c';
+          break;
+        case 'd':
+          oppDir = 'h';
+          break;
+        case 'h':
+          oppDir = 'd';
+          break;
+        case 'e':
+          oppDir = 'm';
+          break;
+        case 'm':
+          oppDir = 'e';
+          break;
+        case 'f':
+          oppDir = 'o';
+          break;
+        case 'o':
+          oppDir = 'f';
+          break;
+      }
+      return "Eightpieces/tmp_png/" + b[0] + oppDir;
+    }
+    // TODO: after we have SVG pieces, remove the folder and next prefix:
+    return "Eightpieces/tmp_png/" + b;
+  }
+
+  getPPpath(m, orientation) {
+    return (
+      this.getPpath(
+        m.appear[0].c + m.appear[0].p,
+        null,
+        null,
+        orientation
+      )
+    );
+  }
+
+  static GenRandInitFen(randomness) {
+    if (randomness == 0)
+      // Deterministic:
+      return "efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM w 0 ahah -";
+
+    const baseFen = ChessRules.GenRandInitFen(randomness);
+    // Replace black rooks by lancers oriented south,
+    // and white rooks by lancers oriented north:
+    return baseFen.replace(/r/g, 'g').replace(/R/g, 'C');
+  }
+
+  // Because of the lancers, getPiece() could be wrong:
+  // use board[x][y][1] instead (always valid).
+  // TODO: base implementation now uses this too (no?)
+  getBasicMove([sx, sy], [ex, ey], tr) {
+    const initColor = this.getColor(sx, sy);
+    const initPiece = this.board[sx][sy].charAt(1);
+    let mv = new Move({
+      appear: [
+        new PiPo({
+          x: ex,
+          y: ey,
+          c: tr ? tr.c : initColor,
+          p: tr ? tr.p : initPiece
+        })
+      ],
+      vanish: [
+        new PiPo({
+          x: sx,
+          y: sy,
+          c: initColor,
+          p: initPiece
+        })
+      ]
+    });
+
+    // The opponent piece disappears if we take it
+    if (this.board[ex][ey] != V.EMPTY) {
+      mv.vanish.push(
+        new PiPo({
+          x: ex,
+          y: ey,
+          c: this.getColor(ex, ey),
+          p: this.board[ex][ey].charAt(1)
+        })
+      );
+    }
+
+    return mv;
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    if (this.getPiece(x, y) == V.LANCER)
+      return this.getPotentialLancerMoves([x, y]);
+    return super.getPotentialMovesFrom([x, y]);
+  }
+
+  // Obtain all lancer moves in "step" direction
+  getPotentialLancerMoves_aux([x, y], step, tr) {
+    let moves = [];
+    // Add all moves to vacant squares until opponent is met:
+    const color = this.getColor(x, y);
+    const oppCol = V.GetOppCol(color)
+    let sq = [x + step[0], y + step[1]];
+    while (V.OnBoard(sq[0], sq[1]) && this.getColor(sq[0], sq[1]) != oppCol) {
+      if (this.board[sq[0]][sq[1]] == V.EMPTY)
+        moves.push(this.getBasicMove([x, y], sq, tr));
+      sq[0] += step[0];
+      sq[1] += step[1];
+    }
+    if (V.OnBoard(sq[0], sq[1]))
+      // Add capturing move
+      moves.push(this.getBasicMove([x, y], sq, tr));
+    return moves;
+  }
+
+  getPotentialLancerMoves([x, y]) {
+    let moves = [];
+    // Add all lancer possible orientations, similar to pawn promotions.
+    const color = this.getColor(x, y);
+    const dirCode = this.board[x][y][1];
+    const curDir = V.LANCER_DIRS[dirCode];
+    const monodirMoves =
+      this.getPotentialLancerMoves_aux([x, y], V.LANCER_DIRS[dirCode]);
+    monodirMoves.forEach(m => {
+      Object.keys(V.LANCER_DIRS).forEach(k => {
+        const newDir = V.LANCER_DIRS[k];
+        // Prevent orientations toward outer board:
+        if (V.OnBoard(m.end.x + newDir[0], m.end.y + newDir[1])) {
+          let mk = JSON.parse(JSON.stringify(m));
+          mk.appear[0].p = k;
+          moves.push(mk);
+        }
+      });
+    });
+    return moves;
+  }
+
+  isAttacked(sq, color) {
+    return (
+      super.isAttacked(sq, color) ||
+      this.isAttackedByLancer(sq, color)
+    );
+  }
+
+  isAttackedByLancer([x, y], color) {
+    for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
+      // If in this direction there are only enemy pieces and empty squares,
+      // and we meet a lancer: can he reach us?
+      // NOTE: do not stop at first lancer, there might be several!
+      let coord = { x: x + step[0], y: y + step[1] };
+      let lancerPos = [];
+      while (
+        V.OnBoard(coord.x, coord.y) &&
+        (
+          this.board[coord.x][coord.y] == V.EMPTY ||
+          this.getColor(coord.x, coord.y) == color
+        )
+      ) {
+        if (
+          this.getPiece(coord.x, coord.y) == V.LANCER &&
+          !this.isImmobilized([coord.x, coord.y])
+        ) {
+          lancerPos.push({x: coord.x, y: coord.y});
+        }
+        coord.x += step[0];
+        coord.y += step[1];
+      }
+      for (let xy of lancerPos) {
+        const dir = V.LANCER_DIRS[this.board[xy.x][xy.y].charAt(1)];
+        if (dir[0] == -step[0] && dir[1] == -step[1]) return true;
+      }
+    }
+    return false;
+  }
+
+  static get VALUES() {
+    return Object.assign(
+      { l: 4.8 }, //Jeff K. estimation (for Eightpieces)
+      ChessRules.VALUES
+    );
+  }
+
+  // For moves notation:
+  static get LANCER_DIRNAMES() {
+    return {
+      'c': "N",
+      'd': "NE",
+      'e': "E",
+      'f': "SE",
+      'g': "S",
+      'h': "SW",
+      'm': "W",
+      'o': "NW"
+    };
+  }
+
+  filterValid(moves) {
+    // At move 1, forbid captures (in case of...):
+    if (this.movesCount >= 2) return moves;
+    return moves.filter(m => m.vanish.length == 1);
+  }
+
+  getNotation(move) {
+    let notation = super.getNotation(move);
+    if (Object.keys(V.LANCER_DIRNAMES).includes(move.vanish[0].p))
+      // Lancer: add direction info
+      notation += "=" + V.LANCER_DIRNAMES[move.appear[0].p];
+    else if (
+      move.vanish[0].p == V.PAWN &&
+      Object.keys(V.LANCER_DIRNAMES).includes(move.appear[0].p)
+    ) {
+      // Fix promotions in lancer:
+      notation = notation.slice(0, -1) +
+        "L:" + V.LANCER_DIRNAMES[move.appear[0].p];
+    }
+    return notation;
+  }
+
+};
index 398a96c..5dc355f 100644 (file)
@@ -2,10 +2,12 @@ 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";
 
-// NOTE: initial setup differs from the original; see
-// https://www.chessvariants.com/large.dir/freeling.html
 export class GrandRules extends ChessRules {
 
 export class GrandRules extends ChessRules {
 
+  static get HasCastle() {
+    return false;
+  }
+
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -24,7 +26,7 @@ export class GrandRules extends ChessRules {
     const fenParts = fen.split(" ");
     return Object.assign(
       ChessRules.ParseFen(fen),
     const fenParts = fen.split(" ");
     return Object.assign(
       ChessRules.ParseFen(fen),
-      { captured: fenParts[5] }
+      { captured: fenParts[4] }
     );
   }
 
     );
   }
 
@@ -97,52 +99,6 @@ export class GrandRules extends ChessRules {
     return ChessRules.PIECES.concat([V.MARSHALL, V.CARDINAL]);
   }
 
     return ChessRules.PIECES.concat([V.MARSHALL, V.CARDINAL]);
   }
 
-  // There may be 2 enPassant squares (if pawn jump 3 squares)
-  getEnpassantFen() {
-    const L = this.epSquares.length;
-    if (!this.epSquares[L - 1]) return "-"; //no en-passant
-    let res = "";
-    this.epSquares[L - 1].forEach(sq => {
-      res += V.CoordsToSquare(sq) + ",";
-    });
-    return res.slice(0, -1); //remove last comma
-  }
-
-  // En-passant after 2-sq or 3-sq jumps
-  getEpSquare(moveOrSquare) {
-    if (!moveOrSquare) return undefined;
-    if (typeof moveOrSquare === "string") {
-      const square = moveOrSquare;
-      if (square == "-") return undefined;
-      let res = [];
-      square.split(",").forEach(sq => {
-        res.push(V.SquareToCoords(sq));
-      });
-      return res;
-    }
-    // Argument is a move:
-    const move = moveOrSquare;
-    const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
-    if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) {
-      const step = (ex - sx) / Math.abs(ex - sx);
-      let res = [
-        {
-          x: sx + step,
-          y: sy
-        }
-      ];
-      if (sx + 2 * step != ex) {
-        // 3-squares jump
-        res.push({
-          x: sx + 2 * step,
-          y: sy
-        });
-      }
-      return res;
-    }
-    return undefined; //default
-  }
-
   getPotentialMovesFrom([x, y]) {
     switch (this.getPiece(x, y)) {
       case V.MARSHALL:
   getPotentialMovesFrom([x, y]) {
     switch (this.getPiece(x, y)) {
       case V.MARSHALL:
@@ -161,7 +117,7 @@ export class GrandRules extends ChessRules {
     let moves = [];
     const [sizeX, sizeY] = [V.size.x, V.size.y];
     const shiftX = color == "w" ? -1 : 1;
     let moves = [];
     const [sizeX, sizeY] = [V.size.x, V.size.y];
     const shiftX = color == "w" ? -1 : 1;
-    const startRanks = color == "w" ? [sizeX - 2, sizeX - 3] : [1, 2];
+    const startRank = (color == "w" ? sizeX - 3 : 2);
     const lastRanks =
       color == "w" ? [0, 1, 2] : [sizeX - 1, sizeX - 2, sizeX - 3];
     const promotionPieces = [
     const lastRanks =
       color == "w" ? [0, 1, 2] : [sizeX - 1, sizeX - 2, sizeX - 3];
     const promotionPieces = [
@@ -178,23 +134,17 @@ export class GrandRules extends ChessRules {
     if (lastRanks.includes(x + shiftX)) {
       finalPieces = promotionPieces.filter(p => this.captured[color][p] > 0);
       if (x + shiftX != lastRanks[0]) finalPieces.push(V.PAWN);
     if (lastRanks.includes(x + shiftX)) {
       finalPieces = promotionPieces.filter(p => this.captured[color][p] > 0);
       if (x + shiftX != lastRanks[0]) finalPieces.push(V.PAWN);
-    } else finalPieces = [V.PAWN];
+    }
+    else finalPieces = [V.PAWN];
     if (this.board[x + shiftX][y] == V.EMPTY) {
       // One square forward
       for (let piece of finalPieces)
         moves.push(
           this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
         );
     if (this.board[x + shiftX][y] == V.EMPTY) {
       // One square forward
       for (let piece of finalPieces)
         moves.push(
           this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
         );
-      if (startRanks.includes(x)) {
-        if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
-          // Two squares jump
-          moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
-          if (x == startRanks[0] && this.board[x + 3 * shiftX][y] == V.EMPTY) {
-            // Three squares jump
-            moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
-          }
-        }
-      }
+      if (x == startRank && this.board[x + 2 * shiftX][y] == V.EMPTY)
+        // Two squares jump
+        moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
     }
     // Captures
     for (let shiftY of [-1, 1]) {
     }
     // Captures
     for (let shiftY of [-1, 1]) {
@@ -216,32 +166,14 @@ export class GrandRules extends ChessRules {
     }
 
     // En passant
     }
 
     // En passant
-    const Lep = this.epSquares.length;
-    const epSquare = this.epSquares[Lep - 1];
-    if (!!epSquare) {
-      for (let epsq of epSquare) {
-        // TODO: some redundant checks
-        if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
-          let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
-          // WARNING: the captured pawn may be diagonally behind us,
-          // if it's a 3-squares jump and we take on 1st passing square
-          const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
-          enpassantMove.vanish.push({
-            x: px,
-            y: epsq.y,
-            p: "p",
-            c: this.getColor(px, epsq.y)
-          });
-          moves.push(enpassantMove);
-        }
-      }
-    }
+    Array.prototype.push.apply(
+      moves,
+      this.getEnpassantCaptures([x, y], shiftX)
+    );
 
     return moves;
   }
 
 
     return moves;
   }
 
-  // TODO: different castle?
-
   getPotentialMarshallMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
       this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
   getPotentialMarshallMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
       this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
@@ -316,64 +248,57 @@ export class GrandRules extends ChessRules {
     if (randomness == 0) {
       return (
         "r8r/1nbqkmcbn1/pppppppppp/91/91/91/91/PPPPPPPPPP/1NBQKMCBN1/R8R " +
     if (randomness == 0) {
       return (
         "r8r/1nbqkmcbn1/pppppppppp/91/91/91/91/PPPPPPPPPP/1NBQKMCBN1/R8R " +
-        // No castling in the official initial setup
-        "w 0 zzzz - 00000000000000"
+        "w 0 - 00000000000000"
       );
     }
 
       );
     }
 
-    let pieces = { w: new Array(10), b: new Array(10) };
-    let flags = "";
-    // Shuffle pieces on first and last rank
+    let pieces = { w: new Array(8), b: new Array(8) };
+    // Shuffle pieces on second and before-last rank
     for (let c of ["w", "b"]) {
       if (c == 'b' && randomness == 1) {
         pieces['b'] = pieces['w'];
     for (let c of ["w", "b"]) {
       if (c == 'b' && randomness == 1) {
         pieces['b'] = pieces['w'];
-        flags += flags;
         break;
       }
 
         break;
       }
 
-      let positions = ArrayFun.range(10);
+      let positions = ArrayFun.range(8);
 
       // Get random squares for bishops
 
       // Get random squares for bishops
-      let randIndex = 2 * randInt(5);
+      let randIndex = 2 * randInt(4);
       let bishop1Pos = positions[randIndex];
       // The second bishop must be on a square of different color
       let bishop1Pos = positions[randIndex];
       // The second bishop must be on a square of different color
-      let randIndex_tmp = 2 * randInt(5) + 1;
+      let randIndex_tmp = 2 * randInt(4) + 1;
       let bishop2Pos = positions[randIndex_tmp];
       // Remove chosen squares
       positions.splice(Math.max(randIndex, randIndex_tmp), 1);
       positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       // Get random squares for knights
       let bishop2Pos = positions[randIndex_tmp];
       // Remove chosen squares
       positions.splice(Math.max(randIndex, randIndex_tmp), 1);
       positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       // Get random squares for knights
-      randIndex = randInt(8);
+      randIndex = randInt(6);
       let knight1Pos = positions[randIndex];
       positions.splice(randIndex, 1);
       let knight1Pos = positions[randIndex];
       positions.splice(randIndex, 1);
-      randIndex = randInt(7);
+      randIndex = randInt(5);
       let knight2Pos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // Get random square for queen
       let knight2Pos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // Get random square for queen
-      randIndex = randInt(6);
+      randIndex = randInt(4);
       let queenPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // ...random square for marshall
       let queenPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // ...random square for marshall
-      randIndex = randInt(5);
+      randIndex = randInt(3);
       let marshallPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // ...random square for cardinal
       let marshallPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // ...random square for cardinal
-      randIndex = randInt(4);
+      randIndex = randInt(2);
       let cardinalPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       let cardinalPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
-      // Rooks and king positions are now fixed,
-      // because of the ordering rook-king-rook
-      let rook1Pos = positions[0];
-      let kingPos = positions[1];
-      let rook2Pos = positions[2];
+      // King position is now fixed,
+      let kingPos = positions[0];
 
       // Finally put the shuffled pieces in the board array
 
       // Finally put the shuffled pieces in the board array
-      pieces[c][rook1Pos] = "r";
       pieces[c][knight1Pos] = "n";
       pieces[c][bishop1Pos] = "b";
       pieces[c][queenPos] = "q";
       pieces[c][knight1Pos] = "n";
       pieces[c][bishop1Pos] = "b";
       pieces[c][queenPos] = "q";
@@ -382,14 +307,12 @@ export class GrandRules extends ChessRules {
       pieces[c][kingPos] = "k";
       pieces[c][bishop2Pos] = "b";
       pieces[c][knight2Pos] = "n";
       pieces[c][kingPos] = "k";
       pieces[c][bishop2Pos] = "b";
       pieces[c][knight2Pos] = "n";
-      pieces[c][rook2Pos] = "r";
-      flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
     }
     return (
     }
     return (
-      pieces["b"].join("") +
-      "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
-      pieces["w"].join("").toUpperCase() +
-      " w 0 " + flags + " - 00000000000000"
+      "r8r/1" + pieces["b"].join("") + "1/" +
+      "pppppppppp/91/91/91/91/PPPPPPPPPP/" +
+      "1" + pieces["w"].join("").toUpperCase() + "1/R8R" +
+      " w 0 " + " - 00000000000000"
     );
   }
 
     );
   }
 
index 7bd5e54..ea28380 100644 (file)
@@ -35,9 +35,6 @@ export class LosersRules extends ChessRules {
   getPossibleMovesFrom(sq) {
     let moves = this.filterValid(this.getPotentialMovesFrom(sq));
     const captureMoves = V.KeepCaptures(moves);
   getPossibleMovesFrom(sq) {
     let moves = this.filterValid(this.getPotentialMovesFrom(sq));
     const captureMoves = V.KeepCaptures(moves);
-
-console.log(this.atLeastOneCapture());
-
     if (captureMoves.length > 0) return captureMoves;
     if (this.atLeastOneCapture()) return [];
     return moves;
     if (captureMoves.length > 0) return captureMoves;
     if (this.atLeastOneCapture()) return [];
     return moves;
index aadc3e7..e9b0594 100644 (file)
@@ -185,6 +185,14 @@ export class TitanRules extends ChessRules {
     return moves;
   }
 
     return moves;
   }
 
+  hoverHighlight(x, y) {
+    const c = this.turn;
+    return (
+      this.movesCount <= 3 &&
+      ((c == 'w' && x == 7) || (c == 'b' && x == 0))
+    );
+  }
+
   // Special case of move 1 = choose squares, knight first, then bishop
   doClick(square) {
     if (this.movesCount >= 4) return null;
   // Special case of move 1 = choose squares, knight first, then bishop
   doClick(square) {
     if (this.movesCount >= 4) return null;
index eb36422..06ef790 100644 (file)
@@ -99,7 +99,7 @@ export default {
           this.alertAndQuit("Analysis disabled for this variant");
         else this.loadGame(orientation);
       })
           this.alertAndQuit("Analysis disabled for this variant");
         else this.loadGame(orientation);
       })
-      .catch((err) => { this.alertAndQuit("Mispelled variant name", true); });
+      //.catch((err) => { this.alertAndQuit("Mispelled variant name", true); });
       this.rulesContent =
         afterRawLoad(
           require(
       this.rulesContent =
         afterRawLoad(
           require(
index 47ed4a8..7def7a6 100644 (file)
@@ -19,7 +19,8 @@ insert or ignore into Variants (name, description) values
   ('Antiking2', 'Keep antiking in check (v2)'),
   ('Antimatter', 'Dangerous collisions'),
   ('Arena', 'Middle battle'),
   ('Antiking2', 'Keep antiking in check (v2)'),
   ('Antimatter', 'Dangerous collisions'),
   ('Arena', 'Middle battle'),
-  ('Atomic', 'Explosive captures'),
+  ('Atomic1', 'Explosive captures (v1)'),
+  ('Atomic2', 'Explosive captures (v2)'),
   ('Ball', 'Score a goal'),
   ('Balaklava', 'Meet the Mammoth'),
   ('Baroque', 'Exotic captures'),
   ('Ball', 'Score a goal'),
   ('Balaklava', 'Meet the Mammoth'),
   ('Baroque', 'Exotic captures'),
@@ -54,6 +55,7 @@ insert or ignore into Variants (name, description) values
   ('Football', 'Score a goal'),
   ('Forward', 'Moving forward'),
   ('Freecapture', 'Capture both colors'),
   ('Football', 'Score a goal'),
   ('Forward', 'Moving forward'),
   ('Freecapture', 'Capture both colors'),
+  ('Fullcavalry', 'Lancers everywhere'),
   ('Grand', 'Big board'),
   ('Grasshopper', 'Long jumps over pieces'),
   ('Gridolina', 'Jump the borders'),
   ('Grand', 'Big board'),
   ('Grasshopper', 'Long jumps over pieces'),
   ('Gridolina', 'Jump the borders'),