From e50a802531b99829c533f22ecd21e359e7e1e049 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 19 May 2020 21:45:52 +0200
Subject: [PATCH] Fix parseInt() usage, rename Doubleorda --> Ordamirror,
 implement Clorange variant

---
 TODO                                          |   5 -
 client/public/images/pieces/Clorange/bc.svg   |   1 +
 client/public/images/pieces/Clorange/bo.svg   |   1 +
 client/public/images/pieces/Clorange/bs.svg   |   1 +
 client/public/images/pieces/Clorange/bt.svg   |   1 +
 client/public/images/pieces/Clorange/bu.svg   |   1 +
 client/public/images/pieces/Clorange/wc.svg   |   1 +
 client/public/images/pieces/Clorange/wo.svg   |   1 +
 client/public/images/pieces/Clorange/ws.svg   |   1 +
 client/public/images/pieces/Clorange/wt.svg   |   1 +
 client/public/images/pieces/Clorange/wu.svg   |   1 +
 client/src/base_rules.js                      |  12 +-
 client/src/components/BaseGame.vue            |   5 +-
 client/src/components/MoveList.vue            |   2 +-
 client/src/data/challengeCheck.js             |   2 +-
 client/src/data/problemCheck.js               |   2 +-
 client/src/store.js                           |   2 +-
 client/src/translations/rules/Clorange/en.pug |  31 ++-
 client/src/translations/rules/Clorange/es.pug |  34 ++-
 client/src/translations/rules/Clorange/fr.pug |  34 ++-
 .../rules/{Doubleorda => Ordamirror}/en.pug   |   0
 .../rules/{Doubleorda => Ordamirror}/es.pug   |   0
 .../rules/{Doubleorda => Ordamirror}/fr.pug   |   0
 client/src/utils/printDiagram.js              |   2 +-
 client/src/utils/squareId.js                  |   2 +-
 client/src/utils/timeControl.js               |   4 +-
 client/src/variants/Alice.js                  |   4 +-
 client/src/variants/Antiking1.js              |   2 +-
 client/src/variants/Antiking2.js              |   2 +-
 client/src/variants/Apocalypse.js             |   6 +-
 client/src/variants/Arena.js                  |   2 +-
 client/src/variants/Ball.js                   |   2 +-
 client/src/variants/Baroque.js                |   2 +-
 client/src/variants/Cannibal.js               |   4 +-
 client/src/variants/Chakart.js                |  29 +--
 client/src/variants/Checkered1.js             |   2 +-
 client/src/variants/Clorange.js               | 206 ++++++++++++------
 client/src/variants/Coregal.js                |   8 +-
 client/src/variants/Crazyhouse.js             |  23 +-
 client/src/variants/Extinction.js             |   2 +-
 client/src/variants/Football.js               |   2 +-
 client/src/variants/Forward.js                |   2 +-
 client/src/variants/Grand.js                  |   3 +-
 client/src/variants/Hamilton.js               |   2 +-
 client/src/variants/Hidden.js                 |   2 +-
 client/src/variants/Horde.js                  |   2 +-
 client/src/variants/Interweave.js             |  17 +-
 client/src/variants/Kinglet.js                |   2 +-
 client/src/variants/Koopa.js                  |  12 +-
 client/src/variants/Maxima.js                 |   4 +-
 client/src/variants/Minishogi.js              |  23 +-
 client/src/variants/Monochrome.js             |   2 +-
 client/src/variants/Omega.js                  |   2 +-
 .../variants/{Doubleorda.js => Ordamirror.js} |   0
 client/src/variants/Pacifist1.js              |   4 +-
 client/src/variants/Parachute.js              |  29 +--
 client/src/variants/Recycle.js                |  26 +--
 client/src/variants/Rococo.js                 |   2 +-
 client/src/variants/Schess.js                 |   8 +-
 client/src/variants/Shogi.js                  |  31 +--
 client/src/variants/Suicide.js                |   2 +-
 client/src/variants/Threechecks.js            |   2 +-
 client/src/variants/Twokings.js               |   2 +-
 client/src/variants/Wormhole.js               |   2 +-
 client/src/views/Game.vue                     |   5 +-
 client/src/views/Hall.vue                     |   4 +-
 client/src/views/MyGames.vue                  |   2 +-
 server/db/populate.sql                        |   2 +-
 68 files changed, 410 insertions(+), 227 deletions(-)
 create mode 120000 client/public/images/pieces/Clorange/bc.svg
 create mode 120000 client/public/images/pieces/Clorange/bo.svg
 create mode 120000 client/public/images/pieces/Clorange/bs.svg
 create mode 120000 client/public/images/pieces/Clorange/bt.svg
 create mode 120000 client/public/images/pieces/Clorange/bu.svg
 create mode 120000 client/public/images/pieces/Clorange/wc.svg
 create mode 120000 client/public/images/pieces/Clorange/wo.svg
 create mode 120000 client/public/images/pieces/Clorange/ws.svg
 create mode 120000 client/public/images/pieces/Clorange/wt.svg
 create mode 120000 client/public/images/pieces/Clorange/wu.svg
 rename client/src/translations/rules/{Doubleorda => Ordamirror}/en.pug (100%)
 rename client/src/translations/rules/{Doubleorda => Ordamirror}/es.pug (100%)
 rename client/src/translations/rules/{Doubleorda => Ordamirror}/fr.pug (100%)
 rename client/src/variants/{Doubleorda.js => Ordamirror.js} (100%)

diff --git a/TODO b/TODO
index 0698ddfa..f4d2c505 100644
--- a/TODO
+++ b/TODO
@@ -3,11 +3,6 @@ Also: if new live game starts in background, "new game" notify OK but not first
 On smartphone for Teleport, Chakart, Weiqi and some others: option "confirm moves on touch screen"
 (=> comme pour corr) + option "confirm moves in corr games"?
 
-Clorange:
-Clockwork Orange Chess (Fergus Duniho,
-1999). https://www.chessvariants.com/other.dir/clockworkorange.html
-implem : pieces code, yellow/red, easy
-
 https://www.chessvariants.com/difftaking.dir/replacement.html
 
 https://www.chessvariants.com/other.dir/pocket.html
diff --git a/client/public/images/pieces/Clorange/bc.svg b/client/public/images/pieces/Clorange/bc.svg
new file mode 120000
index 00000000..b30a26ad
--- /dev/null
+++ b/client/public/images/pieces/Clorange/bc.svg
@@ -0,0 +1 @@
+../Alice/bc.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/bo.svg b/client/public/images/pieces/Clorange/bo.svg
new file mode 120000
index 00000000..1200186b
--- /dev/null
+++ b/client/public/images/pieces/Clorange/bo.svg
@@ -0,0 +1 @@
+../Alice/bo.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/bs.svg b/client/public/images/pieces/Clorange/bs.svg
new file mode 120000
index 00000000..e8cf23a8
--- /dev/null
+++ b/client/public/images/pieces/Clorange/bs.svg
@@ -0,0 +1 @@
+../Alice/bs.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/bt.svg b/client/public/images/pieces/Clorange/bt.svg
new file mode 120000
index 00000000..c517549b
--- /dev/null
+++ b/client/public/images/pieces/Clorange/bt.svg
@@ -0,0 +1 @@
+../Alice/bt.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/bu.svg b/client/public/images/pieces/Clorange/bu.svg
new file mode 120000
index 00000000..09e6ea3e
--- /dev/null
+++ b/client/public/images/pieces/Clorange/bu.svg
@@ -0,0 +1 @@
+../Alice/bu.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/wc.svg b/client/public/images/pieces/Clorange/wc.svg
new file mode 120000
index 00000000..d23af91d
--- /dev/null
+++ b/client/public/images/pieces/Clorange/wc.svg
@@ -0,0 +1 @@
+../Alice/wc.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/wo.svg b/client/public/images/pieces/Clorange/wo.svg
new file mode 120000
index 00000000..4a85712d
--- /dev/null
+++ b/client/public/images/pieces/Clorange/wo.svg
@@ -0,0 +1 @@
+../Alice/wo.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/ws.svg b/client/public/images/pieces/Clorange/ws.svg
new file mode 120000
index 00000000..659b2de0
--- /dev/null
+++ b/client/public/images/pieces/Clorange/ws.svg
@@ -0,0 +1 @@
+../Alice/ws.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/wt.svg b/client/public/images/pieces/Clorange/wt.svg
new file mode 120000
index 00000000..447fc4fe
--- /dev/null
+++ b/client/public/images/pieces/Clorange/wt.svg
@@ -0,0 +1 @@
+../Alice/wt.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Clorange/wu.svg b/client/public/images/pieces/Clorange/wu.svg
new file mode 120000
index 00000000..c1403b33
--- /dev/null
+++ b/client/public/images/pieces/Clorange/wu.svg
@@ -0,0 +1 @@
+../Alice/wu.svg
\ No newline at end of file
diff --git a/client/src/base_rules.js b/client/src/base_rules.js
index ee13d5eb..d219f78a 100644
--- a/client/src/base_rules.js
+++ b/client/src/base_rules.js
@@ -158,7 +158,7 @@ export const ChessRules = class ChessRules {
     // 2) Check turn
     if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn)) return false;
     // 3) Check moves count
-    if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount) >= 0))
+    if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount, 10) >= 0))
       return false;
     // 4) Check flags
     if (V.HasFlags && (!fenParsed.flags || !V.IsGoodFlags(fenParsed.flags)))
@@ -185,7 +185,7 @@ export const ChessRules = class ChessRules {
         if (['K','k'].includes(row[i])) kings[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -232,7 +232,7 @@ export const ChessRules = class ChessRules {
     return {
       // NOTE: column is always one char => max 26 columns
       // row is counted from black side => subtraction
-      x: V.size.x - parseInt(sq.substr(1)),
+      x: V.size.x - parseInt(sq.substr(1), 10),
       y: sq[0].charCodeAt() - 97
     };
   }
@@ -480,7 +480,7 @@ export const ChessRules = class ChessRules {
       let j = 0;
       for (let indexInRow = 0; indexInRow < rows[i].length; indexInRow++) {
         const character = rows[i][indexInRow];
-        const num = parseInt(character);
+        const num = parseInt(character, 10);
         // If num is a number, just shift j:
         if (!isNaN(num)) j += num;
         // Else: something at position i,j
@@ -512,7 +512,7 @@ export const ChessRules = class ChessRules {
     const fenParsed = V.ParseFen(fen);
     this.board = V.GetBoard(fenParsed.position);
     this.turn = fenParsed.turn;
-    this.movesCount = parseInt(fenParsed.movesCount);
+    this.movesCount = parseInt(fenParsed.movesCount, 10);
     this.setOtherVariables(fen);
   }
 
@@ -536,7 +536,7 @@ export const ChessRules = class ChessRules {
             this.INIT_COL_KING["w"] = k;
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue
index e7e097fb..cb65f033 100644
--- a/client/src/components/BaseGame.vue
+++ b/client/src/components/BaseGame.vue
@@ -87,6 +87,7 @@ export default {
       endgameMessage: "",
       orientation: "w",
       mode: "",
+      gameMode: "",
       score: "*", //'*' means 'unfinished'
       moves: [],
       cursor: -1, //index of the move just played
@@ -298,7 +299,7 @@ export default {
       }
       else {
         // Exit analyze mode:
-        this.mode = this.gameMode ;
+        this.mode = this.gameMode;
         this.cursor = this.gameCursor;
         this.moves = this.gameMoves;
         let fen = this.game.fenStart;
@@ -464,11 +465,11 @@ export default {
           this.stackToPlay.unshift(move);
           return;
         }
-        this.inPlay = true;
         if (this.mode == "analyze") this.toggleAnalyze();
         if (this.cursor < this.moves.length - 1)
           // To play a received move, cursor must be at the end of the game:
           this.gotoEnd();
+        this.inPlay = true;
       }
       // The board may show some possible moves: (TODO: bad solution)
       this.$refs["board"].resetCurrentAttempt();
diff --git a/client/src/components/MoveList.vue b/client/src/components/MoveList.vue
index 6385ea0d..2e011b49 100644
--- a/client/src/components/MoveList.vue
+++ b/client/src/components/MoveList.vue
@@ -81,7 +81,7 @@ export default {
     document.getElementById("adjuster")
       .addEventListener("click", processModalClick);
     // Take full width on small screens:
-    let boardSize = parseInt(localStorage.getItem("boardSize"));
+    let boardSize = parseInt(localStorage.getItem("boardSize"), 10);
     if (!boardSize) {
       boardSize =
         window.innerWidth >= 768
diff --git a/client/src/data/challengeCheck.js b/client/src/data/challengeCheck.js
index fbfb445c..7665f6f4 100644
--- a/client/src/data/challengeCheck.js
+++ b/client/src/data/challengeCheck.js
@@ -1,7 +1,7 @@
 import { extractTime } from "@/utils/timeControl";
 
 export function checkChallenge(c) {
-  const vid = parseInt(c.vid);
+  const vid = parseInt(c.vid, 10);
   if (isNaN(vid) || vid <= 0) return "Please select a variant";
 
   const tc = extractTime(c.cadence);
diff --git a/client/src/data/problemCheck.js b/client/src/data/problemCheck.js
index d6b916cf..8ee101f3 100644
--- a/client/src/data/problemCheck.js
+++ b/client/src/data/problemCheck.js
@@ -1,5 +1,5 @@
 export function checkProblem(p) {
-  const vid = parseInt(p.vid);
+  const vid = parseInt(p.vid, 10);
   if (isNaN(vid) || vid <= 0) return "Please select a variant";
 
   if (!V.IsGoodFen(p.fen)) return "Errors in FEN";
diff --git a/client/src/store.js b/client/src/store.js
index 0bfeeebb..45535f84 100644
--- a/client/src/store.js
+++ b/client/src/store.js
@@ -90,7 +90,7 @@ export const store = {
       hints: getItemDefaultTrue("hints"),
       highlight: getItemDefaultTrue("highlight"),
       gotonext: getItemDefaultTrue("gotonext"),
-      randomness: parseInt(localStorage.getItem("randomness"))
+      randomness: parseInt(localStorage.getItem("randomness"), 10)
     };
     if (isNaN(this.state.settings.randomness))
       // Default: random asymmetric
diff --git a/client/src/translations/rules/Clorange/en.pug b/client/src/translations/rules/Clorange/en.pug
index 3a33838b..26679f58 100644
--- a/client/src/translations/rules/Clorange/en.pug
+++ b/client/src/translations/rules/Clorange/en.pug
@@ -1,2 +1,31 @@
 p.boxed
-  | TODO
+  | Captured pieces can be dropped later in the game,
+  | with or without capturing abilities.
+
+p Orthodox rules apply, with the following exceptions:
+ul
+  li.
+    When a regular (resp. non-capturing) Chess piece is captured, it is
+    replaced with a non-capturing (resp. regular) counterpart of the same
+    color and given back to the player of that color,
+    who holds it in hand until he drops it on the board.
+  li.
+    A player who has a piece in hand may use his turn to place it on any
+    empty square on the board. Pawns cannot be dropped on the last rank.
+
+p Non-capturing units appear in yellow for white, and red for black.
+
+figure.diagram-container
+  .diagram
+    | fen:3nkr2/1pNSpp1b/1p1sq3/7p/rT1N1cPp/8/PPPPPP2/R1BB1KR1:
+  figcaption The black king must capture the non-capturing white pawn.
+
+h3 Source
+
+p
+  | Slightly simplified from 
+  a(href="https://www.chessvariants.com/other.dir/clockworkorange.html")
+    | Clockwork Orange Chess
+  | &nbsp;on chessvariants.com.
+
+p Inventor: Fergus Duniho (1999)
diff --git a/client/src/translations/rules/Clorange/es.pug b/client/src/translations/rules/Clorange/es.pug
index 3a33838b..a5a8c832 100644
--- a/client/src/translations/rules/Clorange/es.pug
+++ b/client/src/translations/rules/Clorange/es.pug
@@ -1,2 +1,34 @@
 p.boxed
-  | TODO
+  | Las piezas capturadas se pueden soltar más adelante en el juego,
+  | con o sin capacidad de captura.
+
+p Se aplican reglas ortodoxas, con las siguientes excepciones:
+ul
+  li.
+    Cuando se captura una pieza estándar (resp. sin-captura), es
+    reemplazado por una pieza sin-captura (resp. estándar) del mismo color
+    y regresó al jugador de ese color, que lo tiene en la mano hasta que él
+    decide ponerlo en el tablero.
+  li.
+    Un jugador con una pieza en reserva puede soltarlo en cualquier casilla
+    del tablero en lugar de hacer un movimiento. Los peones no pueden alcanzar
+    la última fila así.
+
+p.
+  Las unidades que no capturan aparecen en amarillo para las blancas,
+  y en rojo para las negras.
+
+figure.diagram-container
+  .diagram
+    | fen:3nkr2/1pNSpp1b/1p1sq3/7p/rT1N1cPp/8/PPPPPP2/R1BB1KR1:
+  figcaption El rey negro debe capturar el peón blanco sin-captura.
+
+h3 Fuente
+
+p
+  | Ligeramente simplificado desde 
+  a(href="https://www.chessvariants.com/other.dir/clockworkorange.html")
+    | Clockwork Orange Chess
+  | &nbsp;en chessvariants.com.
+
+p Inventor: Fergus Duniho (1999)
diff --git a/client/src/translations/rules/Clorange/fr.pug b/client/src/translations/rules/Clorange/fr.pug
index 3a33838b..1e8a4069 100644
--- a/client/src/translations/rules/Clorange/fr.pug
+++ b/client/src/translations/rules/Clorange/fr.pug
@@ -1,2 +1,34 @@
 p.boxed
-  | TODO
+  | Les pièces capturées peuvent être parachutées plus tard dans la partie,
+  | avec ou sans capacité de capture.
+
+p Les règles orthodoxes s'appliquent, avec les exceptions suivantes :
+ul
+  li.
+    Quand une pièce standard (resp. non-capturante) est capturée, elle est
+    remplacée par une pièce non-capturante (resp. standard) de la même couleur
+    et rendue au joueur de cette couleur, qui la garde en main jusqu'à ce qu'il
+    décide de la poser sur l'échiquier.
+  li.
+    Un joueur ayant une pièce en réserve peut la parachuter n'importe où sur
+    l'échiquier au lieu d'effectuer un coup. Les pions ne peuvent atteindre
+    la dernière rangée ainsi.
+
+p.
+  Les unités non-capturantes apparaissent en jaune pour les blancs,
+  et en rouge pour les noirs.
+
+figure.diagram-container
+  .diagram
+    | fen:3nkr2/1pNSpp1b/1p1sq3/7p/rT1N1cPp/8/PPPPPP2/R1BB1KR1:
+  figcaption le roi noir doit capturer le pion blanc non-capturant.
+
+h3 Source
+
+p
+  | Légèrement simplifié depuis 
+  a(href="https://www.chessvariants.com/other.dir/clockworkorange.html")
+    | Clockwork Orange Chess
+  | &nbsp;sur chessvariants.com.
+
+p Inventeur : Fergus Duniho (1999)
diff --git a/client/src/translations/rules/Doubleorda/en.pug b/client/src/translations/rules/Ordamirror/en.pug
similarity index 100%
rename from client/src/translations/rules/Doubleorda/en.pug
rename to client/src/translations/rules/Ordamirror/en.pug
diff --git a/client/src/translations/rules/Doubleorda/es.pug b/client/src/translations/rules/Ordamirror/es.pug
similarity index 100%
rename from client/src/translations/rules/Doubleorda/es.pug
rename to client/src/translations/rules/Ordamirror/es.pug
diff --git a/client/src/translations/rules/Doubleorda/fr.pug b/client/src/translations/rules/Ordamirror/fr.pug
similarity index 100%
rename from client/src/translations/rules/Doubleorda/fr.pug
rename to client/src/translations/rules/Ordamirror/fr.pug
diff --git a/client/src/utils/printDiagram.js b/client/src/utils/printDiagram.js
index aee1e96a..524e12bc 100644
--- a/client/src/utils/printDiagram.js
+++ b/client/src/utils/printDiagram.js
@@ -19,7 +19,7 @@ function getShadowArray(shadow) {
   let shadowArray = ArrayFun.init(V.size.x, V.size.y, false);
   const squares = shadow.split(",");
   for (let i = 0; i < squares.length; i++) {
-    const rownum = V.size.x - parseInt(squares[i]);
+    const rownum = V.size.x - parseInt(squares[i], 10);
     if (!isNaN(rownum)) {
       // Shadow a full row
       for (let i = 0; i < V.size.y; i++) shadowArray[rownum][i] = true;
diff --git a/client/src/utils/squareId.js b/client/src/utils/squareId.js
index 58ca0862..a54f1a5d 100644
--- a/client/src/utils/squareId.js
+++ b/client/src/utils/squareId.js
@@ -7,5 +7,5 @@ export function getSquareId(o) {
 // Inverse function
 export function getSquareFromId(id) {
   const idParts = id.split("-");
-  return [parseInt(idParts[1]), parseInt(idParts[2])];
+  return [parseInt(idParts[1], 10), parseInt(idParts[2], 10)];
 }
diff --git a/client/src/utils/timeControl.js b/client/src/utils/timeControl.js
index c38d08b1..97d5ab6a 100644
--- a/client/src/utils/timeControl.js
+++ b/client/src/utils/timeControl.js
@@ -28,7 +28,7 @@ export function extractTime(cadence) {
   tcParts[0] += "m";
   const mainTimeArray = tcParts[0].match(/^([0-9]+)([smhd]+)$/);
   if (!mainTimeArray) return null;
-  const mainTimeValue = parseInt(mainTimeArray[1]);
+  const mainTimeValue = parseInt(mainTimeArray[1], 10);
   const mainTimeUnit = mainTimeArray[2][0];
   const mainTime = timeUnitToSeconds(mainTimeValue, mainTimeUnit);
   let increment = 0;
@@ -38,7 +38,7 @@ export function extractTime(cadence) {
     tcParts[1] += "s";
     const incrementArray = tcParts[1].match(/^([0-9]+)([smhd]+)$/);
     if (!incrementArray) return null;
-    const incrementValue = parseInt(incrementArray[1]);
+    const incrementValue = parseInt(incrementArray[1], 10);
     const incrementUnit = incrementArray[2][0];
     // Increment unit cannot be larger than main unit:
     if (isLargerUnit(incrementUnit, mainTimeUnit)) return null;
diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js
index 82ba6b79..61fcd015 100644
--- a/client/src/variants/Alice.js
+++ b/client/src/variants/Alice.js
@@ -72,7 +72,7 @@ export class AliceRules extends ChessRules {
         if (['K','k','L','l'].includes(row[i])) kings[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -101,7 +101,7 @@ export class AliceRules extends ChessRules {
               this.kingPos["w"] = [i, k];
               break;
             default: {
-              const num = parseInt(rows[i].charAt(j));
+              const num = parseInt(rows[i].charAt(j), 10);
               if (!isNaN(num)) k += num - 1;
             }
           }
diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js
index ab86f69b..07dae26c 100644
--- a/client/src/variants/Antiking1.js
+++ b/client/src/variants/Antiking1.js
@@ -56,7 +56,7 @@ export class Antiking1Rules extends BerolinaRules {
             this.antikingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(rows[i].charAt(j));
+            const num = parseInt(rows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Antiking2.js b/client/src/variants/Antiking2.js
index a7ec2a83..762ca5c8 100644
--- a/client/src/variants/Antiking2.js
+++ b/client/src/variants/Antiking2.js
@@ -43,7 +43,7 @@ export class Antiking2Rules extends ChessRules {
             this.antikingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(rows[i].charAt(j));
+            const num = parseInt(rows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Apocalypse.js b/client/src/variants/Apocalypse.js
index 2439ae5d..77d17c0e 100644
--- a/client/src/variants/Apocalypse.js
+++ b/client/src/variants/Apocalypse.js
@@ -55,7 +55,7 @@ export class ApocalypseRules extends ChessRules {
         if (['P','p'].includes(row[i])) pawns[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -139,8 +139,8 @@ export class ApocalypseRules extends ChessRules {
 
   setFlags(fenflags) {
     this.penaltyFlags = {
-      'w': parseInt(fenflags[0]),
-      'b': parseInt(fenflags[1])
+      'w': parseInt(fenflags[0], 10),
+      'b': parseInt(fenflags[1], 10)
     };
   }
 
diff --git a/client/src/variants/Arena.js b/client/src/variants/Arena.js
index 7cfda1d0..62d2802a 100644
--- a/client/src/variants/Arena.js
+++ b/client/src/variants/Arena.js
@@ -25,7 +25,7 @@ export class ArenaRules extends ChessRules {
         if (['K','k','Q','q'].includes(row[i])) royals[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Ball.js b/client/src/variants/Ball.js
index c9bdb339..056ec61b 100644
--- a/client/src/variants/Ball.js
+++ b/client/src/variants/Ball.js
@@ -105,7 +105,7 @@ export class BallRules extends ChessRules {
           if (withBall.includes(lowerRi)) ballCount++;
           sumElts++;
         } else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js
index 1e02cdf7..4ff23a00 100644
--- a/client/src/variants/Baroque.js
+++ b/client/src/variants/Baroque.js
@@ -38,7 +38,7 @@ export class BaroqueRules extends ChessRules {
             this.kingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(position[i].charAt(j));
+            const num = parseInt(position[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Cannibal.js b/client/src/variants/Cannibal.js
index 1a8a4518..bd5524f7 100644
--- a/client/src/variants/Cannibal.js
+++ b/client/src/variants/Cannibal.js
@@ -49,7 +49,7 @@ export class CannibalRules extends ChessRules {
         else if (kingWhiteCodes.includes(row[i])) kings['w']++;
         if (allPiecesCodes.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -74,7 +74,7 @@ export class CannibalRules extends ChessRules {
             const color = (piece.charCodeAt(0) <= 90 ? 'w' : 'b');
             this.kingPos[color] = [i, k];
           } else {
-            const num = parseInt(rows[i].charAt(j));
+            const num = parseInt(rows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
           k++;
diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js
index 73d7cfc9..1d12e42e 100644
--- a/client/src/variants/Chakart.js
+++ b/client/src/variants/Chakart.js
@@ -148,7 +148,7 @@ export class ChakartRules extends ChessRules {
         if (['K', 'k', 'L', 'l'].includes(row[i])) kings[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -210,24 +210,25 @@ export class ChakartRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Initialize captured pieces' counts from FEN
+    const captured =
+      V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10));
     this.captured = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.captured[0]),
-        [V.ROOK]: parseInt(fenParsed.captured[1]),
-        [V.KNIGHT]: parseInt(fenParsed.captured[2]),
-        [V.BISHOP]: parseInt(fenParsed.captured[3]),
-        [V.QUEEN]: parseInt(fenParsed.captured[4]),
-        [V.KING]: parseInt(fenParsed.captured[5])
+        [V.PAWN]: captured[0],
+        [V.ROOK]: captured[1],
+        [V.KNIGHT]: captured[2],
+        [V.BISHOP]: captured[3],
+        [V.QUEEN]: captured[4],
+        [V.KING]: captured[5]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.captured[6]),
-        [V.ROOK]: parseInt(fenParsed.captured[7]),
-        [V.KNIGHT]: parseInt(fenParsed.captured[8]),
-        [V.BISHOP]: parseInt(fenParsed.captured[9]),
-        [V.QUEEN]: parseInt(fenParsed.captured[10]),
-        [V.KING]: parseInt(fenParsed.captured[11])
+        [V.PAWN]: captured[6],
+        [V.ROOK]: captured[7],
+        [V.KNIGHT]: captured[8],
+        [V.BISHOP]: captured[9],
+        [V.QUEEN]: captured[10],
+        [V.KING]: captured[11]
       }
     };
     this.firstMove = [];
diff --git a/client/src/variants/Checkered1.js b/client/src/variants/Checkered1.js
index 561f02f1..47e7375e 100644
--- a/client/src/variants/Checkered1.js
+++ b/client/src/variants/Checkered1.js
@@ -54,7 +54,7 @@ export class Checkered1Rules extends ChessRules {
     }
     // Stage 1: as Checkered2. Stage 2: checkered pieces are autonomous
     const stageInfo = V.ParseFen(fen).stage;
-    this.stage = parseInt(stageInfo[0]);
+    this.stage = parseInt(stageInfo[0], 10);
     this.sideCheckered = (this.stage == 2 ? stageInfo[1] : undefined);
   }
 
diff --git a/client/src/variants/Clorange.js b/client/src/variants/Clorange.js
index a9991316..4c54e941 100644
--- a/client/src/variants/Clorange.js
+++ b/client/src/variants/Clorange.js
@@ -2,15 +2,6 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 
 export class ClorangeRules extends ChessRules {
-  static get PawnSpecs() {
-    return Object.assign(
-      {},
-      ChessRules.PawnSpecs,
-      // TODO: pawns reaching last rank promote normally? Seems better
-      { promotions: [V.PAWN] }
-    );
-  }
-
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -42,38 +33,64 @@ export class ClorangeRules extends ChessRules {
   }
 
   getReserveFen() {
-    let counts = new Array(10);
-    for (
-      let i = 0;
-      i < V.PIECES.length - 1;
-      i++ //-1: no king reserve
+    return (
+      Object.keys(this.reserve).map(
+        c => Object.values(this.reserve[c]).join("")).join("")
+    );
+  }
+
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
+      const square = moveOrSquare;
+      if (square == "-") return undefined;
+      return V.SquareToCoords(square);
+    }
+    const move = moveOrSquare;
+    const s = move.start,
+          e = move.end;
+    if (
+      s.y == e.y &&
+      Math.abs(s.x - e.x) == 2 &&
+      move.vanish.length > 0 && ['p', 's'].includes(move.vanish[0].p)
     ) {
-      // TODO: adapt
-      counts[i] = this.reserve["w"][V.PIECES[i]];
-      counts[5 + i] = this.reserve["b"][V.PIECES[i]];
+      return {
+        x: (s.x + e.x) / 2,
+        y: s.y
+      };
     }
-    return counts.join("");
+    return undefined;
   }
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
-    // TODO: adapt
+    const reserve =
+      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
     this.reserve = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.reserve[0]),
-        [V.ROOK]: parseInt(fenParsed.reserve[1]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[2]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[3]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[4])
+        'p': reserve[0],
+        'r': reserve[1],
+        'n': reserve[2],
+        'b': reserve[3],
+        'q': reserve[4],
+        's': reserve[5],
+        'u': reserve[6],
+        'o': reserve[7],
+        'c': reserve[8],
+        't': reserve[9]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.reserve[5]),
-        [V.ROOK]: parseInt(fenParsed.reserve[6]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[7]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[8]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[9])
+        'p': reserve[10],
+        'r': reserve[11],
+        'n': reserve[12],
+        'b': reserve[13],
+        'q': reserve[14],
+        's': reserve[15],
+        'u': reserve[16],
+        'o': reserve[17],
+        'c': reserve[18],
+        't': reserve[19]
       }
     };
   }
@@ -88,17 +105,27 @@ export class ClorangeRules extends ChessRules {
     return this.board[i][j].charAt(1);
   }
 
+  getPpath(b) {
+    return (V.NON_VIOLENT.includes(b[1]) ? "Clorange/" : "") + b;
+  }
+
   getReservePpath(index, color) {
-    return color + V.RESERVE_PIECES[index];
+    const prefix =
+      (V.NON_VIOLENT.includes(V.RESERVE_PIECES[index]) ? "Clorange/" : "");
+    return prefix + color + V.RESERVE_PIECES[index];
   }
 
   static get NON_VIOLENT() {
-    return ['s', 'u', 'o', 'c', 't', 'l'];
+    return ['s', 'u', 'o', 'c', 't'];
+  }
+
+  static get PIECES() {
+    return ChessRules.PIECES.concat(V.NON_VIOLENT);
   }
 
   // Ordering on reserve pieces
   static get RESERVE_PIECES() {
-    return ChessRules.PIECES.concat(V.NON_VIOLENT);
+    return V.PIECES.filter(p => p != 'k');
   }
 
   getReserveMoves([x, y]) {
@@ -106,8 +133,13 @@ export class ClorangeRules extends ChessRules {
     const p = V.RESERVE_PIECES[y];
     if (this.reserve[color][p] == 0) return [];
     let moves = [];
-    const pawnShift = p == V.PAWN ? 1 : 0;
-    for (let i = pawnShift; i < V.size.x - pawnShift; i++) {
+    let rank1 = 0;
+    let rank2 = V.size.x - 1;
+    if (['p', 's'].includes(p)) {
+      if (color == 'w') rank1++;
+      else rank2--;
+    }
+    for (let i = rank1; i <= rank2; i++) {
       for (let j = 0; j < V.size.y; j++) {
         if (this.board[i][j] == V.EMPTY) {
           let mv = new Move({
@@ -130,28 +162,52 @@ export class ClorangeRules extends ChessRules {
     return moves;
   }
 
-  // TODO: adapt all below:
   getPotentialMovesFrom([x, y]) {
-    if (x >= V.size.x) {
+    if (x >= V.size.x)
       // Reserves, outside of board: x == sizeX(+1)
       return this.getReserveMoves([x, y]);
-    }
     // Standard moves
-    return super.getPotentialMovesFrom([x, y]);
+    switch (this.getPiece(x, y)) {
+      case 's': return super.getPotentialPawnMoves([x, y]);
+      case 'u': return super.getPotentialRookMoves([x, y]);
+      case 'o': return super.getPotentialKnightMoves([x, y]);
+      case 'c': return super.getPotentialBishopMoves([x, y]);
+      case 't': return super.getPotentialQueenMoves([x, y]);
+      default: return super.getPotentialMovesFrom([x, y]);
+    }
+    return []; //never reached
   }
 
-  getPotentialPawnMoves([x, y]) {
-    
-    let moves = super.getPotentialPawnMoves([x, y]);
-    // Remove pawns on 8th rank ("fallen"):
-    const color = this.turn;
-    const lastRank = (color == "w" ? 0 : V.size.x - 1);
+  getPotentialPawnMoves(sq) {
+    let moves = super.getPotentialPawnMoves(sq);
     moves.forEach(m => {
-      if (m.appear[0].x == lastRank) m.appear.pop();
+      if (m.vanish[0].p == 's' && m.appear[0].p != 's') {
+        // Promotion pieces should be non-violent as well:
+        const pIdx = ChessRules.PIECES.findIndex(p => p == m.appear[0].p)
+        m.appear[0].p = V.NON_VIOLENT[pIdx];
+      }
     });
     return moves;
   }
 
+  getSlideNJumpMoves([x, y], steps, oneStep) {
+    let moves = [];
+    const canTake = ChessRules.PIECES.includes(this.getPiece(x, y));
+    outerLoop: for (let step of steps) {
+      let i = x + step[0];
+      let j = y + step[1];
+      while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+        moves.push(this.getBasicMove([x, y], [i, j]));
+        if (oneStep) continue outerLoop;
+        i += step[0];
+        j += step[1];
+      }
+      if (V.OnBoard(i, j) && canTake && this.canTake([x, y], [i, j]))
+        moves.push(this.getBasicMove([x, y], [i, j]));
+    }
+    return moves;
+  }
+
   getAllValidMoves() {
     let moves = super.getAllPotentialMoves();
     const color = this.turn;
@@ -177,20 +233,22 @@ export class ClorangeRules extends ChessRules {
     return true;
   }
 
-  canTake([x1, y1], [x2, y2]) {
-    // Self-captures allowed, except for the king:
-    return this.getPiece(x2, y2) != V.KING;
-  }
-
   prePlay(move) {
     super.prePlay(move);
     // Skip castle:
     if (move.vanish.length == 2 && move.appear.length == 2) return;
     const color = this.turn;
     if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--;
-    else if (move.vanish.length == 2 && move.vanish[1].c == color)
-      // Self-capture
-      this.reserve[color][move.vanish[1].p]++;
+    else if (move.vanish.length == 2) {
+      // Capture
+      const normal = ChessRules.PIECES.includes(move.vanish[1].p);
+      const pIdx =
+        normal
+          ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p)
+          : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p);
+      const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx];
+      this.reserve[move.vanish[1].c][rPiece]++;
+    }
   }
 
   postUndo(move) {
@@ -198,14 +256,34 @@ export class ClorangeRules extends ChessRules {
     if (move.vanish.length == 2 && move.appear.length == 2) return;
     const color = this.turn;
     if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]++;
-    else if (move.vanish.length == 2 && move.vanish[1].c == color)
-      this.reserve[color][move.vanish[1].p]--;
+    else if (move.vanish.length == 2) {
+      const normal = ChessRules.PIECES.includes(move.vanish[1].p);
+      const pIdx =
+        normal
+          ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p)
+          : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p);
+      const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx];
+      this.reserve[move.vanish[1].c][rPiece]--;
+    }
   }
 
   static get SEARCH_DEPTH() {
     return 2;
   }
 
+  static get VALUES() {
+    return Object.assign(
+      {
+        s: 0.75,
+        u: 4,
+        o: 2,
+        c: 2,
+        t: 7
+      },
+      ChessRules.VALUES
+    );
+  }
+
   evalPosition() {
     let evaluation = super.evalPosition();
     // Add reserves:
@@ -220,16 +298,12 @@ export class ClorangeRules extends ChessRules {
   getNotation(move) {
     const finalSquare = V.CoordsToSquare(move.end);
     if (move.vanish.length > 0) {
-      if (move.appear.length > 0) {
-        // Standard move
-        return super.getNotation(move);
-      } else {
-        // Pawn fallen: capturing or not
-        let res = "";
-        if (move.vanish.length == 2)
-          res += V.CoordToColumn(move.start.y) + "x";
-        return res + finalSquare;
-      }
+      // Standard move (maybe with non-violent piece)
+      let notation = super.getNotation(move);
+      if (move.vanish[0].p == 's' && move.appear[0].p != 's')
+        // Fix non-violent promotions:
+        notation += "=" + move.appear[0].p.toUpperCase();
+      return notation;
     }
     // Rebirth:
     const piece =
diff --git a/client/src/variants/Coregal.js b/client/src/variants/Coregal.js
index d6c736f8..198cbfb0 100644
--- a/client/src/variants/Coregal.js
+++ b/client/src/variants/Coregal.js
@@ -37,7 +37,7 @@ export class CoregalRules extends ChessRules {
             this.kingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
@@ -123,8 +123,10 @@ export class CoregalRules extends ChessRules {
         if (!!bishop1Options[pos]) delete bishop1Options[pos];
         else if (!!bishop2Options[pos]) delete bishop2Options[pos];
       });
-      const bishop1Pos = parseInt(sample(Object.keys(bishop1Options), 1)[0]);
-      const bishop2Pos = parseInt(sample(Object.keys(bishop2Options), 1)[0]);
+      const bishop1Pos =
+        parseInt(sample(Object.keys(bishop1Options), 1)[0], 10);
+      const bishop2Pos =
+        parseInt(sample(Object.keys(bishop2Options), 1)[0], 10);
 
       // Knights' positions are now determined
       const forbidden = [
diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js
index 8e619743..ee485448 100644
--- a/client/src/variants/Crazyhouse.js
+++ b/client/src/variants/Crazyhouse.js
@@ -79,22 +79,23 @@ export class CrazyhouseRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
+    const reserve =
+      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
     this.reserve = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.reserve[0]),
-        [V.ROOK]: parseInt(fenParsed.reserve[1]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[2]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[3]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[4])
+        [V.PAWN]: reserve[0],
+        [V.ROOK]: reserve[1],
+        [V.KNIGHT]: reserve[2],
+        [V.BISHOP]: reserve[3],
+        [V.QUEEN]: reserve[4]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.reserve[5]),
-        [V.ROOK]: parseInt(fenParsed.reserve[6]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[7]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[8]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[9])
+        [V.PAWN]: reserve[5],
+        [V.ROOK]: reserve[6],
+        [V.KNIGHT]: reserve[7],
+        [V.BISHOP]: reserve[8],
+        [V.QUEEN]: reserve[9]
       }
     };
     this.promoted = ArrayFun.init(V.size.x, V.size.y, false);
diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js
index bdea80b5..db8abc06 100644
--- a/client/src/variants/Extinction.js
+++ b/client/src/variants/Extinction.js
@@ -16,7 +16,7 @@ export class ExtinctionRules extends ChessRules {
     let pieces = {};
     for (let row of rows) {
       for (let i = 0; i < row.length; i++) {
-        if (isNaN(parseInt(row[i])) && !pieces[row[i]])
+        if (isNaN(parseInt(row[i], 10)) && !pieces[row[i]])
           pieces[row[i]] = true;
       }
     }
diff --git a/client/src/variants/Football.js b/client/src/variants/Football.js
index e7e65138..1ccb2a4d 100644
--- a/client/src/variants/Football.js
+++ b/client/src/variants/Football.js
@@ -44,7 +44,7 @@ export class FootballRules extends ChessRules {
           pieces[row[i] == lowerRi ? "b" : "w"]++;
           sumElts++;
         } else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Forward.js b/client/src/variants/Forward.js
index 5c5b641d..8f2fc895 100644
--- a/client/src/variants/Forward.js
+++ b/client/src/variants/Forward.js
@@ -46,7 +46,7 @@ export class ForwardRules extends ChessRules {
             this.INIT_COL_KING["w"] = k;
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js
index b88410c9..14c0cc96 100644
--- a/client/src/variants/Grand.js
+++ b/client/src/variants/Grand.js
@@ -55,7 +55,8 @@ export class GrandRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const captured = V.ParseFen(fen).captured.split("").map(parseInt);
+    const captured =
+      V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10));
     // Initialize captured pieces' counts from FEN
     this.captured = {
       w: {
diff --git a/client/src/variants/Hamilton.js b/client/src/variants/Hamilton.js
index 6794e726..69dd9e5c 100644
--- a/client/src/variants/Hamilton.js
+++ b/client/src/variants/Hamilton.js
@@ -50,7 +50,7 @@ export class HamiltonRules extends ChessRules {
       for (let i = 0; i < row.length; i++) {
         if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js
index f9455fd6..2d44df5c 100644
--- a/client/src/variants/Hidden.js
+++ b/client/src/variants/Hidden.js
@@ -94,7 +94,7 @@ export class HiddenRules extends ChessRules {
             this.kingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Horde.js b/client/src/variants/Horde.js
index aac4d27a..c436811b 100644
--- a/client/src/variants/Horde.js
+++ b/client/src/variants/Horde.js
@@ -24,7 +24,7 @@ export class HordeRules extends ChessRules {
           }
           sumElts++;
         } else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Interweave.js b/client/src/variants/Interweave.js
index 8e0c3cc6..973797d6 100644
--- a/client/src/variants/Interweave.js
+++ b/client/src/variants/Interweave.js
@@ -56,7 +56,7 @@ export class InterweaveRules extends ChessRules {
         if (['K','k'].includes(row[i])) kings[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -97,18 +97,19 @@ export class InterweaveRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
+    const captured =
+      V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10));
     // Initialize captured pieces' counts from FEN
     this.captured = {
       w: {
-        [V.ROOK]: parseInt(fenParsed.captured[0]),
-        [V.KNIGHT]: parseInt(fenParsed.captured[1]),
-        [V.BISHOP]: parseInt(fenParsed.captured[2]),
+        [V.ROOK]: captured[0],
+        [V.KNIGHT]: captured[1],
+        [V.BISHOP]: captured[2]
       },
       b: {
-        [V.ROOK]: parseInt(fenParsed.captured[3]),
-        [V.KNIGHT]: parseInt(fenParsed.captured[4]),
-        [V.BISHOP]: parseInt(fenParsed.captured[5]),
+        [V.ROOK]: captured[3],
+        [V.KNIGHT]: captured[4],
+        [V.BISHOP]: captured[5]
       }
     };
     // Stack of "last move" only for intermediate captures
diff --git a/client/src/variants/Kinglet.js b/client/src/variants/Kinglet.js
index 38ca6492..518069f8 100644
--- a/client/src/variants/Kinglet.js
+++ b/client/src/variants/Kinglet.js
@@ -28,7 +28,7 @@ export class KingletRules extends ChessRules {
           if (lowerRi == 'p') pawns[row[i] == lowerRi ? "b" : "w"]++;
           sumElts++;
         } else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Koopa.js b/client/src/variants/Koopa.js
index f24e5b2f..3f2e855a 100644
--- a/client/src/variants/Koopa.js
+++ b/client/src/variants/Koopa.js
@@ -49,11 +49,9 @@ export class KoopaRules extends ChessRules {
   }
 
   getStunnedFen() {
-    return (
-      Object.keys(this.stunned)
-      .map(square => square + this.stunned[square])
-      .join(",")
-    );
+    const squares = Object.keys(this.stunned);
+    if (squares.length == 0) return "-";
+    return squares.map(square => square + this.stunned[square]).join(",");
   }
 
   // Base GenRandInitFen() is fine because en-passant indicator will
@@ -80,7 +78,7 @@ export class KoopaRules extends ChessRules {
             this.INIT_COL_KING["w"] = k;
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
@@ -100,7 +98,7 @@ export class KoopaRules extends ChessRules {
         .map(s => {
           return {
             square: s.substr(0, 2),
-            state: parseInt(s[2])
+            state: parseInt(s[2], 10)
           };
         });
     }
diff --git a/client/src/variants/Maxima.js b/client/src/variants/Maxima.js
index 83d20765..e8b15d25 100644
--- a/client/src/variants/Maxima.js
+++ b/client/src/variants/Maxima.js
@@ -49,7 +49,7 @@ export class MaximaRules extends ChessRules {
         if (['K','k'].includes(row[i])) kings[row[i]]++;
         if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -76,7 +76,7 @@ export class MaximaRules extends ChessRules {
             this.kingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(position[i].charAt(j));
+            const num = parseInt(position[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Minishogi.js b/client/src/variants/Minishogi.js
index 83152894..afeaa57e 100644
--- a/client/src/variants/Minishogi.js
+++ b/client/src/variants/Minishogi.js
@@ -42,22 +42,23 @@ export class MinishogiRules extends ShogiRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
+    const reserve =
+      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
     this.reserve = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.reserve[0]),
-        [V.ROOK]: parseInt(fenParsed.reserve[1]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[2]),
-        [V.GOLD_G]: parseInt(fenParsed.reserve[3]),
-        [V.SILVER_G]: parseInt(fenParsed.reserve[4])
+        [V.PAWN]: reserve[0],
+        [V.ROOK]: reserve[1],
+        [V.BISHOP]: reserve[2],
+        [V.GOLD_G]: reserve[3],
+        [V.SILVER_G]: reserve[4]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.reserve[5]),
-        [V.ROOK]: parseInt(fenParsed.reserve[6]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[7]),
-        [V.GOLD_G]: parseInt(fenParsed.reserve[8]),
-        [V.SILVER_G]: parseInt(fenParsed.reserve[9])
+        [V.PAWN]: reserve[5],
+        [V.ROOK]: reserve[6],
+        [V.BISHOP]: reserve[7],
+        [V.GOLD_G]: reserve[8],
+        [V.SILVER_G]: reserve[9]
       }
     };
   }
diff --git a/client/src/variants/Monochrome.js b/client/src/variants/Monochrome.js
index b17b6f1f..31cf59ea 100644
--- a/client/src/variants/Monochrome.js
+++ b/client/src/variants/Monochrome.js
@@ -23,7 +23,7 @@ export class MonochromeRules extends ChessRules {
       for (let i = 0; i < row.length; i++) {
         if (V.PIECES.includes(row[i])) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Omega.js b/client/src/variants/Omega.js
index 258bc807..1fde5821 100644
--- a/client/src/variants/Omega.js
+++ b/client/src/variants/Omega.js
@@ -48,7 +48,7 @@ export class OmegaRules extends ChessRules {
         if (['K','k'].includes(row[i])) kings[row[i]]++;
         if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Doubleorda.js b/client/src/variants/Ordamirror.js
similarity index 100%
rename from client/src/variants/Doubleorda.js
rename to client/src/variants/Ordamirror.js
diff --git a/client/src/variants/Pacifist1.js b/client/src/variants/Pacifist1.js
index 234b02a8..43c81d71 100644
--- a/client/src/variants/Pacifist1.js
+++ b/client/src/variants/Pacifist1.js
@@ -24,7 +24,7 @@ export class Pacifist1Rules extends ChessRules {
         if (['K','k'].includes(row[i])) kingsCount++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -52,7 +52,7 @@ export class Pacifist1Rules extends ChessRules {
             this.INIT_COL_KING["w"] = k;
             break;
           default: {
-            const num = parseInt(fenRows[i].charAt(j));
+            const num = parseInt(fenRows[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Parachute.js b/client/src/variants/Parachute.js
index 86b1ce84..8cd0f9c2 100644
--- a/client/src/variants/Parachute.js
+++ b/client/src/variants/Parachute.js
@@ -23,7 +23,7 @@ export class ParachuteRules extends ChessRules {
       for (let i = 0; i < row.length; i++) {
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
@@ -65,24 +65,25 @@ export class ParachuteRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
+    const reserve =
+      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
     this.reserve = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.reserve[0]),
-        [V.ROOK]: parseInt(fenParsed.reserve[1]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[2]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[3]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[4]),
-        [V.KING]: parseInt(fenParsed.reserve[5])
+        [V.PAWN]: reserve[0],
+        [V.ROOK]: reserve[1],
+        [V.KNIGHT]: reserve[2],
+        [V.BISHOP]: reserve[3],
+        [V.QUEEN]: reserve[4],
+        [V.KING]: reserve[5]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.reserve[6]),
-        [V.ROOK]: parseInt(fenParsed.reserve[7]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[8]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[9]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[10]),
-        [V.KING]: parseInt(fenParsed.reserve[11])
+        [V.PAWN]: reserve[6],
+        [V.ROOK]: reserve[7],
+        [V.KNIGHT]: reserve[8],
+        [V.BISHOP]: reserve[9],
+        [V.QUEEN]: reserve[10],
+        [V.KING]: reserve[11]
       }
     };
   }
diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js
index bf252632..d23a78c2 100644
--- a/client/src/variants/Recycle.js
+++ b/client/src/variants/Recycle.js
@@ -54,22 +54,23 @@ export class RecycleRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
+    const reserve =
+      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
     this.reserve = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.reserve[0]),
-        [V.ROOK]: parseInt(fenParsed.reserve[1]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[2]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[3]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[4])
+        [V.PAWN]: reserve[0],
+        [V.ROOK]: reserve[1],
+        [V.KNIGHT]: reserve[2],
+        [V.BISHOP]: reserve[3],
+        [V.QUEEN]: reserve[4]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.reserve[5]),
-        [V.ROOK]: parseInt(fenParsed.reserve[6]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[7]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[8]),
-        [V.QUEEN]: parseInt(fenParsed.reserve[9])
+        [V.PAWN]: reserve[5],
+        [V.ROOK]: reserve[6],
+        [V.KNIGHT]: reserve[7],
+        [V.BISHOP]: reserve[8],
+        [V.QUEEN]: reserve[9]
       }
     };
   }
@@ -124,10 +125,9 @@ export class RecycleRules extends ChessRules {
   }
 
   getPotentialMovesFrom([x, y]) {
-    if (x >= V.size.x) {
+    if (x >= V.size.x)
       // Reserves, outside of board: x == sizeX(+1)
       return this.getReserveMoves([x, y]);
-    }
     // Standard moves
     return super.getPotentialMovesFrom([x, y]);
   }
diff --git a/client/src/variants/Rococo.js b/client/src/variants/Rococo.js
index e0838e14..e6480a8c 100644
--- a/client/src/variants/Rococo.js
+++ b/client/src/variants/Rococo.js
@@ -54,7 +54,7 @@ export class RococoRules extends ChessRules {
             this.kingPos["w"] = [i, k];
             break;
           default: {
-            const num = parseInt(position[i].charAt(j));
+            const num = parseInt(position[i].charAt(j), 10);
             if (!isNaN(num)) k += num - 1;
           }
         }
diff --git a/client/src/variants/Schess.js b/client/src/variants/Schess.js
index 06644c7e..95118982 100644
--- a/client/src/variants/Schess.js
+++ b/client/src/variants/Schess.js
@@ -119,12 +119,12 @@ export class SchessRules extends ChessRules {
     const fenParsed = V.ParseFen(fen);
     this.pocket = {
       "w": {
-        h: parseInt(fenParsed.pocket[0]),
-        e: parseInt(fenParsed.pocket[1])
+        h: parseInt(fenParsed.pocket[0], 10),
+        e: parseInt(fenParsed.pocket[1], 10)
       },
       "b": {
-        h: parseInt(fenParsed.pocket[2]),
-        e: parseInt(fenParsed.pocket[3])
+        h: parseInt(fenParsed.pocket[2], 10),
+        e: parseInt(fenParsed.pocket[3], 10)
       }
     };
   }
diff --git a/client/src/variants/Shogi.js b/client/src/variants/Shogi.js
index 521e4680..6c522dc6 100644
--- a/client/src/variants/Shogi.js
+++ b/client/src/variants/Shogi.js
@@ -176,26 +176,27 @@ export class ShogiRules extends ChessRules {
 
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
+    const reserve =
+      V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
     this.reserve = {
       w: {
-        [V.PAWN]: parseInt(fenParsed.reserve[0]),
-        [V.ROOK]: parseInt(fenParsed.reserve[1]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[2]),
-        [V.GOLD_G]: parseInt(fenParsed.reserve[3]),
-        [V.SILVER_G]: parseInt(fenParsed.reserve[4]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[5]),
-        [V.LANCE]: parseInt(fenParsed.reserve[6])
+        [V.PAWN]: reserve[0],
+        [V.ROOK]: reserve[1],
+        [V.BISHOP]: reserve[2],
+        [V.GOLD_G]: reserve[3],
+        [V.SILVER_G]: reserve[4],
+        [V.KNIGHT]: reserve[5],
+        [V.LANCE]: reserve[6]
       },
       b: {
-        [V.PAWN]: parseInt(fenParsed.reserve[7]),
-        [V.ROOK]: parseInt(fenParsed.reserve[8]),
-        [V.BISHOP]: parseInt(fenParsed.reserve[9]),
-        [V.GOLD_G]: parseInt(fenParsed.reserve[10]),
-        [V.SILVER_G]: parseInt(fenParsed.reserve[11]),
-        [V.KNIGHT]: parseInt(fenParsed.reserve[12]),
-        [V.LANCE]: parseInt(fenParsed.reserve[13])
+        [V.PAWN]: reserve[7],
+        [V.ROOK]: reserve[8],
+        [V.BISHOP]: reserve[9],
+        [V.GOLD_G]: reserve[10],
+        [V.SILVER_G]: reserve[11],
+        [V.KNIGHT]: reserve[12],
+        [V.LANCE]: reserve[13]
       }
     };
   }
diff --git a/client/src/variants/Suicide.js b/client/src/variants/Suicide.js
index 2693f84f..ecb2aef1 100644
--- a/client/src/variants/Suicide.js
+++ b/client/src/variants/Suicide.js
@@ -29,7 +29,7 @@ export class SuicideRules extends ChessRules {
           pieces[row[i] == lowerRi ? "b" : "w"]++;
           sumElts++;
         } else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Threechecks.js b/client/src/variants/Threechecks.js
index 175b1246..05250596 100644
--- a/client/src/variants/Threechecks.js
+++ b/client/src/variants/Threechecks.js
@@ -11,7 +11,7 @@ export class ThreechecksRules extends ChessRules {
     this.checkFlags = { w: 0, b: 0 };
     const flags = fenflags.substr(4); //skip first 4 digits, for castle
     for (let c of ["w", "b"]) {
-      this.checkFlags[c] = parseInt(flags.charAt(c == "w" ? 0 : 1));
+      this.checkFlags[c] = parseInt(flags.charAt(c == "w" ? 0 : 1), 10);
     }
   }
 
diff --git a/client/src/variants/Twokings.js b/client/src/variants/Twokings.js
index 52aebc32..d56d0951 100644
--- a/client/src/variants/Twokings.js
+++ b/client/src/variants/Twokings.js
@@ -21,7 +21,7 @@ export class TwokingsRules extends CoregalRules {
         if (['K','k'].includes(row[i])) kings[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/variants/Wormhole.js b/client/src/variants/Wormhole.js
index cc9fd497..1f3ecd2a 100644
--- a/client/src/variants/Wormhole.js
+++ b/client/src/variants/Wormhole.js
@@ -39,7 +39,7 @@ export class WormholeRules extends ChessRules {
         if (['K','k'].includes(row[i])) kings[row[i]]++;
         if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
         else {
-          const num = parseInt(row[i]);
+          const num = parseInt(row[i], 10);
           if (isNaN(num)) return false;
           sumElts += num;
         }
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index 6722f334..cd781a5d 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -1285,7 +1285,10 @@ console.log(data.data);
     //  - from server (one correspondance game I play[ed] or not)
     //  - from remote peer (one live game I don't play, finished or not)
     fetchGame: function(callback) {
-      if (Number.isInteger(this.gameRef) || !isNaN(parseInt(this.gameRef))) {
+      if (
+        Number.isInteger(this.gameRef) ||
+        !isNaN(parseInt(this.gameRef, 10))
+      ) {
         // corr games identifiers are integers
         ajax(
           "/games",
diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue
index 146beb01..451299b7 100644
--- a/client/src/views/Hall.vue
+++ b/client/src/views/Hall.vue
@@ -240,12 +240,12 @@ export default {
       infoMessage: "",
       newchallenge: {
         fen: "",
-        vid: parseInt(localStorage.getItem("vid")) || 0,
+        vid: parseInt(localStorage.getItem("vid"), 10) || 0,
         to: "", //name of challenged player (if any)
         cadence: localStorage.getItem("cadence") || "",
         randomness:
           // Warning: randomness can be 0, then !!randomness is false
-          (parseInt(localStorage.getItem("challRandomness"))+1 || 3) - 1,
+          (parseInt(localStorage.getItem("challRandomness"),10)+1 || 3) - 1,
         // VariantRules object, stored to not interfere with
         // diagrams of targetted challenges:
         V: null,
diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue
index 6835b556..5c3522ec 100644
--- a/client/src/views/MyGames.vue
+++ b/client/src/views/MyGames.vue
@@ -244,7 +244,7 @@ export default {
         case "notifyturn":
         case "notifyscore": {
           const info = data.data;
-          const type = (!!parseInt(info.gid) ? "corr" : "live");
+          const type = (!!parseInt(info.gid, 10) ? "corr" : "live");
           let game = gamesArrays[type].find(g => g.id == info.gid);
           // "notifything" --> "thing":
           const thing = data.code.substr(6);
diff --git a/server/db/populate.sql b/server/db/populate.sql
index 703297ae..60578f83 100644
--- a/server/db/populate.sql
+++ b/server/db/populate.sql
@@ -43,7 +43,6 @@ insert or ignore into Variants (name, description) values
   ('Doublearmy', '64 pieces on the board'),
   ('Doublemove1', 'Double moves (v1)'),
   ('Doublemove2', 'Double moves (v2)'),
-  ('Doubleorda', 'Mongolian Horde (v2)'),
   ('Dynamo', 'Push and pull'),
   ('Eightpieces', 'Each piece is unique'),
   ('Enpassant', 'Capture en passant'),
@@ -73,6 +72,7 @@ insert or ignore into Variants (name, description) values
   ('Monster', 'White move twice'),
   ('Omega', 'A wizard in the corner'),
   ('Orda', 'Mongolian Horde (v1)'),
+  ('Ordamirror', 'Mongolian Horde (v2)'),
   ('Pacifist1', 'Convert & support (v1)'),
   ('Pacifist2', 'Convert & support (v2)'),
   ('Parachute', 'Landing on the board'),
-- 
2.44.0