From 6808d7a16ec1e761c6a2dffec2281c96953e4d89 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 18 Feb 2020 15:30:28 +0100
Subject: [PATCH] Started code review + some fixes (unfinished)

---
 client/package.json                     |   31 +-
 client/src/App.vue                      |   13 +-
 client/src/base_rules.js                | 1203 +++++++++++------------
 client/src/components/BaseGame.vue      |  322 +++---
 client/src/components/Board.vue         |  403 ++++----
 client/src/components/ChallengeList.vue |   23 +-
 client/src/components/Chat.vue          |   24 +-
 client/src/components/ComputerGame.vue  |   85 +-
 client/src/components/ContactForm.vue   |   31 +-
 client/src/components/GameList.vue      |   78 +-
 client/src/components/Language.vue      |    9 +-
 client/src/components/MoveList.vue      |  116 +--
 client/src/components/Settings.vue      |   18 +-
 client/src/components/UpsertUser.vue    |   71 +-
 client/src/data/challengeCheck.js       |   29 +-
 client/src/data/problemCheck.js         |   11 +-
 client/src/data/userCheck.js            |   22 +-
 client/src/main.js                      |   14 +-
 client/src/playCompMove.js              |   17 +-
 client/src/router.js                    |   30 +-
 client/src/store.js                     |   39 +-
 client/src/translations/en.js           |  115 +--
 client/src/translations/es.js           |  119 +--
 client/src/translations/fr.js           |  128 +--
 client/src/utils/ajax.js                |   53 +-
 client/src/utils/alea.js                |   28 +-
 client/src/utils/array.js               |   28 +-
 client/src/utils/cookie.js              |   20 +-
 client/src/utils/datetime.js            |   55 +-
 client/src/utils/gameStorage.js         |  141 ++-
 client/src/utils/modalClick.js          |    6 +-
 client/src/utils/printDiagram.js        |   78 +-
 client/src/utils/scoring.js             |    3 +-
 client/src/utils/squareId.js            |   10 +-
 client/src/utils/timeControl.js         |   49 +-
 client/src/variants/Alice.js            |  320 +++---
 client/src/variants/Antiking.js         |  218 ++--
 client/src/variants/Atomic.js           |  168 ++--
 client/src/variants/Baroque.js          |  618 ++++++------
 client/src/variants/Berolina.js         |  137 ++-
 client/src/variants/Checkered.js        |  359 ++++---
 client/src/variants/Chess960.js         |    5 +-
 client/src/variants/Crazyhouse.js       |  243 ++---
 client/src/variants/Dark.js             |  223 ++---
 client/src/variants/Extinction.js       |  117 ++-
 client/src/variants/Grand.js            |  352 ++++---
 client/src/variants/Losers.js           |  186 ++--
 client/src/variants/Magnetic.js         |  186 ++--
 client/src/variants/Marseille.js        |  264 +++--
 client/src/variants/Upsidedown.js       |   66 +-
 client/src/variants/Wildebeest.js       |  307 +++---
 client/src/variants/Zen.js              |  211 ++--
 client/src/views/About.vue              |   17 +-
 client/src/views/Analyse.vue            |   29 +-
 client/src/views/Auth.vue               |   38 +-
 client/src/views/Game.vue               |  566 ++++++-----
 client/src/views/Hall.vue               |  579 ++++++-----
 client/src/views/Logout.vue             |    6 +-
 client/src/views/MyGames.vue            |   30 +-
 client/src/views/News.vue               |   94 +-
 client/src/views/Problems.vue           |  163 ++-
 client/src/views/Rules.vue              |   54 +-
 client/src/views/Variants.vue           |   38 +-
 client/vue.config.js                    |    6 +-
 64 files changed, 4338 insertions(+), 4684 deletions(-)

diff --git a/client/package.json b/client/package.json
index 242acdc2..88feedb9 100644
--- a/client/package.json
+++ b/client/package.json
@@ -15,7 +15,6 @@
   "devDependencies": {
     "@vue/cli-plugin-eslint": "^3.12.1",
     "@vue/cli-service": "^4.2.2",
-    "@vue/eslint-config-prettier": "^4.0.1",
     "ajv": "^6.11.0",
     "apply-loader": "^2.0.0",
     "babel-eslint": "^10.0.3",
@@ -39,11 +38,39 @@
     },
     "extends": [
       "plugin:vue/essential",
-      "@vue/prettier"
+      "eslint:recommended"
     ],
     "rules": {},
     "parserOptions": {
       "parser": "babel-eslint"
+    },
+    "globals": {
+      "V": "readonly"
+    },
+    "rules": {
+      "consistent-return": 2,
+      "indent": [
+        "error",
+        2,
+        {
+          "SwitchCase": 1,
+          "VariableDeclarator": "first",
+          "FunctionExpression": {
+            "parameters": "first"
+          },
+          "CallExpression": {
+            "arguments": "first"
+          },
+          "flatTernaryExpressions": true
+        }
+      ],
+      "no-else-return"   : [
+        1,
+        {
+          "allowElseIf": false
+        }
+      ],
+      "semi"             : [1, "always"]
     }
   },
   "postcss": {
diff --git a/client/src/App.vue b/client/src/App.vue
index 012b1c2b..9879e275 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -53,17 +53,17 @@ export default {
     ContactForm,
     Language,
     Settings,
-    UpsertUser,
+    UpsertUser
   },
   data: function() {
     return {
-      st: store.state,
+      st: store.state
     };
   },
   computed: {
     flagImage: function() {
       return `/images/flags/${this.st.lang}.svg`;
-    },
+    }
   },
   mounted: function() {
     let dialogs = document.querySelectorAll("div[role='dialog']");
@@ -73,12 +73,11 @@ export default {
   },
   methods: {
     hideDrawer: function(e) {
-      if (e.target.innerText == "Forum")
-        return; //external link
+      if (e.target.innerText == "Forum") return; //external link
       e.preventDefault(); //TODO: why is this needed?
       document.getElementsByClassName("drawer")[0].checked = false;
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/base_rules.js b/client/src/base_rules.js
index 924f7374..056f4774 100644
--- a/client/src/base_rules.js
+++ b/client/src/base_rules.js
@@ -4,71 +4,65 @@
 import { ArrayFun } from "@/utils/array";
 import { randInt, shuffle } from "@/utils/alea";
 
-export const PiPo = class PiPo //Piece+Position
-{
+export const PiPo = class PiPo {
+  //Piece+Position
   // o: {piece[p], color[c], posX[x], posY[y]}
-  constructor(o)
-  {
+  constructor(o) {
     this.p = o.p;
     this.c = o.c;
     this.x = o.x;
     this.y = o.y;
   }
-}
+};
 
 // TODO: for animation, moves should contains "moving" and "fading" maybe...
-export const Move = class Move
-{
+export const Move = class Move {
   // o: {appear, vanish, [start,] [end,]}
   // appear,vanish = arrays of PiPo
   // start,end = coordinates to apply to trigger move visually (think castle)
-  constructor(o)
-  {
+  constructor(o) {
     this.appear = o.appear;
     this.vanish = o.vanish;
-    this.start = !!o.start ? o.start : {x:o.vanish[0].x, y:o.vanish[0].y};
-    this.end = !!o.end ? o.end : {x:o.appear[0].x, y:o.appear[0].y};
+    this.start = o.start ? o.start : { x: o.vanish[0].x, y: o.vanish[0].y };
+    this.end = o.end ? o.end : { x: o.appear[0].x, y: o.appear[0].y };
   }
-}
+};
 
 // NOTE: x coords = top to bottom; y = left to right (from white player perspective)
-export const ChessRules = class ChessRules
-{
+export const ChessRules = class ChessRules {
   //////////////
   // MISC UTILS
 
-  static get HasFlags() { return true; } //some variants don't have flags
+  static get HasFlags() {
+    return true;
+  } //some variants don't have flags
 
-  static get HasEnpassant() { return true; } //some variants don't have ep.
+  static get HasEnpassant() {
+    return true;
+  } //some variants don't have ep.
 
   // Path to pieces
-  static getPpath(b)
-  {
+  static getPpath(b) {
     return b; //usual pieces in pieces/ folder
   }
 
   // Turn "wb" into "B" (for FEN)
-  static board2fen(b)
-  {
-    return (b[0]=='w' ? b[1].toUpperCase() : b[1]);
+  static board2fen(b) {
+    return b[0] == "w" ? b[1].toUpperCase() : b[1];
   }
 
   // Turn "p" into "bp" (for board)
-  static fen2board(f)
-  {
-    return (f.charCodeAt()<=90 ? "w"+f.toLowerCase() : "b"+f);
+  static fen2board(f) {
+    return f.charCodeAt() <= 90 ? "w" + f.toLowerCase() : "b" + f;
   }
 
   // Check if FEN describe a board situation correctly
-  static IsGoodFen(fen)
-  {
+  static IsGoodFen(fen) {
     const fenParsed = V.ParseFen(fen);
     // 1) Check position
-    if (!V.IsGoodPosition(fenParsed.position))
-      return false;
+    if (!V.IsGoodPosition(fenParsed.position)) return false;
     // 2) Check turn
-    if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn))
-      return false;
+    if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn)) return false;
     // 3) Check moves count
     if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount) >= 0))
       return false;
@@ -76,81 +70,65 @@ export const ChessRules = class ChessRules
     if (V.HasFlags && (!fenParsed.flags || !V.IsGoodFlags(fenParsed.flags)))
       return false;
     // 5) Check enpassant
-    if (V.HasEnpassant &&
-      (!fenParsed.enpassant || !V.IsGoodEnpassant(fenParsed.enpassant)))
-    {
+    if (
+      V.HasEnpassant &&
+      (!fenParsed.enpassant || !V.IsGoodEnpassant(fenParsed.enpassant))
+    ) {
       return false;
     }
     return true;
   }
 
   // Is position part of the FEN a priori correct?
-  static IsGoodPosition(position)
-  {
-    if (position.length == 0)
-      return false;
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
     const rows = position.split("/");
-    if (rows.length != V.size.x)
-      return false;
-    for (let row of rows)
-    {
+    if (rows.length != V.size.x) return false;
+    for (let row of rows) {
       let sumElts = 0;
-      for (let i=0; i<row.length; i++)
-      {
-        if (V.PIECES.includes(row[i].toLowerCase()))
-          sumElts++;
-        else
-        {
+      for (let i = 0; i < row.length; i++) {
+        if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
+        else {
           const num = parseInt(row[i]);
-          if (isNaN(num))
-            return false;
+          if (isNaN(num)) return false;
           sumElts += num;
         }
       }
-      if (sumElts != V.size.y)
-        return false;
+      if (sumElts != V.size.y) return false;
     }
     return true;
   }
 
   // For FEN checking
-  static IsGoodTurn(turn)
-  {
-    return ["w","b"].includes(turn);
+  static IsGoodTurn(turn) {
+    return ["w", "b"].includes(turn);
   }
 
   // For FEN checking
-  static IsGoodFlags(flags)
-  {
+  static IsGoodFlags(flags) {
     return !!flags.match(/^[01]{4,4}$/);
   }
 
-  static IsGoodEnpassant(enpassant)
-  {
-    if (enpassant != "-")
-    {
-      const ep = V.SquareToCoords(fenParsed.enpassant);
-      if (isNaN(ep.x) || !V.OnBoard(ep))
-        return false;
+  static IsGoodEnpassant(enpassant) {
+    if (enpassant != "-") {
+      const ep = V.SquareToCoords(enpassant);
+      if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
     }
     return true;
   }
 
   // 3 --> d (column number to letter)
-  static CoordToColumn(colnum)
-  {
+  static CoordToColumn(colnum) {
     return String.fromCharCode(97 + colnum);
   }
 
   // d --> 3 (column letter to number)
-  static ColumnToCoord(column)
-  {
+  static ColumnToCoord(column) {
     return column.charCodeAt(0) - 97;
   }
 
   // a4 --> {x:3,y:0}
-  static SquareToCoords(sq)
-  {
+  static SquareToCoords(sq) {
     return {
       // NOTE: column is always one char => max 26 columns
       // row is counted from black side => subtraction
@@ -160,44 +138,40 @@ export const ChessRules = class ChessRules
   }
 
   // {x:0,y:4} --> e8
-  static CoordsToSquare(coords)
-  {
+  static CoordsToSquare(coords) {
     return V.CoordToColumn(coords.y) + (V.size.x - coords.x);
   }
 
   // Aggregates flags into one object
-  aggregateFlags()
-  {
+  aggregateFlags() {
     return this.castleFlags;
   }
 
   // Reverse operation
-  disaggregateFlags(flags)
-  {
+  disaggregateFlags(flags) {
     this.castleFlags = flags;
   }
 
   // En-passant square, if any
-  getEpSquare(moveOrSquare)
-  {
-    if (!moveOrSquare)
-      return undefined;
-    if (typeof moveOrSquare === "string")
-    {
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
       const square = moveOrSquare;
-      if (square == "-")
-        return undefined;
+      if (square == "-") return undefined;
       return V.SquareToCoords(square);
     }
     // Argument is a move:
     const move = moveOrSquare;
-    const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
+    const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
     // NOTE: next conditions are first for Atomic, and last for Checkered
-    if (move.appear.length > 0 && Math.abs(sx - ex) == 2
-      && move.appear[0].p == V.PAWN && ["w","b"].includes(move.appear[0].c))
-    {
+    if (
+      move.appear.length > 0 &&
+      Math.abs(sx - ex) == 2 &&
+      move.appear[0].p == V.PAWN &&
+      ["w", "b"].includes(move.appear[0].c)
+    ) {
       return {
-        x: (sx + ex)/2,
+        x: (sx + ex) / 2,
         y: sy
       };
     }
@@ -205,26 +179,22 @@ export const ChessRules = class ChessRules
   }
 
   // Can thing on square1 take thing on square2
-  canTake([x1,y1], [x2,y2])
-  {
-    return this.getColor(x1,y1) !== this.getColor(x2,y2);
+  canTake([x1, y1], [x2, y2]) {
+    return this.getColor(x1, y1) !== this.getColor(x2, y2);
   }
 
   // Is (x,y) on the chessboard?
-  static OnBoard(x,y)
-  {
-    return (x>=0 && x<V.size.x && y>=0 && y<V.size.y);
+  static OnBoard(x, y) {
+    return x >= 0 && x < V.size.x && y >= 0 && y < V.size.y;
   }
 
   // Used in interface: 'side' arg == player color
-  canIplay(side, [x,y])
-  {
-    return (this.turn == side && this.getColor(x,y) == side);
+  canIplay(side, [x, y]) {
+    return this.turn == side && this.getColor(x, y) == side;
   }
 
   // On which squares is color under check ? (for interface)
-  getCheckSquares(color)
-  {
+  getCheckSquares(color) {
     return this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
       ? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate!
       : [];
@@ -234,12 +204,10 @@ export const ChessRules = class ChessRules
   // FEN UTILS
 
   // Setup the initial random (assymetric) position
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(8), "b": new Array(8) };
+  static GenRandInitFen() {
+    let pieces = { w: new Array(8), b: new Array(8) };
     // Shuffle pieces on first and last rank
-    for (let c of ["w","b"])
-    {
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(8);
 
       // Get random squares for bishops
@@ -249,8 +217,8 @@ export const ChessRules = class ChessRules
       let randIndex_tmp = 2 * randInt(4) + 1;
       const bishop2Pos = positions[randIndex_tmp];
       // Remove chosen squares
-      positions.splice(Math.max(randIndex,randIndex_tmp), 1);
-      positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       // Get random squares for knights
       randIndex = randInt(6);
@@ -272,63 +240,59 @@ export const ChessRules = class ChessRules
       const rook2Pos = positions[2];
 
       // 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][kingPos] = 'k';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][rook2Pos] = 'r';
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
     }
-    return pieces["b"].join("") +
+    return (
+      pieces["b"].join("") +
       "/pppppppp/8/8/8/8/PPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0 1111 -"; //add turn + flags + enpassant
+      " w 0 1111 -"
+    ); //add turn + flags + enpassant
   }
 
   // "Parse" FEN: just return untransformed string data
-  static ParseFen(fen)
-  {
+  static ParseFen(fen) {
     const fenParts = fen.split(" ");
-    let res =
-    {
+    let res = {
       position: fenParts[0],
       turn: fenParts[1],
-      movesCount: fenParts[2],
+      movesCount: fenParts[2]
     };
     let nextIdx = 3;
-    if (V.HasFlags)
-      Object.assign(res, {flags: fenParts[nextIdx++]});
-    if (V.HasEnpassant)
-      Object.assign(res, {enpassant: fenParts[nextIdx]});
+    if (V.HasFlags) Object.assign(res, { flags: fenParts[nextIdx++] });
+    if (V.HasEnpassant) Object.assign(res, { enpassant: fenParts[nextIdx] });
     return res;
   }
 
   // Return current fen (game state)
-  getFen()
-  {
-    return this.getBaseFen() + " " +
-      this.getTurnFen() + " " + this.movesCount +
-      (V.HasFlags ? (" " + this.getFlagsFen()) : "") +
-      (V.HasEnpassant ? (" " + this.getEnpassantFen()) : "");
+  getFen() {
+    return (
+      this.getBaseFen() +
+      " " +
+      this.getTurnFen() +
+      " " +
+      this.movesCount +
+      (V.HasFlags ? " " + this.getFlagsFen() : "") +
+      (V.HasEnpassant ? " " + this.getEnpassantFen() : "")
+    );
   }
 
   // Position part of the FEN string
-  getBaseFen()
-  {
+  getBaseFen() {
     let position = "";
-    for (let i=0; i<V.size.x; i++)
-    {
+    for (let i = 0; i < V.size.x; i++) {
       let emptyCount = 0;
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] == V.EMPTY)
-          emptyCount++;
-        else
-        {
-          if (emptyCount > 0)
-          {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] == V.EMPTY) emptyCount++;
+        else {
+          if (emptyCount > 0) {
             // Add empty squares in-between
             position += emptyCount;
             emptyCount = 0;
@@ -336,87 +300,72 @@ export const ChessRules = class ChessRules
           position += V.board2fen(this.board[i][j]);
         }
       }
-      if (emptyCount > 0)
-      {
+      if (emptyCount > 0) {
         // "Flush remainder"
         position += emptyCount;
       }
-      if (i < V.size.x - 1)
-        position += "/"; //separate rows
+      if (i < V.size.x - 1) position += "/"; //separate rows
     }
     return position;
   }
 
-  getTurnFen()
-  {
+  getTurnFen() {
     return this.turn;
   }
 
   // Flags part of the FEN string
-  getFlagsFen()
-  {
+  getFlagsFen() {
     let flags = "";
     // Add castling flags
-    for (let i of ['w','b'])
-    {
-      for (let j=0; j<2; j++)
-        flags += (this.castleFlags[i][j] ? '1' : '0');
+    for (let i of ["w", "b"]) {
+      for (let j = 0; j < 2; j++) flags += this.castleFlags[i][j] ? "1" : "0";
     }
     return flags;
   }
 
   // Enpassant part of the FEN string
-  getEnpassantFen()
-  {
+  getEnpassantFen() {
     const L = this.epSquares.length;
-    if (!this.epSquares[L-1])
-      return "-"; //no en-passant
-    return V.CoordsToSquare(this.epSquares[L-1]);
+    if (!this.epSquares[L - 1]) return "-"; //no en-passant
+    return V.CoordsToSquare(this.epSquares[L - 1]);
   }
 
   // Turn position fen into double array ["wb","wp","bk",...]
-  static GetBoard(position)
-  {
+  static GetBoard(position) {
     const rows = position.split("/");
     let board = ArrayFun.init(V.size.x, V.size.y, "");
-    for (let i=0; i<rows.length; i++)
-    {
+    for (let i = 0; i < rows.length; i++) {
       let j = 0;
-      for (let indexInRow = 0; indexInRow < rows[i].length; indexInRow++)
-      {
+      for (let indexInRow = 0; indexInRow < rows[i].length; indexInRow++) {
         const character = rows[i][indexInRow];
         const num = parseInt(character);
-        if (!isNaN(num))
-          j += num; //just shift j
-        else //something at position i,j
-          board[i][j++] = V.fen2board(character);
+        if (!isNaN(num)) j += num;
+        //just shift j
+        //something at position i,j
+        else board[i][j++] = V.fen2board(character);
       }
     }
     return board;
   }
 
   // Extract (relevant) flags from fen
-  setFlags(fenflags)
-  {
+  setFlags(fenflags) {
     // white a-castle, h-castle, black a-castle, h-castle
-    this.castleFlags = {'w': [true,true], 'b': [true,true]};
-    if (!fenflags)
-      return;
-    for (let i=0; i<4; i++)
-      this.castleFlags[i < 2 ? 'w' : 'b'][i%2] = (fenflags.charAt(i) == '1');
+    this.castleFlags = { w: [true, true], b: [true, true] };
+    if (!fenflags) return;
+    for (let i = 0; i < 4; i++)
+      this.castleFlags[i < 2 ? "w" : "b"][i % 2] = fenflags.charAt(i) == "1";
   }
 
   //////////////////
   // INITIALIZATION
 
-  constructor(fen)
-  {
+  constructor(fen) {
     this.re_init(fen);
   }
 
   // Fen string fully describes the game state
-  re_init(fen)
-  {
+  re_init(fen) {
     const fenParsed = V.ParseFen(fen);
     this.board = V.GetBoard(fenParsed.position);
     this.turn = fenParsed.turn[0]; //[0] to work with MarseilleRules
@@ -425,43 +374,35 @@ export const ChessRules = class ChessRules
   }
 
   // Scan board for kings and rooks positions
-  scanKingsRooks(fen)
-  {
-    this.INIT_COL_KING = {'w':-1, 'b':-1};
-    this.INIT_COL_ROOK = {'w':[-1,-1], 'b':[-1,-1]};
-    this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; //squares of white and black king
+  scanKingsRooks(fen) {
+    this.INIT_COL_KING = { w: -1, b: -1 };
+    this.INIT_COL_ROOK = { w: [-1, -1], b: [-1, -1] };
+    this.kingPos = { w: [-1, -1], b: [-1, -1] }; //squares of white and black king
     const fenRows = V.ParseFen(fen).position.split("/");
-    for (let i=0; i<fenRows.length; i++)
-    {
+    for (let i = 0; i < fenRows.length; i++) {
       let k = 0; //column index on board
-      for (let j=0; j<fenRows[i].length; j++)
-      {
-        switch (fenRows[i].charAt(j))
-        {
-          case 'k':
-            this.kingPos['b'] = [i,k];
-            this.INIT_COL_KING['b'] = k;
+      for (let j = 0; j < fenRows[i].length; j++) {
+        switch (fenRows[i].charAt(j)) {
+          case "k":
+            this.kingPos["b"] = [i, k];
+            this.INIT_COL_KING["b"] = k;
             break;
-          case 'K':
-            this.kingPos['w'] = [i,k];
-            this.INIT_COL_KING['w'] = k;
+          case "K":
+            this.kingPos["w"] = [i, k];
+            this.INIT_COL_KING["w"] = k;
             break;
-          case 'r':
-            if (this.INIT_COL_ROOK['b'][0] < 0)
-              this.INIT_COL_ROOK['b'][0] = k;
-            else
-              this.INIT_COL_ROOK['b'][1] = k;
+          case "r":
+            if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k;
+            else this.INIT_COL_ROOK["b"][1] = k;
             break;
-          case 'R':
-            if (this.INIT_COL_ROOK['w'][0] < 0)
-              this.INIT_COL_ROOK['w'][0] = k;
-            else
-              this.INIT_COL_ROOK['w'][1] = k;
+          case "R":
+            if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k;
+            else this.INIT_COL_ROOK["w"][1] = k;
             break;
-          default:
+          default: {
             const num = parseInt(fenRows[i].charAt(j));
-            if (!isNaN(num))
-              k += (num-1);
+            if (!isNaN(num)) k += num - 1;
+          }
         }
         k++;
       }
@@ -469,18 +410,16 @@ export const ChessRules = class ChessRules
   }
 
   // Some additional variables from FEN (variant dependant)
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     // Set flags and enpassant:
     const parsedFen = V.ParseFen(fen);
-    if (V.HasFlags)
-      this.setFlags(parsedFen.flags);
-    if (V.HasEnpassant)
-    {
-      const epSq = parsedFen.enpassant != "-"
-        ? V.SquareToCoords(parsedFen.enpassant)
-        : undefined;
-      this.epSquares = [ epSq ];
+    if (V.HasFlags) this.setFlags(parsedFen.flags);
+    if (V.HasEnpassant) {
+      const epSq =
+        parsedFen.enpassant != "-"
+          ? V.SquareToCoords(parsedFen.enpassant)
+          : undefined;
+      this.epSquares = [epSq];
     }
     // Search for king and rooks positions:
     this.scanKingsRooks(fen);
@@ -489,53 +428,80 @@ export const ChessRules = class ChessRules
   /////////////////////
   // GETTERS & SETTERS
 
-  static get size()
-  {
-    return {x:8, y:8};
+  static get size() {
+    return { x: 8, y: 8 };
   }
 
   // Color of thing on suqare (i,j). 'undefined' if square is empty
-  getColor(i,j)
-  {
+  getColor(i, j) {
     return this.board[i][j].charAt(0);
   }
 
   // Piece type on square (i,j). 'undefined' if square is empty
-  getPiece(i,j)
-  {
+  getPiece(i, j) {
     return this.board[i][j].charAt(1);
   }
 
   // Get opponent color
-  static GetOppCol(color)
-  {
-    return (color=="w" ? "b" : "w");
+  static GetOppCol(color) {
+    return color == "w" ? "b" : "w";
   }
 
   // Pieces codes (for a clearer code)
-  static get PAWN() { return 'p'; }
-  static get ROOK() { return 'r'; }
-  static get KNIGHT() { return 'n'; }
-  static get BISHOP() { return 'b'; }
-  static get QUEEN() { return 'q'; }
-  static get KING() { return 'k'; }
+  static get PAWN() {
+    return "p";
+  }
+  static get ROOK() {
+    return "r";
+  }
+  static get KNIGHT() {
+    return "n";
+  }
+  static get BISHOP() {
+    return "b";
+  }
+  static get QUEEN() {
+    return "q";
+  }
+  static get KING() {
+    return "k";
+  }
 
   // For FEN checking:
-  static get PIECES()
-  {
-    return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.KING];
+  static get PIECES() {
+    return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
   }
 
   // Empty square
-  static get EMPTY() { return ""; }
+  static get EMPTY() {
+    return "";
+  }
 
   // Some pieces movements
-  static get steps()
-  {
+  static get steps() {
     return {
-      'r': [ [-1,0],[1,0],[0,-1],[0,1] ],
-      'n': [ [-1,-2],[-1,2],[1,-2],[1,2],[-2,-1],[-2,1],[2,-1],[2,1] ],
-      'b': [ [-1,-1],[-1,1],[1,-1],[1,1] ],
+      r: [
+        [-1, 0],
+        [1, 0],
+        [0, -1],
+        [0, 1]
+      ],
+      n: [
+        [-1, -2],
+        [-1, 2],
+        [1, -2],
+        [1, 2],
+        [-2, -1],
+        [-2, 1],
+        [2, -1],
+        [2, 1]
+      ],
+      b: [
+        [-1, -1],
+        [-1, 1],
+        [1, -1],
+        [1, 1]
+      ]
     };
   }
 
@@ -543,57 +509,54 @@ export const ChessRules = class ChessRules
   // MOVES GENERATION
 
   // All possible moves from selected square (assumption: color is OK)
-  getPotentialMovesFrom([x,y])
-  {
-    switch (this.getPiece(x,y))
-    {
+  getPotentialMovesFrom([x, y]) {
+    switch (this.getPiece(x, y)) {
       case V.PAWN:
-        return this.getPotentialPawnMoves([x,y]);
+        return this.getPotentialPawnMoves([x, y]);
       case V.ROOK:
-        return this.getPotentialRookMoves([x,y]);
+        return this.getPotentialRookMoves([x, y]);
       case V.KNIGHT:
-        return this.getPotentialKnightMoves([x,y]);
+        return this.getPotentialKnightMoves([x, y]);
       case V.BISHOP:
-        return this.getPotentialBishopMoves([x,y]);
+        return this.getPotentialBishopMoves([x, y]);
       case V.QUEEN:
-        return this.getPotentialQueenMoves([x,y]);
+        return this.getPotentialQueenMoves([x, y]);
       case V.KING:
-        return this.getPotentialKingMoves([x,y]);
+        return this.getPotentialKingMoves([x, y]);
     }
+    return []; //never reached
   }
 
   // Build a regular move from its initial and destination squares.
   // tr: transformation
-  getBasicMove([sx,sy], [ex,ey], tr)
-  {
+  getBasicMove([sx, sy], [ex, ey], tr) {
     let mv = new Move({
       appear: [
         new PiPo({
           x: ex,
           y: ey,
-          c: !!tr ? tr.c : this.getColor(sx,sy),
-          p: !!tr ? tr.p : this.getPiece(sx,sy)
+          c: tr ? tr.c : this.getColor(sx, sy),
+          p: tr ? tr.p : this.getPiece(sx, sy)
         })
       ],
       vanish: [
         new PiPo({
           x: sx,
           y: sy,
-          c: this.getColor(sx,sy),
-          p: this.getPiece(sx,sy)
+          c: this.getColor(sx, sy),
+          p: this.getPiece(sx, sy)
         })
       ]
     });
 
     // The opponent piece disappears if we take it
-    if (this.board[ex][ey] != V.EMPTY)
-    {
+    if (this.board[ex][ey] != V.EMPTY) {
       mv.vanish.push(
         new PiPo({
           x: ex,
           y: ey,
-          c: this.getColor(ex,ey),
-          p: this.getPiece(ex,ey)
+          c: this.getColor(ex, ey),
+          p: this.getPiece(ex, ey)
         })
       );
     }
@@ -602,92 +565,94 @@ export const ChessRules = class ChessRules
 
   // Generic method to find possible moves of non-pawn pieces:
   // "sliding or jumping"
-  getSlideNJumpMoves([x,y], steps, oneStep)
-  {
-    const color = this.getColor(x,y);
+  getSlideNJumpMoves([x, y], steps, oneStep) {
     let moves = [];
-    outerLoop:
-    for (let step of steps)
-    {
+    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 !== undefined)
-          continue outerLoop;
+      while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+        moves.push(this.getBasicMove([x, y], [i, j]));
+        if (oneStep !== undefined) continue outerLoop;
         i += step[0];
         j += step[1];
       }
-      if (V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
-        moves.push(this.getBasicMove([x,y], [i,j]));
+      if (V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
+        moves.push(this.getBasicMove([x, y], [i, j]));
     }
     return moves;
   }
 
   // What are the pawn moves from square x,y ?
-  getPotentialPawnMoves([x,y])
-  {
+  getPotentialPawnMoves([x, y]) {
     const color = this.turn;
     let moves = [];
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
-    const shiftX = (color == "w" ? -1 : 1);
-    const firstRank = (color == 'w' ? sizeX-1 : 0);
-    const startRank = (color == "w" ? sizeX-2 : 1);
-    const lastRank = (color == "w" ? 0 : sizeX-1);
-    const pawnColor = this.getColor(x,y); //can be different for checkered
+    const [sizeX, sizeY] = [V.size.x, V.size.y];
+    const shiftX = color == "w" ? -1 : 1;
+    const firstRank = color == "w" ? sizeX - 1 : 0;
+    const startRank = color == "w" ? sizeX - 2 : 1;
+    const lastRank = color == "w" ? 0 : sizeX - 1;
+    const pawnColor = this.getColor(x, y); //can be different for checkered
 
     // NOTE: next condition is generally true (no pawn on last rank)
-    if (x+shiftX >= 0 && x+shiftX < sizeX)
-    {
-      const finalPieces = x + shiftX == lastRank
-        ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
-        : [V.PAWN]
+    if (x + shiftX >= 0 && x + shiftX < sizeX) {
+      const finalPieces =
+        x + shiftX == lastRank
+          ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
+          : [V.PAWN];
       // One square forward
-      if (this.board[x+shiftX][y] == V.EMPTY)
-      {
-        for (let piece of finalPieces)
-        {
-          moves.push(this.getBasicMove([x,y], [x+shiftX,y],
-            {c:pawnColor,p:piece}));
+      if (this.board[x + shiftX][y] == V.EMPTY) {
+        for (let piece of finalPieces) {
+          moves.push(
+            this.getBasicMove([x, y], [x + shiftX, y], {
+              c: pawnColor,
+              p: piece
+            })
+          );
         }
         // Next condition because pawns on 1st rank can generally jump
-        if ([startRank,firstRank].includes(x)
-          && this.board[x+2*shiftX][y] == V.EMPTY)
-        {
+        if (
+          [startRank, firstRank].includes(x) &&
+          this.board[x + 2 * shiftX][y] == V.EMPTY
+        ) {
           // Two squares jump
-          moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+          moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
         }
       }
       // Captures
-      for (let shiftY of [-1,1])
-      {
-        if (y + shiftY >= 0 && y + shiftY < sizeY
-          && this.board[x+shiftX][y+shiftY] != V.EMPTY
-          && this.canTake([x,y], [x+shiftX,y+shiftY]))
-        {
-          for (let piece of finalPieces)
-          {
-            moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
-              {c:pawnColor,p:piece}));
+      for (let shiftY of [-1, 1]) {
+        if (
+          y + shiftY >= 0 &&
+          y + shiftY < sizeY &&
+          this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+          this.canTake([x, y], [x + shiftX, y + shiftY])
+        ) {
+          for (let piece of finalPieces) {
+            moves.push(
+              this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+                c: pawnColor,
+                p: piece
+              })
+            );
           }
         }
       }
     }
 
-    if (V.HasEnpassant)
-    {
+    if (V.HasEnpassant) {
       // En passant
       const Lep = this.epSquares.length;
-      const epSquare = this.epSquares[Lep-1]; //always at least one element
-      if (!!epSquare && epSquare.x == x+shiftX && Math.abs(epSquare.y - y) == 1)
-      {
-        let enpassantMove = this.getBasicMove([x,y], [epSquare.x,epSquare.y]);
+      const epSquare = this.epSquares[Lep - 1]; //always at least one element
+      if (
+        !!epSquare &&
+        epSquare.x == x + shiftX &&
+        Math.abs(epSquare.y - y) == 1
+      ) {
+        let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
         enpassantMove.vanish.push({
           x: x,
           y: epSquare.y,
-          p: 'p',
-          c: this.getColor(x,epSquare.y)
+          p: "p",
+          c: this.getColor(x, epSquare.y)
         });
         moves.push(enpassantMove);
       }
@@ -697,106 +662,112 @@ export const ChessRules = class ChessRules
   }
 
   // What are the rook moves from square x,y ?
-  getPotentialRookMoves(sq)
-  {
+  getPotentialRookMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
   }
 
   // What are the knight moves from square x,y ?
-  getPotentialKnightMoves(sq)
-  {
+  getPotentialKnightMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
   }
 
   // What are the bishop moves from square x,y ?
-  getPotentialBishopMoves(sq)
-  {
+  getPotentialBishopMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
   }
 
   // What are the queen moves from square x,y ?
-  getPotentialQueenMoves(sq)
-  {
-    return this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+  getPotentialQueenMoves(sq) {
+    return this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+    );
   }
 
   // What are the king moves from square x,y ?
-  getPotentialKingMoves(sq)
-  {
+  getPotentialKingMoves(sq) {
     // Initialize with normal moves
-    let moves = this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+    let moves = this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
     return moves.concat(this.getCastleMoves(sq));
   }
 
-  getCastleMoves([x,y])
-  {
-    const c = this.getColor(x,y);
-    if (x != (c=="w" ? V.size.x-1 : 0) || y != this.INIT_COL_KING[c])
+  getCastleMoves([x, y]) {
+    const c = this.getColor(x, y);
+    if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
       return []; //x isn't first rank, or king has moved (shortcut)
 
     // Castling ?
     const oppCol = V.GetOppCol(c);
     let moves = [];
     let i = 0;
-    const finalSquares = [ [2,3], [V.size.y-2,V.size.y-3] ]; //king, then rook
-    castlingCheck:
-    for (let castleSide=0; castleSide < 2; castleSide++) //large, then small
-    {
-      if (!this.castleFlags[c][castleSide])
-        continue;
+    const finalSquares = [
+      [2, 3],
+      [V.size.y - 2, V.size.y - 3]
+    ]; //king, then rook
+    castlingCheck: for (
+      let castleSide = 0;
+      castleSide < 2;
+      castleSide++ //large, then small
+    ) {
+      if (!this.castleFlags[c][castleSide]) continue;
       // If this code is reached, rooks and king are on initial position
 
       // Nothing on the path of the king ? (and no checks)
       const finDist = finalSquares[castleSide][0] - y;
       let step = finDist / Math.max(1, Math.abs(finDist));
       i = y;
-      do
-      {
-        if (this.isAttacked([x,i], [oppCol]) || (this.board[x][i] != V.EMPTY &&
-          // NOTE: next check is enough, because of chessboard constraints
-          (this.getColor(x,i) != c
-            || ![V.KING,V.ROOK].includes(this.getPiece(x,i)))))
-        {
+      do {
+        if (
+          this.isAttacked([x, i], [oppCol]) ||
+          (this.board[x][i] != V.EMPTY &&
+            // NOTE: next check is enough, because of chessboard constraints
+            (this.getColor(x, i) != c ||
+              ![V.KING, V.ROOK].includes(this.getPiece(x, i))))
+        ) {
           continue castlingCheck;
         }
         i += step;
-      }
-      while (i!=finalSquares[castleSide][0]);
+      } while (i != finalSquares[castleSide][0]);
 
       // Nothing on the path to the rook?
-      step = (castleSide == 0 ? -1 : 1);
-      for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step)
-      {
-        if (this.board[x][i] != V.EMPTY)
-          continue castlingCheck;
+      step = castleSide == 0 ? -1 : 1;
+      for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) {
+        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
       }
       const rookPos = this.INIT_COL_ROOK[c][castleSide];
 
       // Nothing on final squares, except maybe king and castling rook?
-      for (i=0; i<2; i++)
-      {
-        if (this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
-          this.getPiece(x,finalSquares[castleSide][i]) != V.KING &&
-          finalSquares[castleSide][i] != rookPos)
-        {
+      for (i = 0; i < 2; i++) {
+        if (
+          this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
+          this.getPiece(x, finalSquares[castleSide][i]) != V.KING &&
+          finalSquares[castleSide][i] != rookPos
+        ) {
           continue castlingCheck;
         }
       }
 
       // If this code is reached, castle is valid
-      moves.push( new Move({
-        appear: [
-          new PiPo({x:x,y:finalSquares[castleSide][0],p:V.KING,c:c}),
-          new PiPo({x:x,y:finalSquares[castleSide][1],p:V.ROOK,c:c})],
-        vanish: [
-          new PiPo({x:x,y:y,p:V.KING,c:c}),
-          new PiPo({x:x,y:rookPos,p:V.ROOK,c:c})],
-        end: Math.abs(y - rookPos) <= 2
-          ? {x:x, y:rookPos}
-          : {x:x, y:y + 2 * (castleSide==0 ? -1 : 1)}
-      }) );
+      moves.push(
+        new Move({
+          appear: [
+            new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }),
+            new PiPo({ x: x, y: finalSquares[castleSide][1], p: V.ROOK, c: c })
+          ],
+          vanish: [
+            new PiPo({ x: x, y: y, p: V.KING, c: c }),
+            new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
+          ],
+          end:
+            Math.abs(y - rookPos) <= 2
+              ? { x: x, y: rookPos }
+              : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
+        })
+      );
     }
 
     return moves;
@@ -806,16 +777,13 @@ export const ChessRules = class ChessRules
   // MOVES VALIDATION
 
   // For the interface: possible moves for the current turn from square sq
-  getPossibleMovesFrom(sq)
-  {
-    return this.filterValid( this.getPotentialMovesFrom(sq) );
+  getPossibleMovesFrom(sq) {
+    return this.filterValid(this.getPotentialMovesFrom(sq));
   }
 
   // TODO: promotions (into R,B,N,Q) should be filtered only once
-  filterValid(moves)
-  {
-    if (moves.length == 0)
-      return [];
+  filterValid(moves) {
+    if (moves.length == 0) return [];
     const color = this.turn;
     return moves.filter(m => {
       this.play(m);
@@ -827,20 +795,18 @@ export const ChessRules = class ChessRules
 
   // Search for all valid moves considering current turn
   // (for engine and game end)
-  getAllValidMoves()
-  {
+  getAllValidMoves() {
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
     let potentialMoves = [];
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
         // Next condition "!= oppCol" to work with checkered variant
-        if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
-        {
-          Array.prototype.push.apply(potentialMoves,
-            this.getPotentialMovesFrom([i,j]));
+        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+          Array.prototype.push.apply(
+            potentialMoves,
+            this.getPotentialMovesFrom([i, j])
+          );
         }
       }
     }
@@ -848,23 +814,16 @@ export const ChessRules = class ChessRules
   }
 
   // Stop at the first move found
-  atLeastOneMove()
-  {
+  atLeastOneMove() {
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
-        {
-          const moves = this.getPotentialMovesFrom([i,j]);
-          if (moves.length > 0)
-          {
-            for (let k=0; k<moves.length; k++)
-            {
-              if (this.filterValid([moves[k]]).length > 0)
-                return true;
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+          const moves = this.getPotentialMovesFrom([i, j]);
+          if (moves.length > 0) {
+            for (let k = 0; k < moves.length; k++) {
+              if (this.filterValid([moves[k]]).length > 0) return true;
             }
           }
         }
@@ -874,29 +833,29 @@ export const ChessRules = class ChessRules
   }
 
   // Check if pieces of color in 'colors' are attacking (king) on square x,y
-  isAttacked(sq, colors)
-  {
-    return (this.isAttackedByPawn(sq, colors)
-      || this.isAttackedByRook(sq, colors)
-      || this.isAttackedByKnight(sq, colors)
-      || this.isAttackedByBishop(sq, colors)
-      || this.isAttackedByQueen(sq, colors)
-      || this.isAttackedByKing(sq, colors));
+  isAttacked(sq, colors) {
+    return (
+      this.isAttackedByPawn(sq, colors) ||
+      this.isAttackedByRook(sq, colors) ||
+      this.isAttackedByKnight(sq, colors) ||
+      this.isAttackedByBishop(sq, colors) ||
+      this.isAttackedByQueen(sq, colors) ||
+      this.isAttackedByKing(sq, colors)
+    );
   }
 
   // Is square x,y attacked by 'colors' pawns ?
-  isAttackedByPawn([x,y], colors)
-  {
-    for (let c of colors)
-    {
-      let pawnShift = (c=="w" ? 1 : -1);
-      if (x+pawnShift>=0 && x+pawnShift<V.size.x)
-      {
-        for (let i of [-1,1])
-        {
-          if (y+i>=0 && y+i<V.size.y && this.getPiece(x+pawnShift,y+i)==V.PAWN
-            && this.getColor(x+pawnShift,y+i)==c)
-          {
+  isAttackedByPawn([x, y], colors) {
+    for (let c of colors) {
+      let pawnShift = c == "w" ? 1 : -1;
+      if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+        for (let i of [-1, 1]) {
+          if (
+            y + i >= 0 &&
+            y + i < V.size.y &&
+            this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+            this.getColor(x + pawnShift, y + i) == c
+          ) {
             return true;
           }
         }
@@ -906,53 +865,62 @@ export const ChessRules = class ChessRules
   }
 
   // Is square x,y attacked by 'colors' rooks ?
-  isAttackedByRook(sq, colors)
-  {
+  isAttackedByRook(sq, colors) {
     return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
   }
 
   // Is square x,y attacked by 'colors' knights ?
-  isAttackedByKnight(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors,
-      V.KNIGHT, V.steps[V.KNIGHT], "oneStep");
+  isAttackedByKnight(sq, colors) {
+    return this.isAttackedBySlideNJump(
+      sq,
+      colors,
+      V.KNIGHT,
+      V.steps[V.KNIGHT],
+      "oneStep"
+    );
   }
 
   // Is square x,y attacked by 'colors' bishops ?
-  isAttackedByBishop(sq, colors)
-  {
+  isAttackedByBishop(sq, colors) {
     return this.isAttackedBySlideNJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
   }
 
   // Is square x,y attacked by 'colors' queens ?
-  isAttackedByQueen(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors, V.QUEEN,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+  isAttackedByQueen(sq, colors) {
+    return this.isAttackedBySlideNJump(
+      sq,
+      colors,
+      V.QUEEN,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+    );
   }
 
   // Is square x,y attacked by 'colors' king(s) ?
-  isAttackedByKing(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors, V.KING,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+  isAttackedByKing(sq, colors) {
+    return this.isAttackedBySlideNJump(
+      sq,
+      colors,
+      V.KING,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
   // Generic method for non-pawn pieces ("sliding or jumping"):
   // is x,y attacked by a piece of color in array 'colors' ?
-  isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
-  {
-    for (let step of steps)
-    {
-      let rx = x+step[0], ry = y+step[1];
-      while (V.OnBoard(rx,ry) && this.board[rx][ry] == V.EMPTY && !oneStep)
-      {
+  isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+    for (let step of steps) {
+      let rx = x + step[0],
+          ry = y + step[1];
+      while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
         rx += step[0];
         ry += step[1];
       }
-      if (V.OnBoard(rx,ry) && this.getPiece(rx,ry) === piece
-        && colors.includes(this.getColor(rx,ry)))
-      {
+      if (
+        V.OnBoard(rx, ry) &&
+        this.getPiece(rx, ry) === piece &&
+        colors.includes(this.getColor(rx, ry))
+      ) {
         return true;
       }
     }
@@ -960,8 +928,7 @@ export const ChessRules = class ChessRules
   }
 
   // Is color under check after his move ?
-  underCheck(color)
-  {
+  underCheck(color) {
     return this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]);
   }
 
@@ -969,70 +936,58 @@ export const ChessRules = class ChessRules
   // MOVES PLAYING
 
   // Apply a move on board
-  static PlayOnBoard(board, move)
-  {
-    for (let psq of move.vanish)
-      board[psq.x][psq.y] = V.EMPTY;
-    for (let psq of move.appear)
-      board[psq.x][psq.y] = psq.c + psq.p;
+  static PlayOnBoard(board, move) {
+    for (let psq of move.vanish) board[psq.x][psq.y] = V.EMPTY;
+    for (let psq of move.appear) board[psq.x][psq.y] = psq.c + psq.p;
   }
   // Un-apply the played move
-  static UndoOnBoard(board, move)
-  {
-    for (let psq of move.appear)
-      board[psq.x][psq.y] = V.EMPTY;
-    for (let psq of move.vanish)
-      board[psq.x][psq.y] = psq.c + psq.p;
+  static UndoOnBoard(board, move) {
+    for (let psq of move.appear) board[psq.x][psq.y] = V.EMPTY;
+    for (let psq of move.vanish) board[psq.x][psq.y] = psq.c + psq.p;
   }
 
   // After move is played, update variables + flags
-  updateVariables(move)
-  {
+  updateVariables(move) {
     let piece = undefined;
     let c = undefined;
-    if (move.vanish.length >= 1)
-    {
+    if (move.vanish.length >= 1) {
       // Usual case, something is moved
       piece = move.vanish[0].p;
       c = move.vanish[0].c;
-    }
-    else
-    {
+    } else {
       // Crazyhouse-like variants
       piece = move.appear[0].p;
       c = move.appear[0].c;
     }
-    if (c == "c") //if (!["w","b"].includes(c))
-    {
+    if (c == "c") {
+      //if (!["w","b"].includes(c))
       // 'c = move.vanish[0].c' doesn't work for Checkered
       c = V.GetOppCol(this.turn);
     }
-    const firstRank = (c == "w" ? V.size.x-1 : 0);
+    const firstRank = c == "w" ? V.size.x - 1 : 0;
 
     // Update king position + flags
-    if (piece == V.KING && move.appear.length > 0)
-    {
+    if (piece == V.KING && move.appear.length > 0) {
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
-      if (V.HasFlags)
-        this.castleFlags[c] = [false,false];
+      if (V.HasFlags) this.castleFlags[c] = [false, false];
       return;
     }
-    if (V.HasFlags)
-    {
+    if (V.HasFlags) {
       // Update castling flags if rooks are moved
       const oppCol = V.GetOppCol(c);
-      const oppFirstRank = (V.size.x-1) - firstRank;
-      if (move.start.x == firstRank //our rook moves?
-        && this.INIT_COL_ROOK[c].includes(move.start.y))
-      {
-        const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1);
+      const oppFirstRank = V.size.x - 1 - firstRank;
+      if (
+        move.start.x == firstRank && //our rook moves?
+        this.INIT_COL_ROOK[c].includes(move.start.y)
+      ) {
+        const flagIdx = move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1;
         this.castleFlags[c][flagIdx] = false;
-      }
-      else if (move.end.x == oppFirstRank //we took opponent rook?
-        && this.INIT_COL_ROOK[oppCol].includes(move.end.y))
-      {
-        const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1);
+      } else if (
+        move.end.x == oppFirstRank && //we took opponent rook?
+        this.INIT_COL_ROOK[oppCol].includes(move.end.y)
+      ) {
+        const flagIdx = move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1;
         this.castleFlags[oppCol][flagIdx] = false;
       }
     }
@@ -1040,55 +995,48 @@ export const ChessRules = class ChessRules
 
   // After move is undo-ed *and flags resetted*, un-update other variables
   // TODO: more symmetry, by storing flags increment in move (?!)
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     // (Potentially) Reset king position
-    const c = this.getColor(move.start.x,move.start.y);
-    if (this.getPiece(move.start.x,move.start.y) == V.KING)
+    const c = this.getColor(move.start.x, move.start.y);
+    if (this.getPiece(move.start.x, move.start.y) == V.KING)
       this.kingPos[c] = [move.start.x, move.start.y];
   }
 
-  play(move)
-  {
+  play(move) {
     // DEBUG:
-//    if (!this.states) this.states = [];
-//    const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
-//    this.states.push(stateFen);
-
-    if (V.HasFlags)
-      move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
-    if (V.HasEnpassant)
-      this.epSquares.push( this.getEpSquare(move) );
+    //    if (!this.states) this.states = [];
+    //    const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
+    //    this.states.push(stateFen);
+
+    if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
+    if (V.HasEnpassant) this.epSquares.push(this.getEpSquare(move));
     V.PlayOnBoard(this.board, move);
     this.turn = V.GetOppCol(this.turn);
     this.movesCount++;
     this.updateVariables(move);
   }
 
-  undo(move)
-  {
-    if (V.HasEnpassant)
-      this.epSquares.pop();
-    if (V.HasFlags)
-      this.disaggregateFlags(JSON.parse(move.flags));
+  undo(move) {
+    if (V.HasEnpassant) this.epSquares.pop();
+    if (V.HasFlags) this.disaggregateFlags(JSON.parse(move.flags));
     V.UndoOnBoard(this.board, move);
     this.turn = V.GetOppCol(this.turn);
     this.movesCount--;
     this.unupdateVariables(move);
 
     // DEBUG:
-//    const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
-//    if (stateFen != this.states[this.states.length-1]) debugger;
-//    this.states.pop();
+    //    const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
+    //    if (stateFen != this.states[this.states.length-1]) debugger;
+    //    this.states.pop();
   }
 
   ///////////////
   // END OF GAME
 
   // What is the score ? (Interesting if game is over)
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
 
     // Game over
@@ -1097,184 +1045,180 @@ export const ChessRules = class ChessRules
     if (!this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
       return "1/2";
     // OK, checkmate
-    return (color == "w" ? "0-1" : "1-0");
+    return color == "w" ? "0-1" : "1-0";
   }
 
   ///////////////
   // ENGINE PLAY
 
   // Pieces values
-  static get VALUES()
-  {
+  static get VALUES() {
     return {
-      'p': 1,
-      'r': 5,
-      'n': 3,
-      'b': 3,
-      'q': 9,
-      'k': 1000
+      p: 1,
+      r: 5,
+      n: 3,
+      b: 3,
+      q: 9,
+      k: 1000
     };
   }
 
   // "Checkmate" (unreachable eval)
-  static get INFINITY() { return 9999; }
+  static get INFINITY() {
+    return 9999;
+  }
 
   // At this value or above, the game is over
-  static get THRESHOLD_MATE() { return V.INFINITY; }
+  static get THRESHOLD_MATE() {
+    return V.INFINITY;
+  }
 
   // Search depth: 2 for high branching factor, 4 for small (Loser chess, eg.)
-  static get SEARCH_DEPTH() { return 3; }
+  static get SEARCH_DEPTH() {
+    return 3;
+  }
 
   // NOTE: works also for extinction chess because depth is 3...
-  getComputerMove()
-  {
+  getComputerMove() {
     const maxeval = V.INFINITY;
     const color = this.turn;
     // Some variants may show a bigger moves list to the human (Switching),
     // thus the argument "computer" below (which is generally ignored)
     let moves1 = this.getAllValidMoves("computer");
-    if (moves1.length == 0) //TODO: this situation should not happen
+    if (moves1.length == 0)
+      //TODO: this situation should not happen
       return null;
 
     // Can I mate in 1 ? (for Magnetic & Extinction)
-    for (let i of shuffle(ArrayFun.range(moves1.length)))
-    {
+    for (let i of shuffle(ArrayFun.range(moves1.length))) {
       this.play(moves1[i]);
-      let finish = (Math.abs(this.evalPosition()) >= V.THRESHOLD_MATE);
-      if (!finish)
-      {
+      let finish = Math.abs(this.evalPosition()) >= V.THRESHOLD_MATE;
+      if (!finish) {
         const score = this.getCurrentScore();
-        if (["1-0","0-1"].includes(score))
-          finish = true;
+        if (["1-0", "0-1"].includes(score)) finish = true;
       }
       this.undo(moves1[i]);
-      if (finish)
-        return moves1[i];
+      if (finish) return moves1[i];
     }
 
     // Rank moves using a min-max at depth 2
-    for (let i=0; i<moves1.length; i++)
-    {
+    for (let i = 0; i < moves1.length; i++) {
       // Initial self evaluation is very low: "I'm checkmated"
-      moves1[i].eval = (color=="w" ? -1 : 1) * maxeval;
+      moves1[i].eval = (color == "w" ? -1 : 1) * maxeval;
       this.play(moves1[i]);
       const score1 = this.getCurrentScore();
       let eval2 = undefined;
-      if (score1 == "*")
-      {
+      if (score1 == "*") {
         // Initial enemy evaluation is very low too, for him
-        eval2 = (color=="w" ? 1 : -1) * maxeval;
+        eval2 = (color == "w" ? 1 : -1) * maxeval;
         // Second half-move:
         let moves2 = this.getAllValidMoves("computer");
-        for (let j=0; j<moves2.length; j++)
-        {
+        for (let j = 0; j < moves2.length; j++) {
           this.play(moves2[j]);
           const score2 = this.getCurrentScore();
-          const evalPos = score2 == "*"
-            ? this.evalPosition()
-            : (score2=="1/2" ? 0 : (score2=="1-0" ? 1 : -1) * maxeval);
-          if ((color == "w" && evalPos < eval2)
-            || (color=="b" && evalPos > eval2))
-          {
+          let evalPos = 0; //1/2 value
+          switch (score2) {
+            case "*":
+              evalPos = this.evalPosition();
+              break;
+            case "1-0":
+              evalPos = maxeval;
+              break;
+            case "0-1":
+              evalPos = -maxeval;
+              break;
+          }
+          if (
+            (color == "w" && evalPos < eval2) ||
+            (color == "b" && evalPos > eval2)
+          ) {
             eval2 = evalPos;
           }
           this.undo(moves2[j]);
         }
-      }
-      else
-        eval2 = (score1=="1/2" ? 0 : (score1=="1-0" ? 1 : -1) * maxeval);
-      if ((color=="w" && eval2 > moves1[i].eval)
-        || (color=="b" && eval2 < moves1[i].eval))
-      {
+      } else eval2 = score1 == "1/2" ? 0 : (score1 == "1-0" ? 1 : -1) * maxeval;
+      if (
+        (color == "w" && eval2 > moves1[i].eval) ||
+        (color == "b" && eval2 < moves1[i].eval)
+      ) {
         moves1[i].eval = eval2;
       }
       this.undo(moves1[i]);
     }
-    moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+    moves1.sort((a, b) => {
+      return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+    });
 
     let candidates = [0]; //indices of candidates moves
-    for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
+    for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++)
       candidates.push(j);
     let currentBest = moves1[candidates[randInt(candidates.length)]];
 
     // Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...)
-    if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE)
-    {
+    if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE) {
       // From here, depth >= 3: may take a while, so we control time
       const timeStart = Date.now();
-      for (let i=0; i<moves1.length; i++)
-      {
-        if (Date.now()-timeStart >= 5000) //more than 5 seconds
+      for (let i = 0; i < moves1.length; i++) {
+        if (Date.now() - timeStart >= 5000)
+          //more than 5 seconds
           return currentBest; //depth 2 at least
         this.play(moves1[i]);
         // 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
-        moves1[i].eval = 0.1*moves1[i].eval +
-          this.alphabeta(V.SEARCH_DEPTH-1, -maxeval, maxeval);
+        moves1[i].eval =
+          0.1 * moves1[i].eval +
+          this.alphabeta(V.SEARCH_DEPTH - 1, -maxeval, maxeval);
         this.undo(moves1[i]);
       }
-      moves1.sort( (a,b) => {
-        return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
-    }
-    else
-      return currentBest;
-//    console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
+      moves1.sort((a, b) => {
+        return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+      });
+    } else return currentBest;
+    //    console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
 
     candidates = [0];
-    for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
+    for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++)
       candidates.push(j);
     return moves1[candidates[randInt(candidates.length)]];
   }
 
-  alphabeta(depth, alpha, beta)
-  {
+  alphabeta(depth, alpha, beta) {
     const maxeval = V.INFINITY;
     const color = this.turn;
     const score = this.getCurrentScore();
     if (score != "*")
-      return (score=="1/2" ? 0 : (score=="1-0" ? 1 : -1) * maxeval);
-    if (depth == 0)
-      return this.evalPosition();
+      return score == "1/2" ? 0 : (score == "1-0" ? 1 : -1) * maxeval;
+    if (depth == 0) return this.evalPosition();
     const moves = this.getAllValidMoves("computer");
-    let v = color=="w" ? -maxeval : maxeval;
-    if (color == "w")
-    {
-      for (let i=0; i<moves.length; i++)
-      {
+    let v = color == "w" ? -maxeval : maxeval;
+    if (color == "w") {
+      for (let i = 0; i < moves.length; i++) {
         this.play(moves[i]);
-        v = Math.max(v, this.alphabeta(depth-1, alpha, beta));
+        v = Math.max(v, this.alphabeta(depth - 1, alpha, beta));
         this.undo(moves[i]);
         alpha = Math.max(alpha, v);
-        if (alpha >= beta)
-          break; //beta cutoff
+        if (alpha >= beta) break; //beta cutoff
       }
-    }
-    else //color=="b"
-    {
-      for (let i=0; i<moves.length; i++)
-      {
+    } //color=="b"
+    else {
+      for (let i = 0; i < moves.length; i++) {
         this.play(moves[i]);
-        v = Math.min(v, this.alphabeta(depth-1, alpha, beta));
+        v = Math.min(v, this.alphabeta(depth - 1, alpha, beta));
         this.undo(moves[i]);
         beta = Math.min(beta, v);
-        if (alpha >= beta)
-          break; //alpha cutoff
+        if (alpha >= beta) break; //alpha cutoff
       }
     }
     return v;
   }
 
-  evalPosition()
-  {
+  evalPosition() {
     let evaluation = 0;
     // Just count material for now
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY)
-        {
-          const sign = this.getColor(i,j) == "w" ? 1 : -1;
-          evaluation += sign * V.VALUES[this.getPiece(i,j)];
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY) {
+          const sign = this.getColor(i, j) == "w" ? 1 : -1;
+          evaluation += sign * V.VALUES[this.getPiece(i, j)];
         }
       }
     }
@@ -1287,37 +1231,34 @@ export const ChessRules = class ChessRules
 
   // Context: just before move is played, turn hasn't changed
   // TODO: un-ambiguous notation (switch on piece type, check directions...)
-  getNotation(move)
-  {
-    if (move.appear.length == 2 && move.appear[0].p == V.KING) //castle
-      return (move.end.y < move.start.y ? "0-0-0" : "0-0");
+  getNotation(move) {
+    if (move.appear.length == 2 && move.appear[0].p == V.KING)
+      //castle
+      return move.end.y < move.start.y ? "0-0-0" : "0-0";
 
     // Translate final square
     const finalSquare = V.CoordsToSquare(move.end);
 
     const piece = this.getPiece(move.start.x, move.start.y);
-    if (piece == V.PAWN)
-    {
+    if (piece == V.PAWN) {
       // Pawn move
       let notation = "";
-      if (move.vanish.length > move.appear.length)
-      {
+      if (move.vanish.length > move.appear.length) {
         // Capture
         const startColumn = V.CoordToColumn(move.start.y);
         notation = startColumn + "x" + finalSquare;
-      }
-      else //no capture
-        notation = finalSquare;
-      if (move.appear.length > 0 && move.appear[0].p != V.PAWN) //promotion
+      } //no capture
+      else notation = finalSquare;
+      if (move.appear.length > 0 && move.appear[0].p != V.PAWN)
+        //promotion
         notation += "=" + move.appear[0].p.toUpperCase();
       return notation;
     }
-
-    else
-    {
-      // Piece movement
-      return piece.toUpperCase() +
-        (move.vanish.length > move.appear.length ? "x" : "") + finalSquare;
-    }
-  }
-}
+    // Piece movement
+    return (
+      piece.toUpperCase() +
+      (move.vanish.length > move.appear.length ? "x" : "") +
+      finalSquare
+    );
+  }
+};
diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue
index ef6ef01e..b2dd4a72 100644
--- a/client/src/components/BaseGame.vue
+++ b/client/src/components/BaseGame.vue
@@ -1,25 +1,47 @@
 <template lang="pug">
-div#baseGame(tabindex=-1 @click="focusBg()"
-    @keydown="handleKeys($event)" @wheel="handleScroll($event)")
+div#baseGame(
+  tabindex=-1
+  @click="focusBg()"
+  @keydown="handleKeys($event)"
+  @wheel="handleScroll($event)"
+)
   input#modalEog.modal(type="checkbox")
-  div#eogDiv(role="dialog" data-checkbox="modalEog")
+  div#eogDiv(
+    role="dialog"
+    data-checkbox="modalEog"
+  )
     .card.text-center
       label.modal-close(for="modalEog")
       h3.section {{ endgameMessage }}
   input#modalAdjust.modal(type="checkbox")
-  div#adjuster(role="dialog" data-checkbox="modalAdjust")
+  div#adjuster(
+    role="dialog"
+    data-checkbox="modalAdjust"
+  )
     .card.text-center
       label.modal-close(for="modalAdjust")
       label(for="boardSize") {{ st.tr["Board size"] }}
-      input#boardSize.slider(type="range" min="0" max="100" value="50"
-        @input="adjustBoard()")
+      input#boardSize.slider(
+        type="range"
+        min="0"
+        max="100"
+        value="50"
+        @input="adjustBoard()"
+      )
   #gameContainer
     #boardContainer
-      Board(:vr="vr" :last-move="lastMove" :analyze="analyze"
-        :user-color="game.mycolor" :orientation="orientation"
-        :vname="game.vname" :incheck="incheck" @play-move="play")
+      Board(
+        :vr="vr"
+        :last-move="lastMove"
+        :analyze="analyze"
+        :user-color="game.mycolor"
+        :orientation="orientation"
+        :vname="game.vname"
+        :incheck="incheck"
+        @play-move="play"
+      )
       #turnIndicator(v-if="game.vname=='Dark' && game.score=='*'")
-        | {{ turn }}
+        | {{ st.tr[vr.turn + " to move"] }}
       #controls
         button(@click="gotoBegin()") <<
         button(@click="undo()") <
@@ -31,16 +53,27 @@ div#baseGame(tabindex=-1 @click="focusBg()"
           a#download(href="#")
           button(@click="download()") {{ st.tr["Download"] }} PGN
         button(onClick="doClick('modalAdjust')") &#10530;
-        button(v-if="game.vname!='Dark' && game.mode!='analyze'"
-            @click="analyzePosition()")
+        button(
+          v-if="game.vname!='Dark' && game.mode!='analyze'"
+          @click="analyzePosition()"
+        )
           | {{ st.tr["Analyse"] }}
         // NOTE: rather ugly hack to avoid showing twice "rules" link...
-        button(v-if="!$route.path.match('/variants/')" @click="showRules()")
+        button(
+          v-if="!$route.path.match('/variants/')"
+          @click="showRules()"
+        )
           | {{ st.tr["Rules"] }}
     #movesList
-      MoveList(v-if="showMoves" :score="game.score" :message="game.scoreMsg"
-        :firstNum="firstMoveNumber" :moves="moves" :cursor="cursor"
-        @goto-move="gotoMove")
+      MoveList(
+        v-if="showMoves"
+        :score="game.score"
+        :message="game.scoreMsg"
+        :firstNum="firstMoveNumber"
+        :moves="moves"
+        :cursor="cursor"
+        @goto-move="gotoMove"
+      )
     .clearer
 </template>
 
@@ -53,13 +86,13 @@ import { getDate } from "@/utils/datetime";
 import { processModalClick } from "@/utils/modalClick";
 import { getScoreMessage } from "@/utils/scoring";
 export default {
-  name: 'my-base-game',
+  name: "my-base-game",
   components: {
     Board,
-    MoveList,
+    MoveList
   },
   // "vr": VariantRules object, describing the game state + rules
-  props: ["vr","game"],
+  props: ["vr", "game"],
   data: function() {
     return {
       st: store.state,
@@ -71,7 +104,7 @@ export default {
       cursor: -1, //index of the move just played
       lastMove: null,
       firstMoveNumber: 0, //for printing
-      incheck: [], //for Board
+      incheck: [] //for Board
     };
   },
   watch: {
@@ -80,62 +113,54 @@ export default {
       this.re_setVariables();
     },
     // Received a new move to play:
-    "game.moveToPlay": function(newMove) {
-      if (!!newMove) //if stop + launch new game, get undefined move
-        this.play(newMove, "receive");
+    "game.moveToPlay": function(move) {
+      if (move) this.play(move, "receive");
     },
     // ...Or to undo (corr game, move not validated)
     "game.moveToUndo": function(move) {
-      if (!!move)
-        this.undo(move);
-    },
+      if (move) this.undo(move);
+    }
   },
   computed: {
     showMoves: function() {
       return this.game.vname != "Dark" || this.game.score != "*";
     },
-    turn: function() {
-      let color = "";
-      const L = this.moves.length;
-      if (L == 0 || this.moves[L-1].color == "b")
-        color = "White";
-      else //if (this.moves[L-1].color == "w")
-        color = "Black";
-      return this.st.tr[color + " to move"];
-    },
     analyze: function() {
-      return this.game.mode=="analyze" ||
+      return (
+        this.game.mode == "analyze" ||
         // From Board viewpoint, a finished Dark game == analyze (TODO: unclear)
-        (this.game.vname == "Dark" && this.game.score != "*");
-    },
+        (this.game.vname == "Dark" && this.game.score != "*")
+      );
+    }
   },
   created: function() {
-    if (!!this.game.fenStart)
-      this.re_setVariables();
+    if (this.game.fenStart) this.re_setVariables();
   },
   mounted: function() {
-    [document.getElementById("eogDiv"),document.getElementById("adjuster")]
-      .forEach(elt => elt.addEventListener("click", processModalClick));
+    [
+      document.getElementById("eogDiv"),
+      document.getElementById("adjuster")
+    ].forEach(elt => elt.addEventListener("click", processModalClick));
     // Take full width on small screens:
     let boardSize = parseInt(localStorage.getItem("boardSize"));
-    if (!boardSize)
-    {
-      boardSize = (window.innerWidth >= 768
-        ? 0.75 * Math.min(window.innerWidth, window.innerHeight)
-        : window.innerWidth);
+    if (!boardSize) {
+      boardSize =
+        window.innerWidth >= 768
+          ? 0.75 * Math.min(window.innerWidth, window.innerHeight)
+          : window.innerWidth;
     }
-    const movesWidth = (window.innerWidth >= 768 ? 280 : 0);
+    const movesWidth = window.innerWidth >= 768 ? 280 : 0;
     document.getElementById("boardContainer").style.width = boardSize + "px";
     let gameContainer = document.getElementById("gameContainer");
-    gameContainer.style.width = (boardSize + movesWidth) + "px";
-    document.getElementById("boardSize").value = (boardSize * 100) / (window.innerWidth - movesWidth);
+    gameContainer.style.width = boardSize + movesWidth + "px";
+    document.getElementById("boardSize").value =
+      (boardSize * 100) / (window.innerWidth - movesWidth);
     // timeout to avoid calling too many time the adjust method
     let timeoutLaunched = false;
-    window.addEventListener("resize", (e) => {
-      if (!timeoutLaunched)
-      {
+    window.addEventListener("resize", () => {
+      if (!timeoutLaunched) {
         timeoutLaunched = true;
-        setTimeout( () => {
+        setTimeout(() => {
           this.adjustBoard();
           timeoutLaunched = false;
         }, 500);
@@ -149,24 +174,22 @@ export default {
     },
     adjustBoard: function() {
       const boardContainer = document.getElementById("boardContainer");
-      if (!boardContainer)
-        return; //no board on page
+      if (!boardContainer) return; //no board on page
       const k = document.getElementById("boardSize").value;
-      const movesWidth = (window.innerWidth >= 768 ? 280 : 0);
+      const movesWidth = window.innerWidth >= 768 ? 280 : 0;
       const minBoardWidth = 240; //TODO: these 240 and 280 are arbitrary...
       // Value of 0 is board min size; 100 is window.width [- movesWidth]
-      const boardSize = minBoardWidth +
-        k * (window.innerWidth - (movesWidth+minBoardWidth)) / 100;
+      const boardSize =
+        minBoardWidth +
+        (k * (window.innerWidth - (movesWidth + minBoardWidth))) / 100;
       localStorage.setItem("boardSize", boardSize);
       boardContainer.style.width = boardSize + "px";
       document.getElementById("gameContainer").style.width =
-        (boardSize + movesWidth) + "px";
+        boardSize + movesWidth + "px";
     },
     handleKeys: function(e) {
-      if ([32,37,38,39,40].includes(e.keyCode))
-        e.preventDefault();
-      switch (e.keyCode)
-      {
+      if ([32, 37, 38, 39, 40].includes(e.keyCode)) e.preventDefault();
+      switch (e.keyCode) {
         case 37:
           this.undo();
           break;
@@ -186,13 +209,10 @@ export default {
     },
     handleScroll: function(e) {
       // NOTE: since game.mode=="analyze" => no score, next condition is enough
-      if (this.game.score != "*")
-      {
+      if (this.game.score != "*") {
         e.preventDefault();
-        if (e.deltaY < 0)
-          this.undo();
-        else if (e.deltaY > 0)
-          this.play();
+        if (e.deltaY < 0) this.undo();
+        else if (e.deltaY > 0) this.play();
       }
     },
     showRules: function() {
@@ -206,8 +226,9 @@ export default {
       // Post-processing: decorate each move with color + current FEN:
       // (to be able to jump to any position quickly)
       let vr_tmp = new V(this.game.fenStart); //vr is already at end of game
-      this.firstMoveNumber =
-        Math.floor(V.ParseFen(this.game.fenStart).movesCount / 2);
+      this.firstMoveNumber = Math.floor(
+        V.ParseFen(this.game.fenStart).movesCount / 2
+      );
       this.moves.forEach(move => {
         // NOTE: this is doing manually what play() function below achieve,
         // but in a lighter "fast-forward" way
@@ -216,31 +237,39 @@ export default {
         vr_tmp.play(move);
         move.fen = vr_tmp.getFen();
       });
-      if ((this.moves.length > 0 && this.moves[0].color == "b") ||
-        (this.moves.length == 0 && this.vr_tmp.turn == "b"))
-      {
+      if (
+        (this.moves.length > 0 && this.moves[0].color == "b") ||
+        (this.moves.length == 0 && vr_tmp.turn == "b")
+      ) {
         // 'end' is required for Board component to check lastMove for e.p.
-        this.moves.unshift({color: "w", notation: "...", end: {x:-1,y:-1}});
+        this.moves.unshift({
+          color: "w",
+          notation: "...",
+          end: { x: -1, y: -1 }
+        });
       }
       const L = this.moves.length;
-      this.cursor = L-1;
-      this.lastMove = (L > 0 ? this.moves[L-1]  : null);
+      this.cursor = L - 1;
+      this.lastMove = L > 0 ? this.moves[L - 1] : null;
       this.incheck = this.vr.getCheckSquares(this.vr.turn);
     },
     analyzePosition: function() {
-      const newUrl = "/analyse/" + this.game.vname +
-        "/?fen=" + this.vr.getFen().replace(/ /g, "_");
-      if (this.game.type == "live")
-        this.$router.push(newUrl); //open in same tab: against cheating...
-      else
-        window.open("#" + newUrl); //open in a new tab: more comfortable
+      const newUrl =
+        "/analyse/" +
+        this.game.vname +
+        "/?fen=" +
+        this.vr.getFen().replace(/ /g, "_");
+      if (this.game.type == "live") this.$router.push(newUrl);
+      //open in same tab: against cheating...
+      else window.open("#" + newUrl); //open in a new tab: more comfortable
     },
     download: function() {
       const content = this.getPgn();
       // Prepare and trigger download link
       let downloadAnchor = document.getElementById("download");
       downloadAnchor.setAttribute("download", "game.pgn");
-      downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content);
+      downloadAnchor.href =
+        "data:text/plain;charset=utf-8," + encodeURIComponent(content);
       downloadAnchor.click();
     },
     getPgn: function() {
@@ -254,15 +283,13 @@ export default {
       pgn += '[Result "' + this.game.score + '"]\n\n';
       let counter = 1;
       let i = 0;
-      while (i < this.moves.length)
-      {
-        pgn += (counter++) + ".";
-        for (let color of ["w","b"])
-        {
+      while (i < this.moves.length) {
+        pgn += counter++ + ".";
+        for (let color of ["w", "b"]) {
           let move = "";
           while (i < this.moves.length && this.moves[i].color == color)
             move += this.moves[i++].notation + ",";
-          move = move.slice(0,-1); //remove last comma
+          move = move.slice(0, -1); //remove last comma
           pgn += move + (i < this.moves.length ? " " : "");
         }
       }
@@ -272,37 +299,41 @@ export default {
       this.endgameMessage = message;
       let modalBox = document.getElementById("modalEog");
       modalBox.checked = true;
-      setTimeout(() => { modalBox.checked = false; }, 2000);
+      setTimeout(() => {
+        modalBox.checked = false;
+      }, 2000);
     },
     animateMove: function(move, callback) {
       let startSquare = document.getElementById(getSquareId(move.start));
       // TODO: error "flush nextTick callbacks" when observer reloads page:
       // this late check is not a fix!
-      if (!startSquare)
-        return;
+      if (!startSquare) return;
       let endSquare = document.getElementById(getSquareId(move.end));
       let rectStart = startSquare.getBoundingClientRect();
       let rectEnd = endSquare.getBoundingClientRect();
-      let translation = {x:rectEnd.x-rectStart.x, y:rectEnd.y-rectStart.y};
-      let movingPiece =
-        document.querySelector("#" + getSquareId(move.start) + " > img.piece");
-      if (!movingPiece) //TODO: shouldn't happen
+      let translation = {
+        x: rectEnd.x - rectStart.x,
+        y: rectEnd.y - rectStart.y
+      };
+      let movingPiece = document.querySelector(
+        "#" + getSquareId(move.start) + " > img.piece"
+      );
+      if (!movingPiece)
+        //TODO: shouldn't happen
         return;
       // HACK for animation (with positive translate, image slides "under background")
       // Possible improvement: just alter squares on the piece's way...
       const squares = document.getElementsByClassName("board");
-      for (let i=0; i<squares.length; i++)
-      {
+      for (let i = 0; i < squares.length; i++) {
         let square = squares.item(i);
-        if (square.id != getSquareId(move.start))
-          square.style.zIndex = "-1";
+        if (square.id != getSquareId(move.start)) square.style.zIndex = "-1";
       }
-      movingPiece.style.transform = "translate(" + translation.x + "px," +
-        translation.y + "px)";
+      movingPiece.style.transform =
+        "translate(" + translation.x + "px," + translation.y + "px)";
       movingPiece.style.transitionDuration = "0.2s";
       movingPiece.style.zIndex = "3000";
-      setTimeout( () => {
-        for (let i=0; i<squares.length; i++)
+      setTimeout(() => {
+        for (let i = 0; i < squares.length; i++)
           squares.item(i).style.zIndex = "auto";
         movingPiece.style = {}; //required e.g. for 0-0 with KR swap
         callback();
@@ -313,22 +344,20 @@ export default {
       const navigate = !move;
       // Forbid playing outside analyze mode, except if move is received.
       // Sufficient condition because Board already knows which turn it is.
-      if (!navigate && this.game.mode!="analyze" && !receive
-        && (this.game.score != "*" || this.cursor < this.moves.length-1))
-      {
+      if (
+        !navigate &&
+        this.game.mode != "analyze" &&
+        !receive &&
+        (this.game.score != "*" || this.cursor < this.moves.length - 1)
+      ) {
         return;
       }
       const doPlayMove = () => {
-        if (!!receive && this.cursor < this.moves.length-1)
-          this.gotoEnd(); //required to play the move
-        if (navigate)
-        {
-          if (this.cursor == this.moves.length-1)
-            return; //no more moves
-          move = this.moves[this.cursor+1];
-        }
-        else
-        {
+        if (!!receive && this.cursor < this.moves.length - 1) this.gotoEnd(); //required to play the move
+        if (navigate) {
+          if (this.cursor == this.moves.length - 1) return; //no more moves
+          move = this.moves[this.cursor + 1];
+        } else {
           move.color = this.vr.turn;
           move.notation = this.vr.getNotation(move);
         }
@@ -336,51 +365,43 @@ export default {
         this.cursor++;
         this.lastMove = move;
         if (this.st.settings.sound == 2)
-          new Audio("/sounds/move.mp3").play().catch(err => {});
-        if (!navigate)
-        {
+          new Audio("/sounds/move.mp3").play().catch(() => {});
+        if (!navigate) {
           move.fen = this.vr.getFen();
           // Stack move on movesList at current cursor
-          if (this.cursor == this.moves.length)
-            this.moves.push(move);
-          else
-            this.moves = this.moves.slice(0,this.cursor).concat([move]);
+          if (this.cursor == this.moves.length) this.moves.push(move);
+          else this.moves = this.moves.slice(0, this.cursor).concat([move]);
         }
         // Is opponent in check?
         this.incheck = this.vr.getCheckSquares(this.vr.turn);
         const score = this.vr.getCurrentScore();
-        if (score != "*")
-        {
+        if (score != "*") {
           const message = getScoreMessage(score);
           if (this.game.mode != "analyze")
             this.$emit("gameover", score, message);
-          else //just show score on screen (allow undo)
-            this.showEndgameMsg(score + " . " + message);
+          //just show score on screen (allow undo)
+          else this.showEndgameMsg(score + " . " + message);
         }
-        if (!navigate && this.game.mode!="analyze")
+        if (!navigate && this.game.mode != "analyze")
           this.$emit("newmove", move); //post-processing (e.g. computer play)
       };
       if (!!receive && this.game.vname != "Dark")
         this.animateMove(move, doPlayMove);
-      else
-        doPlayMove();
+      else doPlayMove();
     },
     undo: function(move) {
       const navigate = !move;
-      if (navigate)
-      {
-        if (this.cursor < 0)
-          return; //no more moves
+      if (navigate) {
+        if (this.cursor < 0) return; //no more moves
         move = this.moves[this.cursor];
       }
       this.vr.undo(move);
       this.cursor--;
-      this.lastMove = (this.cursor >= 0 ? this.moves[this.cursor] : undefined);
+      this.lastMove = this.cursor >= 0 ? this.moves[this.cursor] : undefined;
       if (this.st.settings.sound == 2)
-        new Audio("/sounds/undo.mp3").play().catch(err => {});
+        new Audio("/sounds/undo.mp3").play().catch(() => {});
       this.incheck = this.vr.getCheckSquares(this.vr.turn);
-      if (!navigate)
-        this.moves.pop();
+      if (!navigate) this.moves.pop();
     },
     gotoMove: function(index) {
       this.vr.re_init(this.moves[index].fen);
@@ -388,29 +409,24 @@ export default {
       this.lastMove = this.moves[index];
     },
     gotoBegin: function() {
-      if (this.cursor == -1)
-        return;
+      if (this.cursor == -1) return;
       this.vr.re_init(this.game.fenStart);
-      if (this.moves.length > 0 && this.moves[0].notation == "...")
-      {
+      if (this.moves.length > 0 && this.moves[0].notation == "...") {
         this.cursor = 0;
         this.lastMove = this.moves[0];
-      }
-      else
-      {
+      } else {
         this.cursor = -1;
         this.lastMove = null;
       }
     },
     gotoEnd: function() {
-      if (this.cursor == this.moves.length - 1)
-        return;
-      this.gotoMove(this.moves.length-1);
+      if (this.cursor == this.moves.length - 1) return;
+      this.gotoMove(this.moves.length - 1);
     },
     flip: function() {
       this.orientation = V.GetOppCol(this.orientation);
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/Board.vue b/client/src/components/Board.vue
index a7920d35..45c7293b 100644
--- a/client/src/components/Board.vue
+++ b/client/src/components/Board.vue
@@ -3,118 +3,130 @@ import { getSquareId, getSquareFromId } from "@/utils/squareId";
 import { ArrayFun } from "@/utils/array";
 import { store } from "@/store";
 export default {
-  name: 'my-board',
+  name: "my-board",
   // Last move cannot be guessed from here, and is required to highlight squares
   // vr: object to check moves, print board...
   // userColor is left undefined for an external observer
-  props: ["vr","lastMove","analyze","incheck","orientation","userColor","vname"],
-  data: function () {
+  props: [
+    "vr",
+    "lastMove",
+    "analyze",
+    "incheck",
+    "orientation",
+    "userColor",
+    "vname"
+  ],
+  data: function() {
     return {
       possibleMoves: [], //filled after each valid click/dragstart
       choices: [], //promotion pieces, or checkered captures... (as moves)
       selectedPiece: null, //moving piece (or clicked piece)
       start: {}, //pixels coordinates + id of starting square (click or drag)
-      settings: store.state.settings,
+      settings: store.state.settings
     };
   },
   render(h) {
-    if (!this.vr)
-    {
+    if (!this.vr) {
       // Return empty div of class 'game' to avoid error when setting size
-      return h("div",
-        {
-          "class": {
-            "game": true,
-          },
-        });
+      return h("div", {
+        class: {
+          game: true
+        }
+      });
     }
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
+    const [sizeX, sizeY] = [V.size.x, V.size.y];
     // Precompute hints squares to facilitate rendering
     let hintSquares = ArrayFun.init(sizeX, sizeY, false);
-    this.possibleMoves.forEach(m => { hintSquares[m.end.x][m.end.y] = true; });
+    this.possibleMoves.forEach(m => {
+      hintSquares[m.end.x][m.end.y] = true;
+    });
     // Also precompute in-check squares
     let incheckSq = ArrayFun.init(sizeX, sizeY, false);
-    this.incheck.forEach(sq => { incheckSq[sq[0]][sq[1]] = true; });
+    this.incheck.forEach(sq => {
+      incheckSq[sq[0]][sq[1]] = true;
+    });
 
     // Create board element (+ reserves if needed by variant or mode)
     const lm = this.lastMove;
     const showLight = this.settings.highlight && this.vname != "Dark";
     const gameDiv = h(
-      'div',
+      "div",
       {
-        'class': {
-          'game': true,
-          'clearer': true,
-        },
+        class: {
+          game: true,
+          clearer: true
+        }
       },
       [...Array(sizeX).keys()].map(i => {
-        let ci = (this.orientation=='w' ? i : sizeX-i-1);
+        let ci = this.orientation == "w" ? i : sizeX - i - 1;
         return h(
-          'div',
+          "div",
           {
-            'class': {
-              'row': true,
+            class: {
+              row: true
             },
-            style: { 'opacity': this.choices.length>0?"0.5":"1" },
+            style: { opacity: this.choices.length > 0 ? "0.5" : "1" }
           },
           [...Array(sizeY).keys()].map(j => {
-            let cj = (this.orientation=='w' ? j : sizeY-j-1);
+            let cj = this.orientation == "w" ? j : sizeY - j - 1;
             let elems = [];
-            if (this.vr.board[ci][cj] != V.EMPTY && (this.vname!="Dark"
-              || this.analyze || (!!this.userColor
-                && this.vr.enlightened[this.userColor][ci][cj])))
-            {
+            if (
+              this.vr.board[ci][cj] != V.EMPTY &&
+              (this.vname != "Dark" ||
+                this.analyze ||
+                (!!this.userColor &&
+                  this.vr.enlightened[this.userColor][ci][cj]))
+            ) {
               elems.push(
-                h(
-                  'img',
-                  {
-                    'class': {
-                      'piece': true,
-                      'ghost': !!this.selectedPiece
-                        && this.selectedPiece.parentNode.id == "sq-"+ci+"-"+cj,
-                    },
-                    attrs: {
-                      src: "/images/pieces/" +
-                        V.getPpath(this.vr.board[ci][cj]) + ".svg",
-                    },
+                h("img", {
+                  class: {
+                    piece: true,
+                    ghost:
+                      !!this.selectedPiece &&
+                      this.selectedPiece.parentNode.id == "sq-" + ci + "-" + cj
+                  },
+                  attrs: {
+                    src:
+                      "/images/pieces/" +
+                      V.getPpath(this.vr.board[ci][cj]) +
+                      ".svg"
                   }
-                )
+                })
               );
             }
-            if (this.settings.hints && hintSquares[ci][cj])
-            {
+            if (this.settings.hints && hintSquares[ci][cj]) {
               elems.push(
-                h(
-                  'img',
-                  {
-                    'class': {
-                      'mark-square': true,
-                    },
-                    attrs: {
-                      src: "/images/mark.svg",
-                    },
+                h("img", {
+                  class: {
+                    "mark-square": true
+                  },
+                  attrs: {
+                    src: "/images/mark.svg"
                   }
-                )
+                })
               );
             }
             return h(
-              'div',
+              "div",
               {
-                'class': {
-                  'board': true,
-                  ['board'+sizeY]: true,
-                  'light-square': (i+j)%2==0,
-                  'dark-square': (i+j)%2==1,
+                class: {
+                  board: true,
+                  ["board" + sizeY]: true,
+                  "light-square": (i + j) % 2 == 0,
+                  "dark-square": (i + j) % 2 == 1,
                   [this.settings.bcolor]: true,
-                  'in-shadow': this.vname=="Dark" && !this.analyze
-                    && (!this.userColor
-                      || !this.vr.enlightened[this.userColor][ci][cj]),
-                  'highlight': showLight && !!lm && lm.end.x == ci && lm.end.y == cj,
-                  'incheck': showLight && incheckSq[ci][cj],
+                  "in-shadow":
+                    this.vname == "Dark" &&
+                    !this.analyze &&
+                    (!this.userColor ||
+                      !this.vr.enlightened[this.userColor][ci][cj]),
+                  highlight:
+                    showLight && !!lm && lm.end.x == ci && lm.end.y == cj,
+                  incheck: showLight && incheckSq[ci][cj]
                 },
                 attrs: {
-                  id: getSquareId({x:ci,y:cj}),
-                },
+                  id: getSquareId({ x: ci, y: cj })
+                }
               },
               elems
             );
@@ -124,118 +136,134 @@ export default {
     );
     let elementArray = [gameDiv];
     const playingColor = this.userColor || "w"; //default for an observer
-    if (!!this.vr.reserve)
-    {
-      const shiftIdx = (playingColor=="w" ? 0 : 1);
+    if (this.vr.reserve) {
+      const shiftIdx = playingColor == "w" ? 0 : 1;
       let myReservePiecesArray = [];
-      for (let i=0; i<V.RESERVE_PIECES.length; i++)
-      {
-        myReservePiecesArray.push(h('div',
-        {
-          'class': {'board':true, ['board'+sizeY]:true},
-          attrs: { id: getSquareId({x:sizeX+shiftIdx,y:i}) }
-        },
-        [
-          h('img',
-          {
-            'class': {"piece":true, "reserve":true},
-            attrs: {
-              "src": "/images/pieces/" +
-                this.vr.getReservePpath(playingColor,i) + ".svg",
-            }
-          }),
-          h('sup',
-            {"class": { "reserve-count": true } },
-            [ this.vr.reserve[playingColor][V.RESERVE_PIECES[i]] ]
+      for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+        myReservePiecesArray.push(
+          h(
+            "div",
+            {
+              class: { board: true, ["board" + sizeY]: true },
+              attrs: { id: getSquareId({ x: sizeX + shiftIdx, y: i }) }
+            },
+            [
+              h("img", {
+                class: { piece: true, reserve: true },
+                attrs: {
+                  src:
+                    "/images/pieces/" +
+                    this.vr.getReservePpath(playingColor, i) +
+                    ".svg"
+                }
+              }),
+              h("sup", { class: { "reserve-count": true } }, [
+                this.vr.reserve[playingColor][V.RESERVE_PIECES[i]]
+              ])
+            ]
           )
-        ]));
+        );
       }
       let oppReservePiecesArray = [];
       const oppCol = V.GetOppCol(playingColor);
-      for (let i=0; i<V.RESERVE_PIECES.length; i++)
-      {
-        oppReservePiecesArray.push(h('div',
-        {
-          'class': {'board':true, ['board'+sizeY]:true},
-          attrs: { id: getSquareId({x:sizeX+(1-shiftIdx),y:i}) }
-        },
-        [
-          h('img',
-          {
-            'class': {"piece":true, "reserve":true},
-            attrs: {
-              "src": "/images/pieces/" +
-                this.vr.getReservePpath(oppCol,i) + ".svg",
-            }
-          }),
-          h('sup',
-            {"class": { "reserve-count": true } },
-            [ this.vr.reserve[oppCol][V.RESERVE_PIECES[i]] ]
+      for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+        oppReservePiecesArray.push(
+          h(
+            "div",
+            {
+              class: { board: true, ["board" + sizeY]: true },
+              attrs: { id: getSquareId({ x: sizeX + (1 - shiftIdx), y: i }) }
+            },
+            [
+              h("img", {
+                class: { piece: true, reserve: true },
+                attrs: {
+                  src:
+                    "/images/pieces/" +
+                    this.vr.getReservePpath(oppCol, i) +
+                    ".svg"
+                }
+              }),
+              h("sup", { class: { "reserve-count": true } }, [
+                this.vr.reserve[oppCol][V.RESERVE_PIECES[i]]
+              ])
+            ]
           )
-        ]));
+        );
       }
-      let reserves = h('div',
+      let reserves = h(
+        "div",
         {
-          'class':{
-            'game': true,
-            "reserve-div": true,
-          },
+          class: {
+            game: true,
+            "reserve-div": true
+          }
         },
         [
-          h('div',
+          h(
+            "div",
             {
-              'class': {
-                'row': true,
-                "reserve-row-1": true,
-              },
+              class: {
+                row: true,
+                "reserve-row-1": true
+              }
             },
             myReservePiecesArray
           ),
-          h('div',
-            { 'class': { 'row': true }},
-            oppReservePiecesArray
-          )
+          h("div", { class: { row: true } }, oppReservePiecesArray)
         ]
       );
       elementArray.push(reserves);
     }
     const boardElt = document.querySelector(".game");
-    if (this.choices.length > 0 && !!boardElt) //no choices to show at first drawing
-    {
+    if (this.choices.length > 0 && !!boardElt) {
+      //no choices to show at first drawing
       const squareWidth = boardElt.offsetWidth / sizeY;
       const offset = [boardElt.offsetTop, boardElt.offsetLeft];
       const choices = h(
-        'div',
+        "div",
         {
-          attrs: { "id": "choices" },
-          'class': { 'row': true },
+          attrs: { id: "choices" },
+          class: { row: true },
           style: {
-            "top": (offset[0] + (sizeY/2)*squareWidth-squareWidth/2) + "px",
-            "left": (offset[1] + squareWidth*(sizeY - this.choices.length)/2) + "px",
-            "width": (this.choices.length * squareWidth) + "px",
-            "height": squareWidth + "px",
-          },
+            top: offset[0] + (sizeY / 2) * squareWidth - squareWidth / 2 + "px",
+            left:
+              offset[1] +
+              (squareWidth * (sizeY - this.choices.length)) / 2 +
+              "px",
+            width: this.choices.length * squareWidth + "px",
+            height: squareWidth + "px"
+          }
         },
-        this.choices.map(m => { //a "choice" is a move
-          return h('div',
+        this.choices.map(m => {
+          //a "choice" is a move
+          return h(
+            "div",
             {
-              'class': {
-                'board': true,
-                ['board'+sizeY]: true,
+              class: {
+                board: true,
+                ["board" + sizeY]: true
               },
               style: {
-                'width': (100/this.choices.length) + "%",
-                'padding-bottom': (100/this.choices.length) + "%",
-              },
+                width: 100 / this.choices.length + "%",
+                "padding-bottom": 100 / this.choices.length + "%"
+              }
             },
-            [h('img',
-              {
-                attrs: { "src": '/images/pieces/' +
-                  V.getPpath(m.appear[0].c+m.appear[0].p) + '.svg' },
-                'class': { 'choice-piece': true },
-                on: {
-                  "click": e => { this.play(m); this.choices=[]; },
+            [
+              h("img", {
+                attrs: {
+                  src:
+                    "/images/pieces/" +
+                    V.getPpath(m.appear[0].c + m.appear[0].p) +
+                    ".svg"
                 },
+                class: { "choice-piece": true },
+                on: {
+                  click: () => {
+                    this.play(m);
+                    this.choices = [];
+                  }
+                }
               })
             ]
           );
@@ -245,95 +273,83 @@ export default {
     }
     let onEvents = {};
     // NOTE: click = mousedown + mouseup
-    if ('ontouchstart' in window)
-    {
+    if ("ontouchstart" in window) {
       onEvents = {
         on: {
           touchstart: this.mousedown,
           touchmove: this.mousemove,
-          touchend: this.mouseup,
-        },
+          touchend: this.mouseup
+        }
       };
-    }
-    else
-    {
+    } else {
       onEvents = {
         on: {
           mousedown: this.mousedown,
           mousemove: this.mousemove,
-          mouseup: this.mouseup,
-        },
+          mouseup: this.mouseup
+        }
       };
     }
-    return h(
-      'div',
-      onEvents,
-      elementArray
-    );
+    return h("div", onEvents, elementArray);
   },
   methods: {
     mousedown: function(e) {
       // Abort if a piece is already being processed, or target is not a piece.
       // NOTE: just looking at classList[0] because piece is the first assigned class
-      if (!!this.selectedPiece || e.target.classList[0] != "piece")
-        return;
+      if (!!this.selectedPiece || e.target.classList[0] != "piece") return;
       e.preventDefault(); //disable native drag & drop
       let parent = e.target.parentNode; //the surrounding square
       // Next few lines to center the piece on mouse cursor
       let rect = parent.getBoundingClientRect();
       this.start = {
-        x: rect.x + rect.width/2,
-        y: rect.y + rect.width/2,
-        id: parent.id,
+        x: rect.x + rect.width / 2,
+        y: rect.y + rect.width / 2,
+        id: parent.id
       };
       this.selectedPiece = e.target.cloneNode();
-      let spStyle = this.selectedPiece.style
+      let spStyle = this.selectedPiece.style;
       spStyle.position = "absolute";
       spStyle.top = 0;
       spStyle.display = "inline-block";
       spStyle.zIndex = 3000;
       const startSquare = getSquareFromId(parent.id);
       this.possibleMoves = [];
-      const color = (this.analyze ? this.vr.turn : this.userColor);
-      if (this.vr.canIplay(color,startSquare))
+      const color = this.analyze ? this.vr.turn : this.userColor;
+      if (this.vr.canIplay(color, startSquare))
         this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
       // Next line add moving piece just after current image
       // (required for Crazyhouse reserve)
       parent.insertBefore(this.selectedPiece, e.target.nextSibling);
     },
     mousemove: function(e) {
-      if (!this.selectedPiece)
-        return;
+      if (!this.selectedPiece) return;
       // There is an active element: move it around
-      const [offsetX,offsetY] = !!e.clientX
-        ? [e.clientX,e.clientY] //desktop browser
+      const [offsetX, offsetY] = e.clientX
+        ? [e.clientX, e.clientY] //desktop browser
         : [e.changedTouches[0].pageX, e.changedTouches[0].pageY]; //smartphone
-      this.selectedPiece.style.left = (offsetX-this.start.x) + "px";
-      this.selectedPiece.style.top = (offsetY-this.start.y) + "px";
+      this.selectedPiece.style.left = offsetX - this.start.x + "px";
+      this.selectedPiece.style.top = offsetY - this.start.y + "px";
     },
     mouseup: function(e) {
-      if (!this.selectedPiece)
-        return;
+      if (!this.selectedPiece) return;
       // There is an active element: obtain the move from start and end squares
       this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coords
-      const [offsetX,offsetY] = !!e.clientX
-        ? [e.clientX,e.clientY]
+      const [offsetX, offsetY] = e.clientX
+        ? [e.clientX, e.clientY]
         : [e.changedTouches[0].pageX, e.changedTouches[0].pageY];
       let landing = document.elementFromPoint(offsetX, offsetY);
       this.selectedPiece.style.zIndex = 3000;
       // Next condition: classList.contains(piece) fails because of marks
-      while (landing.tagName == "IMG")
-        landing = landing.parentNode;
-      if (this.start.id == landing.id) //one or multi clicks on same piece
+      while (landing.tagName == "IMG") landing = landing.parentNode;
+      if (this.start.id == landing.id)
+        //one or multi clicks on same piece
         return;
       // OK: process move attempt, landing is a square node
       let endSquare = getSquareFromId(landing.id);
       let moves = this.findMatchingMoves(endSquare);
       this.possibleMoves = [];
-      if (moves.length > 1)
-        this.choices = moves;
-      else if (moves.length==1)
-        this.play(moves[0]);
+      if (moves.length > 1) this.choices = moves;
+      else if (moves.length == 1) this.play(moves[0]);
       // Else: impossible move
       this.selectedPiece.parentNode.removeChild(this.selectedPiece);
       delete this.selectedPiece;
@@ -343,15 +359,14 @@ export default {
       // Run through moves list and return the matching set (if promotions...)
       let moves = [];
       this.possibleMoves.forEach(function(m) {
-        if (endSquare[0] == m.end.x && endSquare[1] == m.end.y)
-          moves.push(m);
+        if (endSquare[0] == m.end.x && endSquare[1] == m.end.y) moves.push(m);
       });
       return moves;
     },
     play: function(move) {
-      this.$emit('play-move', move);
-    },
-  },
+      this.$emit("play-move", move);
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/ChallengeList.vue b/client/src/components/ChallengeList.vue
index f6d3090e..92c42f7c 100644
--- a/client/src/components/ChallengeList.vue
+++ b/client/src/components/ChallengeList.vue
@@ -20,32 +20,29 @@ export default {
   props: ["challenges"],
   data: function() {
     return {
-      st: store.state,
+      st: store.state
     };
   },
   computed: {
     sortedChallenges: function() {
       // Show in order: challenges I sent, challenges I received, other challenges
-      let minAdded = Number.MAX_SAFE_INTEGER
-      let maxAdded = 0
+      let minAdded = Number.MAX_SAFE_INTEGER;
+      let maxAdded = 0;
       let augmentedChalls = this.challenges.map(c => {
         let priority = 0;
-        if (!!c.to && c.to == this.st.user.name)
-          priority = 1;
+        if (!!c.to && c.to == this.st.user.name) priority = 1;
         else if (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id)
           priority = 2;
-        if (c.added < minAdded)
-          minAdded = c.added;
-        if (c.added > maxAdded)
-          maxAdded = c.added
-        return Object.assign({}, c, {priority: priority});
+        if (c.added < minAdded) minAdded = c.added;
+        if (c.added > maxAdded) maxAdded = c.added;
+        return Object.assign({}, c, { priority: priority });
       });
       const deltaAdded = maxAdded - minAdded;
-      return augmentedChalls.sort((c1,c2) => {
+      return augmentedChalls.sort((c1, c2) => {
         return c2.priority - c1.priority + (c2.added - c1.added) / deltaAdded;
       });
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/Chat.vue b/client/src/components/Chat.vue
index ae85e736..7205f3b7 100644
--- a/client/src/components/Chat.vue
+++ b/client/src/components/Chat.vue
@@ -13,38 +13,40 @@ import { store } from "@/store";
 export default {
   name: "my-chat",
   // Prop 'pastChats' for corr games where chats are on server
-  props: ["players","pastChats","newChat"],
+  props: ["players", "pastChats", "newChat"],
   data: function() {
     return {
       st: store.state,
-      chats: [], //chat messages after human game
+      chats: [] //chat messages after human game
     };
   },
   watch: {
     newChat: function(chat) {
       if (chat.msg != "")
-        this.chats.unshift({msg:chat.msg, name:chat.name || "@nonymous"});
-    },
+        this.chats.unshift({ msg: chat.msg, name: chat.name || "@nonymous" });
+    }
   },
   methods: {
     classObject: function(chat) {
       return {
         "my-chatmsg": chat.name == this.st.user.name,
-        "opp-chatmsg": !!this.players && this.players.some(
-          p => p.name == chat.name && p.name != this.st.user.name)
+        "opp-chatmsg":
+          !!this.players &&
+          this.players.some(
+            p => p.name == chat.name && p.name != this.st.user.name
+          )
       };
     },
     sendChat: function() {
       let chatInput = document.getElementById("inputChat");
       const chatTxt = chatInput.value.trim();
-      if (chatTxt == "")
-        return; //nothing to send
+      if (chatTxt == "") return; //nothing to send
       chatInput.value = "";
-      const chat = {msg:chatTxt, name: this.st.user.name || "@nonymous"};
+      const chat = { msg: chatTxt, name: this.st.user.name || "@nonymous" };
       this.$emit("mychat", chat);
       this.chats.unshift(chat);
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/ComputerGame.vue b/client/src/components/ComputerGame.vue
index 3bbdc046..402bc7fa 100644
--- a/client/src/components/ComputerGame.vue
+++ b/client/src/components/ComputerGame.vue
@@ -9,7 +9,7 @@ import Worker from "worker-loader!@/playCompMove";
 export default {
   name: "my-computer-game",
   components: {
-    BaseGame,
+    BaseGame
   },
   // gameInfo: fen + mode + vname
   // mode: "auto" (game comp vs comp) or "versus" (normal)
@@ -22,7 +22,7 @@ export default {
       // Web worker to play computer moves without freezing interface:
       timeStart: undefined, //time when computer starts thinking
       compThink: false, //avoid asking a new move while one is being searched
-      compWorker: null,
+      compWorker: null
     };
   },
   watch: {
@@ -30,13 +30,11 @@ export default {
       this.launchGame();
     },
     "gameInfo.score": function(newScore) {
-      if (newScore != "*")
-      {
+      if (newScore != "*") {
         this.game.score = newScore; //user action
-        if (!this.compThink)
-          this.$emit("game-stopped"); //otherwise wait for comp
+        if (!this.compThink) this.$emit("game-stopped"); //otherwise wait for comp
       }
-    },
+    }
   },
   // Modal end of game, and then sub-components
   created: function() {
@@ -44,60 +42,51 @@ export default {
     this.compWorker = new Worker();
     this.compWorker.onmessage = e => {
       let compMove = e.data;
-      if (!compMove)
-      {
+      if (!compMove) {
         this.compThink = false;
         this.$emit("game-stopped"); //no more moves: mate or stalemate
         return; //after game ends, no more moves, nothing to do
       }
-      if (!Array.isArray(compMove))
-        compMove = [compMove]; //to deal with MarseilleRules
+      if (!Array.isArray(compMove)) compMove = [compMove]; //to deal with MarseilleRules
       // Small delay for the bot to appear "more human"
-      const delay = Math.max(500-(Date.now()-this.timeStart), 0);
+      const delay = Math.max(500 - (Date.now() - this.timeStart), 0);
       setTimeout(() => {
-        if (this.currentUrl != document.location.href)
-          return; //page change
+        if (this.currentUrl != document.location.href) return; //page change
         // NOTE: Dark and 2-moves are incompatible
-        const animate = (this.gameInfo.vname != "Dark");
-        const animDelay = (animate ? 250 : 0);
+        const animate = this.gameInfo.vname != "Dark";
+        const animDelay = animate ? 250 : 0;
         let moveIdx = 0;
         let self = this;
         (function executeMove() {
           self.$set(self.game, "moveToPlay", compMove[moveIdx++]);
-          if (moveIdx >= compMove.length)
-          {
+          if (moveIdx >= compMove.length) {
             self.compThink = false;
-            if (self.game.score != "*") //user action
+            if (self.game.score != "*")
+              //user action
               self.$emit("game-stopped");
-          }
-          else
-            setTimeout(executeMove, 500 + animDelay);
+          } else setTimeout(executeMove, 500 + animDelay);
         })();
       }, delay);
-    }
-    if (!!this.gameInfo.fen)
-      this.launchGame();
+    };
+    if (this.gameInfo.fen) this.launchGame();
   },
   methods: {
     launchGame: function() {
-      this.compWorker.postMessage(["scripts",this.gameInfo.vname]);
-      this.compWorker.postMessage(["init",this.gameInfo.fen]);
+      this.compWorker.postMessage(["scripts", this.gameInfo.vname]);
+      this.compWorker.postMessage(["init", this.gameInfo.fen]);
       this.vr = new V(this.gameInfo.fen);
-      const mycolor = (Math.random() < 0.5 ? "w" : "b");
-      let players = [{name:"Myself"},{name:"Computer"}];
-      if (mycolor == "b")
-        players = players.reverse();
+      const mycolor = Math.random() < 0.5 ? "w" : "b";
+      let players = [{ name: "Myself" }, { name: "Computer" }];
+      if (mycolor == "b") players = players.reverse();
       this.currentUrl = document.location.href; //to avoid playing outside page
       // NOTE: fen and fenStart are redundant in game object
-      this.game = Object.assign({},
-        this.gameInfo,
-        {
-          fenStart: this.gameInfo.fen,
-          players: players,
-          mycolor: mycolor,
-          score: "*",
-        });
-      this.compWorker.postMessage(["init",this.gameInfo.fen]);
+      this.game = Object.assign({}, this.gameInfo, {
+        fenStart: this.gameInfo.fen,
+        players: players,
+        mycolor: mycolor,
+        score: "*"
+      });
+      this.compWorker.postMessage(["init", this.gameInfo.fen]);
       if (mycolor != "w" || this.gameInfo.mode == "auto")
         this.playComputerMove();
     },
@@ -107,14 +96,14 @@ export default {
       this.compWorker.postMessage(["askmove"]);
     },
     processMove: function(move) {
-      if (this.game.score != "*")
-        return;
+      if (this.game.score != "*") return;
       // Send the move to web worker (including his own moves)
-      this.compWorker.postMessage(["newmove",move]);
+      this.compWorker.postMessage(["newmove", move]);
       // subTurn condition for Marseille (and Avalanche) rules
-      if ((!this.vr.subTurn || this.vr.subTurn <= 1)
-        && (this.gameInfo.mode == "auto" || this.vr.turn != this.game.mycolor))
-      {
+      if (
+        (!this.vr.subTurn || this.vr.subTurn <= 1) &&
+        (this.gameInfo.mode == "auto" || this.vr.turn != this.game.mycolor)
+      ) {
         this.playComputerMove();
       }
     },
@@ -122,7 +111,7 @@ export default {
       this.game.score = score;
       this.game.scoreMsg = scoreMsg;
       this.$emit("game-over", score); //bubble up to Rules.vue
-    },
-  },
+    }
+  }
 };
 </script>
diff --git a/client/src/components/ContactForm.vue b/client/src/components/ContactForm.vue
index 61796bfa..9c70a559 100644
--- a/client/src/components/ContactForm.vue
+++ b/client/src/components/ContactForm.vue
@@ -27,13 +27,12 @@ export default {
     return {
       enterTime: Number.MAX_SAFE_INTEGER, //for a basic anti-bot strategy
       st: store.state,
-      infoMsg: "",
+      infoMsg: ""
     };
   },
   methods: {
     trySetEnterTime: function(event) {
-      if (!!event.target.checked)
-      {
+      if (event.target.checked) {
         this.enterTime = Date.now();
         this.infoMsg = "";
       }
@@ -41,17 +40,21 @@ export default {
     trySendMessage: function() {
       // Basic anti-bot strategy:
       const exitTime = Date.now();
-      if (exitTime - this.enterTime < 5000)
-        return;
+      if (exitTime - this.enterTime < 5000) return;
       let email = document.getElementById("userEmail");
       let subject = document.getElementById("mailSubject");
       let content = document.getElementById("mailContent");
-      const error = checkNameEmail({email: email});
-      if (!!error)
-        return alert(error);
-      if (content.value.trim().length == 0)
-        return alert(this.st.tr["Empty message"]);
-      if (subject.value.trim().length == 0 && !confirm(this.st.tr["No subject. Send anyway?"]))
+      let error = checkNameEmail({ email: email });
+      if (!error && content.value.trim().length == 0)
+        error = this.st.tr["Empty message"];
+      if (error) {
+        alert(error);
+        return;
+      }
+      if (
+        subject.value.trim().length == 0 &&
+        !confirm(this.st.tr["No subject. Send anyway?"])
+      )
         return;
 
       // Message sending:
@@ -61,7 +64,7 @@ export default {
         {
           email: email.value,
           subject: subject.value,
-          content: content.value,
+          content: content.value
         },
         () => {
           this.infoMsg = "Email sent!";
@@ -69,8 +72,8 @@ export default {
           content.value = "";
         }
       );
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/GameList.vue b/client/src/components/GameList.vue
index 2a4b0239..7125fde8 100644
--- a/client/src/components/GameList.vue
+++ b/client/src/components/GameList.vue
@@ -22,21 +22,20 @@ import { store } from "@/store";
 import { GameStorage } from "@/utils/gameStorage";
 export default {
   name: "my-game-list",
-  props: ["games","showBoth"],
+  props: ["games", "showBoth"],
   data: function() {
     return {
       st: store.state,
-      showCadence: true,
+      showCadence: true
     };
   },
   mounted: function() {
     // timeout to avoid calling too many time the adjust method
     let timeoutLaunched = false;
-    window.addEventListener("resize", (e) => {
-      if (!timeoutLaunched)
-      {
+    window.addEventListener("resize", () => {
+      if (!timeoutLaunched) {
         timeoutLaunched = true;
-        setTimeout( () => {
+        setTimeout(() => {
           this.showCadence = window.innerWidth >= 425; //TODO: arbitrary
           timeoutLaunched = false;
         }, 500);
@@ -46,55 +45,64 @@ export default {
   computed: {
     sortedGames: function() {
       // Show in order: games where it's my turn, my running games, my games, other games
-      let minCreated = Number.MAX_SAFE_INTEGER
-      let maxCreated = 0
+      let minCreated = Number.MAX_SAFE_INTEGER;
+      let maxCreated = 0;
       let augmentedGames = this.games.map(g => {
         let priority = 0;
-        if (g.players.some(p => p.uid == this.st.user.id || p.sid == this.st.user.sid))
-        {
+        if (
+          g.players.some(
+            p => p.uid == this.st.user.id || p.sid == this.st.user.sid
+          )
+        ) {
           priority++;
-          if (g.score == "*")
-          {
+          if (g.score == "*") {
             priority++;
-            const myColor = g.players[0].uid == this.st.user.id
-                || g.players[0].sid == this.st.user.sid
-              ? "w"
-              : "b";
+            const myColor =
+              g.players[0].uid == this.st.user.id ||
+              g.players[0].sid == this.st.user.sid
+                ? "w"
+                : "b";
             // I play in this game, so g.fen will be defined
-            if (!!g.fen.match(" " + myColor + " "))
-              priority++;
+            if (g.fen.match(" " + myColor + " ")) priority++;
           }
         }
-        if (g.created < minCreated)
-          minCreated = g.created;
-        if (g.created > maxCreated)
-          maxCreated = g.created;
-        return Object.assign({}, g, {priority: priority, myTurn: priority==3});
+        if (g.created < minCreated) minCreated = g.created;
+        if (g.created > maxCreated) maxCreated = g.created;
+        return Object.assign({}, g, {
+          priority: priority,
+          myTurn: priority == 3
+        });
       });
       const deltaCreated = maxCreated - minCreated;
-      return augmentedGames.sort((g1,g2) => {
-        return g2.priority - g1.priority +
-          (g2.created - g1.created) / deltaCreated;
+      return augmentedGames.sort((g1, g2) => {
+        return (
+          g2.priority - g1.priority + (g2.created - g1.created) / deltaCreated
+        );
       });
-    },
+    }
   },
   methods: {
     player_s: function(g) {
       if (this.showBoth)
-        return (g.players[0].name || "@nonymous") + " - " + (g.players[1].name || "@nonymous");
-      if (this.st.user.sid == g.players[0].sid || this.st.user.id == g.players[0].uid)
+        return (
+          (g.players[0].name || "@nonymous") +
+          " - " +
+          (g.players[1].name || "@nonymous")
+        );
+      if (
+        this.st.user.sid == g.players[0].sid ||
+        this.st.user.id == g.players[0].uid
+      )
         return g.players[1].name || "@nonymous";
       return g.players[0].name || "@nonymous";
     },
     deleteGame: function(game, e) {
-      if (game.score != "*")
-      {
-        if (confirm(this.st.tr["Remove game?"]))
-          GameStorage.remove(game.id);
+      if (game.score != "*") {
+        if (confirm(this.st.tr["Remove game?"])) GameStorage.remove(game.id);
         e.stopPropagation();
       }
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/Language.vue b/client/src/components/Language.vue
index e7243555..bdfd3417 100644
--- a/client/src/components/Language.vue
+++ b/client/src/components/Language.vue
@@ -25,21 +25,20 @@ export default {
   name: "my-language",
   data: function() {
     return {
-      st: store.state,
+      st: store.state
     };
   },
   mounted: function() {
     // NOTE: better style would be in pug directly, but how?
     document.querySelectorAll("#langSelect > option").forEach(opt => {
-      if (opt.value == this.st.lang)
-        opt.selected = true;
+      if (opt.value == this.st.lang) opt.selected = true;
     });
   },
   methods: {
     setLanguage: function(e) {
       localStorage["lang"] = e.target.value;
       store.setLanguage(e.target.value);
-    },
-  },
+    }
+  }
 };
 </script>
diff --git a/client/src/components/MoveList.vue b/client/src/components/MoveList.vue
index 0dede660..29e9a9fe 100644
--- a/client/src/components/MoveList.vue
+++ b/client/src/components/MoveList.vue
@@ -1,120 +1,97 @@
 <script>
 import { store } from "@/store";
 export default {
-  name: 'my-move-list',
-  props: ["moves","cursor","score","message","firstNum"],
+  name: "my-move-list",
+  props: ["moves", "cursor", "score", "message", "firstNum"],
   watch: {
     cursor: function(newCursor) {
-      if (window.innerWidth <= 767)
-        return; //moves list is below: scrolling would hide chessboard
+      if (window.innerWidth <= 767) return; //moves list is below: scrolling would hide chessboard
       // Count grouped moves until the cursor (if multi-moves):
       let groupsCount = -1;
       let curCol = undefined;
-      for (let i=0; i<newCursor; i++)
-      {
+      for (let i = 0; i < newCursor; i++) {
         const m = this.moves[i];
-        if (m.color != curCol)
-        {
+        if (m.color != curCol) {
           groupsCount++;
           curCol = m.color;
         }
       }
       // $nextTick to wait for table > tr to be rendered
-      this.$nextTick( () => {
-        let rows = document.querySelectorAll('#movesList tr');
-        if (rows.length > 0)
-        {
-          rows[Math.floor(Math.max(groupsCount,0)/2)].scrollIntoView({
+      this.$nextTick(() => {
+        let rows = document.querySelectorAll("#movesList tr");
+        if (rows.length > 0) {
+          rows[Math.floor(Math.max(groupsCount, 0) / 2)].scrollIntoView({
             behavior: "auto",
-            block: "nearest",
+            block: "nearest"
           });
         }
       });
-    },
+    }
   },
   render(h) {
-    if (this.moves.length == 0)
-      return;
+    if (this.moves.length == 0) return h("div");
     let tableContent = [];
     let moveCounter = 0;
     let tableRow = undefined;
     let moveCells = undefined;
     let curCellContent = "";
     let firstIndex = 0;
-    for (let i=0; i<this.moves.length; i++)
-    {
-      if (this.moves[i].color == "w")
-      {
-        if (i == 0 || i>0 && this.moves[i-1].color=="b")
-        {
-          if (!!tableRow)
-          {
+    for (let i = 0; i < this.moves.length; i++) {
+      if (this.moves[i].color == "w") {
+        if (i == 0 || (i > 0 && this.moves[i - 1].color == "b")) {
+          if (tableRow) {
             tableRow.children = moveCells;
             tableContent.push(tableRow);
           }
           moveCells = [
-            h(
-              "td",
-              { domProps: { innerHTML: (++moveCounter) + "." } }
-            )
+            h("td", { domProps: { innerHTML: ++moveCounter + "." } })
           ];
-          tableRow = h(
-            "tr",
-            { }
-          );
+          tableRow = h("tr", {});
           curCellContent = "";
           firstIndex = i;
         }
       }
       // Next condition is fine because even if the first move is black,
       // there will be the "..." which count as white move.
-      else if (this.moves[i].color == "b" && this.moves[i-1].color == "w")
+      else if (this.moves[i].color == "b" && this.moves[i - 1].color == "w")
         firstIndex = i;
       curCellContent += this.moves[i].notation;
-      if (i < this.moves.length-1 && this.moves[i+1].color == this.moves[i].color)
+      if (
+        i < this.moves.length - 1 &&
+        this.moves[i + 1].color == this.moves[i].color
+      )
         curCellContent += ",";
-      else //color change
-      {
+      //color change
+      else {
         moveCells.push(
-          h(
-            "td",
-            {
-              domProps: { innerHTML: curCellContent },
-              on: { click: () => this.gotoMove(i) },
-              "class": { "highlight-lm":
-                this.cursor >= firstIndex && this.cursor <= i },
+          h("td", {
+            domProps: { innerHTML: curCellContent },
+            on: { click: () => this.gotoMove(i) },
+            class: {
+              "highlight-lm": this.cursor >= firstIndex && this.cursor <= i
             }
-          )
+          })
         );
         curCellContent = "";
       }
     }
     // Complete last row, which might not be full:
-    if (moveCells.length-1 == 1)
-    {
-      moveCells.push(
-        h(
-          "td",
-          { domProps: { innerHTML: "" } }
-        )
-      );
+    if (moveCells.length - 1 == 1) {
+      moveCells.push(h("td", { domProps: { innerHTML: "" } }));
     }
     tableRow.children = moveCells;
     tableContent.push(tableRow);
     let rootElements = [];
-    if (!!this.score && this.score != "*")
-    {
-      const scoreDiv = h("div",
+    if (!!this.score && this.score != "*") {
+      const scoreDiv = h(
+        "div",
         {
           id: "scoreInfo",
           style: {
-            display: this.score!="*" ? "block" : "none",
-          },
+            display: this.score != "*" ? "block" : "none"
+          }
         },
-        [
-          h("p", this.score),
-          h("p", store.state.tr[this.message]),
-        ]
+        [h("p", this.score), h("p", store.state.tr[this.message])]
       );
       rootElements.push(scoreDiv);
     }
@@ -122,23 +99,20 @@ export default {
       h(
         "table",
         {
-          "class": {
-            "moves-list": true,
-          },
+          class: {
+            "moves-list": true
+          }
         },
         tableContent
       )
     );
-    return h(
-      "div",
-      { },
-      rootElements);
+    return h("div", {}, rootElements);
   },
   methods: {
     gotoMove: function(index) {
       this.$emit("goto-move", index);
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/Settings.vue b/client/src/components/Settings.vue
index f1031df8..e5a9d9c7 100644
--- a/client/src/components/Settings.vue
+++ b/client/src/components/Settings.vue
@@ -32,21 +32,21 @@ export default {
   name: "my-settings",
   data: function() {
     return {
-      st: store.state,
+      st: store.state
     };
   },
   methods: {
     updateSettings: function(event) {
-      const propName =
-        event.target.id.substr(3).replace(/^\w/, c => c.toLowerCase())
-      let value = (["bcolor","sound"].includes(propName)
+      const propName = event.target.id
+        .substr(3)
+        .replace(/^\w/, c => c.toLowerCase());
+      let value = ["bcolor", "sound"].includes(propName)
         ? event.target.value
-        : event.target.checked);
-      if (propName == "sound")
-        value = parseInt(value);
+        : event.target.checked;
+      if (propName == "sound") value = parseInt(value);
       store.updateSetting(propName, value);
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/components/UpsertUser.vue b/client/src/components/UpsertUser.vue
index eb94dedd..222a8625 100644
--- a/client/src/components/UpsertUser.vue
+++ b/client/src/components/UpsertUser.vue
@@ -35,34 +35,30 @@ import { store } from "@/store";
 import { checkNameEmail } from "@/data/userCheck";
 import { ajax } from "@/utils/ajax";
 export default {
-  name: 'my-upsert-user',
+  name: "my-upsert-user",
   data: function() {
     return {
       nameOrEmail: "", //for login
       logStage: "Login", //or Register
       infoMsg: "",
       enterTime: Number.MAX_SAFE_INTEGER, //for a basic anti-bot strategy
-      st: store.state,
+      st: store.state
     };
   },
   watch: {
     nameOrEmail: function(newValue) {
-      if (newValue.indexOf('@') >= 0)
-      {
+      if (newValue.indexOf("@") >= 0) {
         this.st.user.email = newValue;
         this.st.user.name = "";
-      }
-      else
-      {
+      } else {
         this.st.user.name = newValue;
         this.st.user.email = "";
       }
-    },
+    }
   },
   computed: {
     submitMessage: function() {
-      switch (this.stage)
-      {
+      switch (this.stage) {
         case "Login":
           return "Go";
         case "Register":
@@ -70,26 +66,25 @@ export default {
         case "Update":
           return "Apply";
       }
+      return "Never reached";
     },
     stage: function() {
       return this.st.user.id > 0 ? "Update" : this.logStage;
-    },
+    }
   },
   methods: {
     trySetEnterTime: function(event) {
-      if (!!event.target.checked)
-      {
+      if (event.target.checked) {
         this.infoMsg = "";
         this.enterTime = Date.now();
       }
     },
     toggleStage: function() {
       // Loop login <--> register (update is for logged-in users)
-      this.logStage = (this.logStage == "Login" ? "Register" : "Login");
+      this.logStage = this.logStage == "Login" ? "Register" : "Login";
     },
     ajaxUrl: function() {
-      switch (this.stage)
-      {
+      switch (this.stage) {
         case "Login":
           return "/sendtoken";
         case "Register":
@@ -97,10 +92,10 @@ export default {
         case "Update":
           return "/update";
       }
+      return "Never reached";
     },
     ajaxMethod: function() {
-      switch (this.stage)
-      {
+      switch (this.stage) {
         case "Login":
           return "GET";
         case "Register":
@@ -108,10 +103,10 @@ export default {
         case "Update":
           return "PUT";
       }
+      return "Never reached";
     },
     infoMessage: function() {
-      switch (this.stage)
-      {
+      switch (this.stage) {
         case "Login":
           return "Connection token sent. Check your emails!";
         case "Register":
@@ -119,29 +114,31 @@ export default {
         case "Update":
           return "Modifications applied!";
       }
+      return "Never reached";
     },
     onSubmit: function() {
       // Basic anti-bot strategy:
       const exitTime = Date.now();
-      if (this.stage == "Register" && exitTime - this.enterTime < 5000)
-        return;
+      if (this.stage == "Register" && exitTime - this.enterTime < 5000) return;
       let error = undefined;
-      if (this.stage == 'Login')
-      {
-        const type = (this.nameOrEmail.indexOf('@') >= 0 ? "email" : "name");
-        error = checkNameEmail({[type]: this.nameOrEmail});
+      if (this.stage == "Login") {
+        const type = this.nameOrEmail.indexOf("@") >= 0 ? "email" : "name";
+        error = checkNameEmail({ [type]: this.nameOrEmail });
+      } else error = checkNameEmail(this.st.user);
+      if (error) {
+        alert(error);
+        return;
       }
-      else
-        error = checkNameEmail(this.st.user);
-      if (!!error)
-        return alert(error);
       this.infoMsg = "Processing... Please wait";
-      ajax(this.ajaxUrl(), this.ajaxMethod(),
-        this.stage == "Login" ? { nameOrEmail: this.nameOrEmail } : this.st.user,
-        res => {
+      ajax(
+        this.ajaxUrl(),
+        this.ajaxMethod(),
+        this.stage == "Login"
+          ? { nameOrEmail: this.nameOrEmail }
+          : this.st.user,
+        () => {
           this.infoMsg = this.infoMessage();
-          if (this.stage != "Update")
-            this.nameOrEmail = "";
+          if (this.stage != "Update") this.nameOrEmail = "";
         },
         err => {
           this.infoMsg = "";
@@ -152,8 +149,8 @@ export default {
     doLogout: function() {
       document.getElementById("modalUser").checked = false;
       this.$router.push("/logout");
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/data/challengeCheck.js b/client/src/data/challengeCheck.js
index 626b7cf6..789952f8 100644
--- a/client/src/data/challengeCheck.js
+++ b/client/src/data/challengeCheck.js
@@ -1,29 +1,22 @@
 import { extractTime } from "@/utils/timeControl";
 
-export function checkChallenge(c)
-{
+export function checkChallenge(c) {
   const vid = parseInt(c.vid);
-  if (isNaN(vid) || vid <= 0)
-    return "Please select a variant";
+  if (isNaN(vid) || vid <= 0) return "Please select a variant";
 
   const tc = extractTime(c.cadence);
-  if (!tc)
-    return "Wrong time control";
+  if (!tc) return "Wrong time control";
 
   // Basic alphanumeric check for opponent name
-  if (!!c.to)
-  {
-     // NOTE: slightly redundant (see data/userCheck.js)
-    if (!c.to.match(/^[\w]+$/))
-      return "Wrong characters in opponent name";
+  if (c.to) {
+    // NOTE: slightly redundant (see data/userCheck.js)
+    if (!c.to.match(/^[\w]+$/)) return "Wrong characters in opponent name";
   }
 
   // Allow custom FEN (and check it) only for individual challenges
-  if (c.fen.length > 0 && !!c.to)
-  {
-    if (!V.IsGoodFen(c.fen))
-      return "Bad FEN string";
-  }
-  else
-    c.fen = "";
+  if (c.fen.length > 0 && !!c.to) {
+    if (!V.IsGoodFen(c.fen)) return "Bad FEN string";
+  } else c.fen = "";
+
+  return "";
 }
diff --git a/client/src/data/problemCheck.js b/client/src/data/problemCheck.js
index 3b9ccb46..652daaed 100644
--- a/client/src/data/problemCheck.js
+++ b/client/src/data/problemCheck.js
@@ -1,9 +1,8 @@
-export function checkProblem(p)
-{
+export function checkProblem(p) {
   const vid = parseInt(p.vid);
-  if (isNaN(vid) || vid <= 0)
-    return "Please select a variant";
+  if (isNaN(vid) || vid <= 0) return "Please select a variant";
 
-  if (!V.IsGoodFen(p.fen))
-    return "Bad FEN string";
+  if (!V.IsGoodFen(p.fen)) return "Bad FEN string";
+
+  return "";
 }
diff --git a/client/src/data/userCheck.js b/client/src/data/userCheck.js
index 9eb85622..dac8ef6f 100644
--- a/client/src/data/userCheck.js
+++ b/client/src/data/userCheck.js
@@ -1,17 +1,11 @@
-export function checkNameEmail(o)
-{
-  if (typeof o.name === "string")
-  {
-    if (o.name.length == 0)
-      return "Empty name";
-    if (!o.name.match(/^[\w]+$/))
-      return "Bad characters in name";
+export function checkNameEmail(o) {
+  if (typeof o.name === "string") {
+    if (o.name.length == 0) return "Empty name";
+    if (!o.name.match(/^[\w]+$/)) return "Bad characters in name";
   }
-  if (typeof o.email === "string")
-  {
-    if (o.email.length == 0)
-      return "Empty email";
-    if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/))
-      return "Bad characters in email";
+  if (typeof o.email === "string") {
+    if (o.email.length == 0) return "Empty email";
+    if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/)) return "Bad characters in email";
   }
+  return "";
 }
diff --git a/client/src/main.js b/client/src/main.js
index f1e23cbd..db70c3e1 100644
--- a/client/src/main.js
+++ b/client/src/main.js
@@ -11,17 +11,17 @@ new Vue({
     return h(App);
   },
   created: function() {
-    window.doClick = (elemId) => { document.getElementById(elemId).click() };
-    document.addEventListener("keydown", (e) => {
-      if (e.code === "Escape")
-      {
+    window.doClick = elemId => {
+      document.getElementById(elemId).click();
+    };
+    document.addEventListener("keydown", e => {
+      if (e.code === "Escape") {
         let modalBoxes = document.querySelectorAll("[id^='modal']");
         modalBoxes.forEach(m => {
-          if (m.checked && m.id != "modalWelcome")
-            m.checked = false;
+          if (m.checked && m.id != "modalWelcome") m.checked = false;
         });
       }
     });
     store.initialize();
-  },
+  }
 }).$mount("#app");
diff --git a/client/src/playCompMove.js b/client/src/playCompMove.js
index b93a8d1e..d8dd2d47 100644
--- a/client/src/playCompMove.js
+++ b/client/src/playCompMove.js
@@ -1,22 +1,23 @@
 // Logic to play a computer move in a web worker
-onmessage = async function(e)
-{
-  switch (e.data[0])
-  {
-    case "scripts":
+onmessage = async function(e) {
+  switch (e.data[0]) {
+    case "scripts": {
       const vModule = await import("@/variants/" + e.data[1] + ".js");
       self.V = vModule.VariantRules;
       break;
-    case "init":
+    }
+    case "init": {
       const fen = e.data[1];
       self.vr = new self.V(fen);
       break;
+    }
     case "newmove":
       self.vr.play(e.data[1]);
       break;
-    case "askmove":
+    case "askmove": {
       const compMove = self.vr.getComputerMove();
       postMessage(compMove);
       break;
+    }
   }
-}
+};
diff --git a/client/src/router.js b/client/src/router.js
index daaefce8..c5afb17d 100644
--- a/client/src/router.js
+++ b/client/src/router.js
@@ -5,69 +5,67 @@ import Hall from "./views/Hall.vue";
 Vue.use(Router);
 
 function loadView(view) {
-  return () => import(/* webpackChunkName: "view-[request]" */ `@/views/${view}.vue`)
+  return () =>
+    import(/* webpackChunkName: "view-[request]" */ `@/views/${view}.vue`);
 }
 
-import { ajax } from "@/utils/ajax";
-import { store } from "@/store";
-
 const router = new Router({
   routes: [
     {
       path: "/",
       name: "hall",
-      component: Hall,
+      component: Hall
     },
     {
       path: "/variants",
       name: "variants",
-      component: loadView("Variants"),
+      component: loadView("Variants")
     },
     {
       path: "/variants/:vname([a-zA-Z0-9]+)",
       name: "rules",
-      component: loadView("Rules"),
+      component: loadView("Rules")
     },
     {
       path: "/authenticate/:token",
       name: "authenticate",
-      component: loadView("Auth"),
+      component: loadView("Auth")
     },
     {
       path: "/logout",
       name: "logout",
-      component: loadView("Logout"),
+      component: loadView("Logout")
     },
     {
       path: "/problems",
       name: "myproblems",
-      component: loadView("Problems"),
+      component: loadView("Problems")
     },
     {
       path: "/mygames",
       name: "mygames",
-      component: loadView("MyGames"),
+      component: loadView("MyGames")
     },
     {
       path: "/game/:id([a-zA-Z0-9]+)",
       name: "game",
-      component: loadView("Game"),
+      component: loadView("Game")
     },
     {
       path: "/analyse/:vname([a-zA-Z0-9]+)",
       name: "analyse",
-      component: loadView("Analyse"),
+      component: loadView("Analyse")
     },
     {
       path: "/about",
       name: "about",
-      component: loadView("About"),
+      component: loadView("About")
     },
     {
       path: "/news",
       name: "news",
-      component: loadView("News"),
-    },
+      component: loadView("News")
+    }
   ]
 });
 
diff --git a/client/src/store.js b/client/src/store.js
index 52a5247b..373aa288 100644
--- a/client/src/store.js
+++ b/client/src/store.js
@@ -2,21 +2,21 @@ import { ajax } from "./utils/ajax";
 import { getRandString } from "./utils/alea";
 
 // Global store: see https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87
-export const store =
-{
+export const store = {
   state: {
     variants: [],
     tr: {},
     user: {},
     settings: {},
-    lang: "",
+    lang: ""
   },
   socketCloseListener: null,
   initialize() {
-    ajax("/variants", "GET", res => { this.state.variants = res.variantArray; });
+    ajax("/variants", "GET", res => {
+      this.state.variants = res.variantArray;
+    });
     let mysid = localStorage.getItem("mysid");
-    if (!mysid)
-    {
+    if (!mysid) {
       mysid = getRandString();
       localStorage.setItem("mysid", mysid); //done only once (unless user clear browser data)
     }
@@ -26,22 +26,26 @@ export const store =
       name: localStorage.getItem("myname") || "", //"" for "anonymous"
       email: "", //unknown yet
       notify: false, //email notifications
-      sid: mysid,
+      sid: mysid
     };
     // Slow verification through the server:
     // NOTE: still superficial identity usurpation possible, but difficult.
     ajax("/whoami", "GET", res => {
       this.state.user.id = res.id;
       const storedId = localStorage.getItem("myid");
-      if (res.id > 0 && !storedId) //user cleared localStorage
+      if (res.id > 0 && !storedId)
+        //user cleared localStorage
         localStorage.setItem("myid", res.id);
-      else if (res.id == 0 && !!storedId) //user cleared cookie
+      else if (res.id == 0 && !!storedId)
+        //user cleared cookie
         localStorage.removeItem("myid");
       this.state.user.name = res.name;
       const storedName = localStorage.getItem("myname");
-      if (!!res.name && !storedName) //user cleared localStorage
+      if (!!res.name && !storedName)
+        //user cleared localStorage
         localStorage.setItem("myname", res.name);
-      else if (!res.name && !!storedName) //user cleared cookie
+      else if (!res.name && !!storedName)
+        //user cleared cookie
         localStorage.removeItem("myname");
       this.state.user.email = res.email;
       this.state.user.notify = res.notify;
@@ -51,13 +55,12 @@ export const store =
       bcolor: localStorage.getItem("bcolor") || "lichess",
       sound: parseInt(localStorage.getItem("sound")) || 1,
       hints: localStorage.getItem("hints") == "true",
-      highlight: localStorage.getItem("highlight") == "true",
+      highlight: localStorage.getItem("highlight") == "true"
     };
-    const supportedLangs = ["en","es","fr"];
-    this.state.lang = localStorage["lang"] ||
-      (supportedLangs.includes(navigator.language)
-        ? navigator.language
-        : "en");
+    const supportedLangs = ["en", "es", "fr"];
+    this.state.lang =
+      localStorage["lang"] ||
+      (supportedLangs.includes(navigator.language) ? navigator.language : "en");
     this.setTranslations();
   },
   updateSetting: function(propName, value) {
@@ -72,5 +75,5 @@ export const store =
   setLanguage(lang) {
     this.state.lang = lang;
     this.setTranslations();
-  },
+  }
 };
diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index c9653358..7fd40fc5 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -1,54 +1,55 @@
-export const translations =
-{
-  "Abort": "Abort",
-  "About": "About",
+export const translations = {
+  Abort: "Abort",
+  About: "About",
   "Accept draw?": "Accept draw?",
-  "All": "All",
-  "Analyse": "Analyse",
-  "Analyse in Dark mode makes no sense!": "Analyse in Dark mode makes no sense!",
+  All: "All",
+  Analyse: "Analyse",
+  "Analyse in Dark mode makes no sense!":
+    "Analyse in Dark mode makes no sense!",
   "Are you sure?": "Are you sure?",
   "Authentication successful!": "Authentication successful!",
-  "Apply": "Apply",
+  Apply: "Apply",
   "Back to list": "Back to list",
   "Black to move": "Black to move",
   "Black win": "Black win",
   "Board colors": "Board colors",
   "Board size": "Board size",
-  "blue": "blue",
-  "brown": "brown",
-  "Cadence": "Cadence",
-  "Challenge": "Challenge",
+  blue: "blue",
+  brown: "brown",
+  Cadence: "Cadence",
+  Challenge: "Challenge",
   "Challenge declined": "Challenge declined",
   "Chat here": "Chat here",
-  "Connection token sent. Check your emails!": "Connection token sent. Check your emails!",
-  "Contact": "Contact",
+  "Connection token sent. Check your emails!":
+    "Connection token sent. Check your emails!",
+  Contact: "Contact",
   "Correspondance challenges": "Correspondance challenges",
   "Correspondance games": "Correspondance games",
   "Database error:": "Database error:",
-  "Delete": "Delete",
-  "Download": "Download",
-  "Draw": "Draw",
+  Delete: "Delete",
+  Download: "Download",
+  Draw: "Draw",
   "Draw offer only in your turn": "Draw offer only in your turn",
-  "Edit": "Edit",
-  "Email": "Email",
+  Edit: "Edit",
+  Email: "Email",
   "Email sent!": "Email sent!",
   "Empty message": "Empty message",
   "Error while loading database:": "Error while loading database:",
   "Example game": "Example game",
-  "From": "From",
+  From: "From",
   "Game retrieval failed:": "Game retrieval failed:",
   "Game removal failed:": "Game removal failed:",
-  "Go": "Go",
-  "green": "green",
-  "Hall": "Hall",
+  Go: "Go",
+  green: "green",
+  Hall: "Hall",
   "Highlight last move and checks?": "Highlight last move and checks?",
-  "Instructions": "Instructions",
-  "Language": "Language",
+  Instructions: "Instructions",
+  Language: "Language",
   "Live challenges": "Live challenges",
   "Live games": "Live games",
   "Load more": "Load more",
-  "Login": "Login",
-  "Logout": "Logout",
+  Login: "Login",
+  Logout: "Logout",
   "Logout successful!": "Logout successful!",
   "Modifications applied!": "Modifications applied!",
   "Move played:": "Move played:",
@@ -56,56 +57,60 @@ export const translations =
   "My games": "My games",
   "My problems": "My problems",
   "Name or Email": "Name or Email",
-  "New connexion detected: tab now offline": "New connexion detected: tab now offline",
+  "New connexion detected: tab now offline":
+    "New connexion detected: tab now offline",
   "New correspondance game:": "New correspondance game:",
   "New game": "New game",
   "New problem": "New problem",
-  "News": "News",
+  News: "News",
   "No subject. Send anyway?": "No subject. Send anyway?",
-  "None": "None",
+  None: "None",
   "Notifications by email": "Notifications by email",
-  "Number": "Number",
-  "Observe": "Observe",
+  Number: "Number",
+  Observe: "Observe",
   "Offer draw?": "Offer draw?",
   "Opponent action": "Opponent action",
   "Play sounds?": "Play sounds?",
   "Play with?": "Play with?",
-  "Players": "Players",
-  "Please log in to accept corr challenges": "Please log in to accept corr challenges",
-  "Please log in to play correspondance games": "Please log in to play correspondance games",
+  Players: "Players",
+  "Please log in to accept corr challenges":
+    "Please log in to accept corr challenges",
+  "Please log in to play correspondance games":
+    "Please log in to play correspondance games",
   "Please select a variant": "Please select a variant",
-  "Practice": "Practice",
+  Practice: "Practice",
   "Prefix?": "Prefix?",
   "Processing... Please wait": "Processing... Please wait",
-  "Problems": "Problems",
+  Problems: "Problems",
   "participant(s):": "participant(s):",
-  "Register": "Register",
-  "Registration complete! Please check your emails": "Registration complete! Please check your emails",
+  Register: "Register",
+  "Registration complete! Please check your emails":
+    "Registration complete! Please check your emails",
   "Remove game?": "Remove game?",
-  "Resign": "Resign",
+  Resign: "Resign",
   "Resign the game?": "Resign the game?",
-  "Result": "Result",
-  "Rules": "Rules",
-  "Send": "Send",
+  Result: "Result",
+  Rules: "Rules",
+  Send: "Send",
   "Self-challenge is forbidden": "Self-challenge is forbidden",
   "Send challenge": "Send challenge",
-  "Settings": "Settings",
+  Settings: "Settings",
   "Show possible moves?": "Show possible moves?",
   "Show solution": "Show solution",
-  "Social": "Social",
-  "Solution": "Solution",
+  Social: "Social",
+  Solution: "Solution",
   "Stop game": "Stop game",
-  "Subject": "Subject",
+  Subject: "Subject",
   "Terminate game?": "Terminate game?",
   "Three repetitions": "Three repetitions",
-  "Time": "Time",
-  "To": "To",
-  "Unknown": "Unknown",
-  "Update": "Update",
+  Time: "Time",
+  To: "To",
+  Unknown: "Unknown",
+  Update: "Update",
   "User name": "User name",
-  "Variant": "Variant",
-  "Variants": "Variants",
-  "Versus": "Versus",
+  Variant: "Variant",
+  Variants: "Variants",
+  Versus: "Versus",
   "White to move": "White to move",
   "White win": "White win",
   "Who's there?": "Who's there?",
@@ -130,5 +135,5 @@ export const translations =
   "Pawns move diagonally": "Pawns move diagonally",
   "Reverse captures": "Reverse captures",
   "Shared pieces": "Shared pieces",
-  "Standard rules": "Standard rules",
+  "Standard rules": "Standard rules"
 };
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 78f85854..44203b9a 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -1,55 +1,56 @@
-export const translations =
-{
-  "Abort": "Terminar",
-  "About": "Acerca de",
+export const translations = {
+  Abort: "Terminar",
+  About: "Acerca de",
   "Accept draw?": "¿Acceptar tablas?",
-  "All": "Todos",
-  "Analyse": "Analizar",
-  "Analyse in Dark mode makes no sense!": "¡Analizar en modo Dark no tiene sentido!",
-  "Apply": "Aplicar",
+  All: "Todos",
+  Analyse: "Analizar",
+  "Analyse in Dark mode makes no sense!":
+    "¡Analizar en modo Dark no tiene sentido!",
+  Apply: "Aplicar",
   "Are you sure?": "¿Está usted seguro?",
   "Authentication successful!": "¡Autenticación exitosa!",
   "Back to list": "Volver a la lista",
-  "Black": "Negras",
+  Black: "Negras",
   "Black to move": "Juegan las negras",
   "Black win": "Las negras gagnan",
   "Board colors": "Colores del tablero",
   "Board size": "Tamaño del tablero",
-  "blue": "azul",
-  "brown": "marrón",
-  "Cadence": "Cadencia",
-  "Challenge": "Desafiar",
+  blue: "azul",
+  brown: "marrón",
+  Cadence: "Cadencia",
+  Challenge: "Desafiar",
   "Challenge declined": "Desafío rechazado",
   "Chat here": "Chat aquí",
-  "Connection token sent. Check your emails!": "Token de conexión enviado. ¡Revisa tus correos!",
-  "Contact": "Contacto",
+  "Connection token sent. Check your emails!":
+    "Token de conexión enviado. ¡Revisa tus correos!",
+  Contact: "Contacto",
   "Correspondance challenges": "Desafíos por correspondencia",
   "Correspondance games": "Partidas por correspondencia",
   "Database error:": "Error de la base de datos:",
-  "Delete": "Borrar",
-  "Download": "Descargar",
-  "Draw": "Tablas",
+  Delete: "Borrar",
+  Download: "Descargar",
+  Draw: "Tablas",
   "Draw offer only in your turn": "Oferta de tablas solo en tu turno",
-  "Edit": "Editar",
-  "Email": "Email",
+  Edit: "Editar",
+  Email: "Email",
   "Email sent!": "¡Email enviado!",
   "Empty message": "Mensaje vacio",
   "Error while loading database:": "Error al cargar la base de datos:",
   "Example game": "Ejemplo de partida",
-  "From": "De",
+  From: "De",
   "Game retrieval failed:": "La recuperación de la partida falló:",
   "Game removal failed:": "La eliminación de la partida falló:",
-  "Go": "Go",
-  "green": "verde",
-  "Hall": "Salón",
+  Go: "Go",
+  green: "verde",
+  Hall: "Salón",
   "Highlight last move and checks?": "¿Resaltar el último movimiento y jaques?",
-  "Instructions": "Instrucciones",
-  "Language": "Idioma",
+  Instructions: "Instrucciones",
+  Language: "Idioma",
   "Live challenges": "Desafíos en vivo",
   "Live games": "Partidas en vivo",
   "Load more": "Cargar más",
-  "Login": "Login",
-  "Logout": "Logout",
+  Login: "Login",
+  Logout: "Logout",
   "Logout successful!": "¡Desconexión exitosa!",
   "Modifications applied!": "¡Modificaciones aplicadas!",
   "Move played:": "Movimiento jugado:",
@@ -57,57 +58,61 @@ export const translations =
   "My games": "Mis partidas",
   "My problems": "Mis problemas",
   "Name or Email": "Nombre o Email",
-  "New connexion detected: tab now offline": "Nueva conexión detectada: pestaña ahora desconectada",
+  "New connexion detected: tab now offline":
+    "Nueva conexión detectada: pestaña ahora desconectada",
   "New correspondance game:": "Nueva partida por correspondencia:",
   "New game": "Nueva partida",
   "New problem": "Nuevo problema",
-  "News": "Noticias",
+  News: "Noticias",
   "No subject. Send anyway?": "Sin asunto. ¿Enviar sin embargo?",
-  "None": "Ninguno",
+  None: "Ninguno",
   "Notifications by email": "Notificaciones por email",
-  "Number": "Número",
+  Number: "Número",
   "Offer draw?": "¿Ofrecer tablas?",
-  "Observe": "Observar",
+  Observe: "Observar",
   "Opponent action": "Acción del adversario",
   "Play sounds?": "¿Permitir sonidos?",
   "Play with?": "¿Jugar con?",
-  "Players": "Jugadores",
-  "Please log in to accept corr challenges": "Inicia sesión para aceptar los desafíos por correspondencia",
-  "Please log in to play correspondance games": "Inicia sesión para jugar partidas por correspondancia",
+  Players: "Jugadores",
+  "Please log in to accept corr challenges":
+    "Inicia sesión para aceptar los desafíos por correspondencia",
+  "Please log in to play correspondance games":
+    "Inicia sesión para jugar partidas por correspondancia",
   "Please select a variant": "Por favor seleccione una variante",
-  "Practice": "Práctica",
+  Practice: "Práctica",
   "Prefix?": "¿Prefijo?",
   "Processing... Please wait": "Procesando... por favor espere",
-  "Problems": "Problemas",
+  Problems: "Problemas",
   "participant(s):": "participante(s):",
-  "Register": "Registrarse",
-  "Registration complete! Please check your emails": "¡Registro completo! Por favor revise sus correos electrónicos",
+  Register: "Registrarse",
+  "Registration complete! Please check your emails":
+    "¡Registro completo! Por favor revise sus correos electrónicos",
   "Remove game?": "¿Eliminar la partida?",
-  "Resign": "Abandonar",
+  Resign: "Abandonar",
   "Resign the game?": "¿Abandonar la partida?",
-  "Result": "Resultado",
-  "Rules": "Reglas",
-  "Send": "Enviar",
+  Result: "Resultado",
+  Rules: "Reglas",
+  Send: "Enviar",
   "Self-challenge is forbidden": "Auto desafío está prohibido",
   "Send challenge": "Enviar desafío",
-  "Settings": "Configuraciones",
+  Settings: "Configuraciones",
   "Show possible moves?": "¿Mostrar posibles movimientos?",
   "Show solution": "Mostrar la solución",
-  "Social": "Social",
-  "Solution": "Solución",
+  Social: "Social",
+  Solution: "Solución",
   "Stop game": "Terminar la partida",
-  "Subject": "Asunto",
+  Subject: "Asunto",
   "Terminate game?": "¿Terminar la partida?",
   "Three repetitions": "Tres repeticiones",
-  "Time": "Tiempo",
-  "To": "A",
-  "Unknown": "Desconocido",
-  "Update": "Actualización",
+  Time: "Tiempo",
+  To: "A",
+  Unknown: "Desconocido",
+  Update: "Actualización",
   "User name": "Nombre de usuario",
-  "Variant": "Variante",
-  "Variants": "Variantes",
-  "Versus": "Contra",
-  "White": "Blancas",
+  Variant: "Variante",
+  Variants: "Variantes",
+  Versus: "Contra",
+  White: "Blancas",
   "White to move": "Juegan las blancas",
   "White win": "Las blancas gagnan",
   "Who's there?": "¿Quién está ahí?",
@@ -132,5 +137,5 @@ export const translations =
   "Pawns move diagonally": "Peones se mueven en diagonal",
   "Reverse captures": "Capturas invertidas",
   "Shared pieces": "Piezas compartidas",
-  "Standard rules": "Reglas estandar",
+  "Standard rules": "Reglas estandar"
 };
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index e5322eb7..d0877147 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -1,55 +1,59 @@
-export const translations =
-{
-  "Abort": "Arrêter",
-  "About": "À propos",
+export const translations = {
+  Abort: "Arrêter",
+  About: "À propos",
   "Accept draw?": "Accepter la nulle ?",
-  "All": "Tous",
-  "Analyse": "Analyser",
-  "Analyse in Dark mode makes no sense!": "Analyser en mode Dark n'a pas de sens !",
-  "Apply": "Appliquer",
+  All: "Tous",
+  Analyse: "Analyser",
+  "Analyse in Dark mode makes no sense!":
+    "Analyser en mode Dark n'a pas de sens !",
+  Apply: "Appliquer",
   "Authentication successful!": "Authentification réussie !",
   "Are you sure?": "Étes vous sûr?",
   "Back to list": "Retour à la liste",
-  "Black": "Noirs",
+  Black: "Noirs",
   "Black to move": "Trait aux noirs",
   "Black win": "Les noirs gagnent",
   "Board colors": "Couleurs de l'échiquier",
   "Board size": "Taille de l'échiquier",
-  "blue": "bleu",
-  "brown": "marron",
-  "Cadence": "Cadence",
-  "Challenge": "Défier",
+  blue: "bleu",
+  brown: "marron",
+  Cadence: "Cadence",
+  Challenge: "Défier",
   "Challenge declined": "Défi refusé",
   "Chat here": "Chattez ici",
-  "Connection token sent. Check your emails!": "Token de connection envoyé. Allez voir vos emails !",
-  "Contact": "Contact",
+  "Connection token sent. Check your emails!":
+    "Token de connection envoyé. Allez voir vos emails !",
+  Contact: "Contact",
   "Correspondance challenges": "Défis par correspondance",
   "Correspondance games": "Parties par correspondance",
   "Database error:": "Erreur de base de données :",
-  "Delete": "Supprimer",
-  "Download": "Télécharger",
-  "Draw": "Nulle",
-  "Draw offer only in your turn": "Proposition de nulle seulement sur votre temps",
-  "Edit": "Éditer",
-  "Email": "Email",
+  Delete: "Supprimer",
+  Download: "Télécharger",
+  Draw: "Nulle",
+  "Draw offer only in your turn":
+    "Proposition de nulle seulement sur votre temps",
+  Edit: "Éditer",
+  Email: "Email",
   "Email sent!": "Email envoyé !",
   "Empty message": "Message vide",
-  "Error while loading database:": "Erreur lors du chargement de la base de données :",
+  "Error while loading database:":
+    "Erreur lors du chargement de la base de données :",
   "Example game": "Partie exemple",
-  "From": "De",
+  From: "De",
   "Game retrieval failed:": "Échec de la récupération de la partie :",
   "Game removal failed:": "Échec de la suppresion de la partie :",
-  "Go": "Go",
-  "green": "vert",
-  "Hall": "Salon",
-  "Highlight last move and checks?": "Mettre en valeur le dernier coup et les échecs ?",
-  "Instructions": "Instructions",
-  "Language": "Langue",
+  Go: "Go",
+  green: "vert",
+  Hall: "Salon",
+  "Highlight last move and checks?":
+    "Mettre en valeur le dernier coup et les échecs ?",
+  Instructions: "Instructions",
+  Language: "Langue",
   "Live challenges": "Défis en direct",
   "Live games": "Parties en direct",
   "Load more": "Charger plus",
-  "Login": "Login",
-  "Logout": "Logout",
+  Login: "Login",
+  Logout: "Logout",
   "Logout successful!": "Déconnection réussie !",
   "Modifications applied!": "Modifications effectuées !",
   "Move played:": "Coup joué :",
@@ -57,57 +61,61 @@ export const translations =
   "My games": "Mes parties",
   "My problems": "Mes problèmes",
   "Name or Email": "Nom ou Email",
-  "New connexion detected: tab now offline": "Nouvelle connexion détectée : onglet désormais hors ligne",
+  "New connexion detected: tab now offline":
+    "Nouvelle connexion détectée : onglet désormais hors ligne",
   "New correspondance game:": "Nouvelle partie par corespondance :",
   "New game": "Nouvelle partie",
   "New problem": "Nouveau problème",
-  "News": "Nouvelles",
+  News: "Nouvelles",
   "No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
-  "None": "Aucun",
+  None: "Aucun",
   "Notifications by email": "Notifications par email",
-  "Number": "Numéro",
+  Number: "Numéro",
   "Offer draw?": "Proposer nulle ?",
-  "Observe": "Observer",
+  Observe: "Observer",
   "Opponent action": "Action de l'adversaire",
   "Play sounds?": "Jouer les sons ?",
   "Play with?": "Jouer avec ?",
-  "Players": "Joueurs",
-  "Please log in to accept corr challenges": "Identifiez vous pour accepter des défis par correspondance",
-  "Please log in to play correspondance games": "Identifiez vous pour jouer des parties par correspondance",
+  Players: "Joueurs",
+  "Please log in to accept corr challenges":
+    "Identifiez vous pour accepter des défis par correspondance",
+  "Please log in to play correspondance games":
+    "Identifiez vous pour jouer des parties par correspondance",
   "Please select a variant": "Sélectionnez une variante SVP",
-  "Practice": "Pratiquer",
+  Practice: "Pratiquer",
   "Prefix?": "Préfixe ?",
   "Processing... Please wait": "Traitement en cours... Attendez SVP",
-  "Problems": "Problèmes",
+  Problems: "Problèmes",
   "participant(s):": "participant(s) :",
-  "Register": "S'enregistrer",
-  "Registration complete! Please check your emails": "Enregistrement terminé ! Allez voir vos emails",
+  Register: "S'enregistrer",
+  "Registration complete! Please check your emails":
+    "Enregistrement terminé ! Allez voir vos emails",
   "Remove game?": "Supprimer la partie ?",
-  "Resign": "Abandonner",
+  Resign: "Abandonner",
   "Resign the game?": "Abandonner la partie ?",
-  "Result": "Résultat",
-  "Rules": "Règles",
-  "Send": "Envoyer",
+  Result: "Résultat",
+  Rules: "Règles",
+  Send: "Envoyer",
   "Self-challenge is forbidden": "Interdit de s'auto-défier",
   "Send challenge": "Envoyer défi",
-  "Settings": "Réglages",
+  Settings: "Réglages",
   "Show possible moves?": "Montrer les coups possibles ?",
   "Show solution": "Montrer la solution",
-  "Social": "Social",
-  "Solution": "Solution",
+  Social: "Social",
+  Solution: "Solution",
   "Stop game": "Arrêter la partie",
-  "Subject": "Sujet",
+  Subject: "Sujet",
   "Terminate game?": "Stopper la partie ?",
   "Three repetitions": "Triple répétition",
-  "Time": "Temps",
-  "To": "À",
-  "Unknown": "Inconnu",
-  "Update": "Mise à jour",
+  Time: "Temps",
+  To: "À",
+  Unknown: "Inconnu",
+  Update: "Mise à jour",
   "User name": "Nom d'utilisateur",
-  "Variant": "Variante",
-  "Variants": "Variantes",
-  "Versus": "Contre",
-  "White": "Blancs",
+  Variant: "Variante",
+  Variants: "Variantes",
+  Versus: "Contre",
+  White: "Blancs",
   "White to move": "Trait aux blancs",
   "White win": "Les blancs gagnent",
   "Who's there?": "Qui est là ?",
@@ -132,5 +140,5 @@ export const translations =
   "Pawns move diagonally": "Les pions vont en diagonale",
   "Reverse captures": "Captures inversées",
   "Shared pieces": "Pièces partagées",
-  "Standard rules": "Règles usuelles",
+  "Standard rules": "Règles usuelles"
 };
diff --git a/client/src/utils/ajax.js b/client/src/utils/ajax.js
index 86925b03..e539f468 100644
--- a/client/src/utils/ajax.js
+++ b/client/src/utils/ajax.js
@@ -5,8 +5,7 @@ import params from "../parameters"; //for server URL
 // Problem: fetch() does not set req.xhr... see access/ajax() security especially for /whoami
 
 // From JSON (encoded string values!) to "arg1=...&arg2=..."
-function toQueryString(data)
-{
+function toQueryString(data) {
   let data_str = "";
   Object.keys(data).forEach(k => {
     data_str += k + "=" + encodeURIComponent(data[k]) + "&";
@@ -15,56 +14,48 @@ function toQueryString(data)
 }
 
 // data, error: optional
-export function ajax(url, method, data, success, error)
-{
+export function ajax(url, method, data, success, error) {
   let xhr = new XMLHttpRequest();
-  if (data === undefined || typeof(data) === "function") //no data
-  {
+  if (data === undefined || typeof data === "function") {
+    //no data
     error = success;
     success = data;
     data = {};
   }
-  if (!success)
-    success = () => {}; //by default, do nothing
+  if (!success) success = () => {}; //by default, do nothing
   if (!error)
-    error = errmsg => { alert(errmsg); };
+    error = errmsg => {
+      alert(errmsg);
+    };
   xhr.onreadystatechange = function() {
-    if (this.readyState == 4 && this.status == 200)
-    {
+    if (this.readyState == 4 && this.status == 200) {
       let res_json = "";
       try {
         res_json = JSON.parse(xhr.responseText);
       } catch (e) {
-        // Plain text (e.g. for rules retrieval)
-        return success(xhr.responseText);
+        // Plain text (e.g. for rules retrieval) (TODO: no more plain text in current version)
+        success(xhr.responseText);
       }
-      if (!res_json.errmsg && !res_json.errno)
-        success(res_json);
-      else
-      {
-        if (!!res_json.errmsg)
-          error(res_json.errmsg);
-        else
-          error(res_json.code + ". errno = " + res_json.errno);
+      if (res_json) {
+        if (!res_json.errmsg && !res_json.errno) success(res_json);
+        else {
+          if (res_json.errmsg) error(res_json.errmsg);
+          else error(res_json.code + ". errno = " + res_json.errno);
+        }
       }
     }
   };
 
-  if (["GET","DELETE"].includes(method) && !!data)
-  {
+  if (["GET", "DELETE"].includes(method) && !!data) {
     // Append query params to URL
     url += "/?" + toQueryString(data);
   }
   xhr.open(method, params.serverUrl + url, true);
-  xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest");
+  xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
   // Next line to allow cross-domain cookies in dev mode
-  if (params.cors)
-    xhr.withCredentials = true;
-  if (["POST","PUT"].includes(method))
-  {
+  if (params.cors) xhr.withCredentials = true;
+  if (["POST", "PUT"].includes(method)) {
     xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
     xhr.send(JSON.stringify(data));
-  }
-  else
-    xhr.send();
+  } else xhr.send();
 }
diff --git a/client/src/utils/alea.js b/client/src/utils/alea.js
index 04563938..9ff11593 100644
--- a/client/src/utils/alea.js
+++ b/client/src/utils/alea.js
@@ -1,27 +1,26 @@
 // Random (enough) string for socket and game IDs
-export function getRandString()
-{
-  return (Date.now().toString(36) + Math.random().toString(36).substr(2, 7))
-    .toUpperCase();
+export function getRandString() {
+  return (
+    Date.now().toString(36) +
+    Math.random()
+      .toString(36)
+      .substr(2, 7)
+  ).toUpperCase();
 }
 
-export function randInt(min, max)
-{
-  if (!max)
-  {
+export function randInt(min, max) {
+  if (!max) {
     max = min;
     min = 0;
   }
-  return Math.floor(Math.random() * (max - min) ) + min;
+  return Math.floor(Math.random() * (max - min)) + min;
 }
 
 // Inspired by https://github.com/jashkenas/underscore/blob/master/underscore.js
-export function sample (arr, n)
-{
+export function sample(arr, n) {
   n = n || 1;
   let cpArr = arr.map(e => e);
-  for (let index = 0; index < n; index++)
-  {
+  for (let index = 0; index < n; index++) {
     const rand = randInt(index, arr.length);
     const temp = cpArr[index];
     cpArr[index] = cpArr[rand];
@@ -30,7 +29,6 @@ export function sample (arr, n)
   return cpArr.slice(0, n);
 }
 
-export function shuffle(arr)
-{
+export function shuffle(arr) {
   return sample(arr, arr.length);
 }
diff --git a/client/src/utils/array.js b/client/src/utils/array.js
index fae4ce91..4d44db02 100644
--- a/client/src/utils/array.js
+++ b/client/src/utils/array.js
@@ -1,32 +1,24 @@
 // Remove item(s) in array (if present)
-export const ArrayFun =
-{
-  remove: function(arr, rfun, all)
-  {
+export const ArrayFun = {
+  remove: function(arr, rfun, all) {
     const index = arr.findIndex(rfun);
-    if (index >= 0)
-    {
+    if (index >= 0) {
       arr.splice(index, 1);
-      if (!!all)
-      {
+      if (all) {
         // Reverse loop because of the splice below
-        for (let i=arr.length-1; i>=index; i--)
-        {
-          if (rfun(arr[i]))
-            arr.splice(i, 1);
+        for (let i = arr.length - 1; i >= index; i--) {
+          if (rfun(arr[i])) arr.splice(i, 1);
         }
       }
     }
   },
 
   // Double array intialization
-  init: function(size1, size2, initElem)
-  {
-    return [...Array(size1)].map(e => Array(size2).fill(initElem));
+  init: function(size1, size2, initElem) {
+    return [...Array(size1)].map(() => Array(size2).fill(initElem));
   },
 
-  range: function(max)
-  {
+  range: function(max) {
     return [...Array(max).keys()];
-  },
+  }
 };
diff --git a/client/src/utils/cookie.js b/client/src/utils/cookie.js
index b0518442..78551a29 100644
--- a/client/src/utils/cookie.js
+++ b/client/src/utils/cookie.js
@@ -1,22 +1,18 @@
 // Source: https://www.quirksmode.org/js/cookies.html
-export function setCookie(name, value)
-{
+export function setCookie(name, value) {
   var date = new Date();
-  date.setTime(date.getTime()+(183*24*60*60*1000)); //6 months
-  var expires = "; expires="+date.toGMTString();
-  document.cookie = name+"="+value+expires+"; path=/";
+  date.setTime(date.getTime() + 183 * 24 * 60 * 60 * 1000); //6 months
+  var expires = "; expires=" + date.toGMTString();
+  document.cookie = name + "=" + value + expires + "; path=/";
 }
 
 export function getCookie(name, defaut) {
   var nameEQ = name + "=";
-  var ca = document.cookie.split(';');
-  for (var i=0;i < ca.length;i++)
-  {
+  var ca = document.cookie.split(";");
+  for (var i = 0; i < ca.length; i++) {
     var c = ca[i];
-    while (c.charAt(0)==' ')
-      c = c.substring(1,c.length);
-    if (c.indexOf(nameEQ) == 0)
-      return c.substring(nameEQ.length,c.length);
+    while (c.charAt(0) == " ") c = c.substring(1, c.length);
+    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
   }
   return defaut; //cookie not found
 }
diff --git a/client/src/utils/datetime.js b/client/src/utils/datetime.js
index 5addd255..75e85d71 100644
--- a/client/src/utils/datetime.js
+++ b/client/src/utils/datetime.js
@@ -1,47 +1,50 @@
-function zeroPad(x)
-{
-  return (x<10 ? "0" : "") + x;
+function zeroPad(x) {
+  return (x < 10 ? "0" : "") + x;
 }
 
-export function getDate(d)
-{
-  return d.getFullYear() + '-' + zeroPad(d.getMonth()+1) + '-' + zeroPad(d.getDate());
+export function getDate(d) {
+  return (
+    d.getFullYear() +
+    "-" +
+    zeroPad(d.getMonth() + 1) +
+    "-" +
+    zeroPad(d.getDate())
+  );
 }
 
-export function getTime(d)
-{
-  return zeroPad(d.getHours()) + ":" + zeroPad(d.getMinutes()) + ":" +
-    zeroPad(d.getSeconds());
+export function getTime(d) {
+  return (
+    zeroPad(d.getHours()) +
+    ":" +
+    zeroPad(d.getMinutes()) +
+    ":" +
+    zeroPad(d.getSeconds())
+  );
 }
 
-function padDigits(x)
-{
-  if (x < 10)
-    return "0" + x;
+function padDigits(x) {
+  if (x < 10) return "0" + x;
   return x;
 }
 
-export function ppt(t)
-{
+export function ppt(t) {
   // "Pretty print" an amount of time given in seconds
   const dayInSeconds = 60 * 60 * 24;
   const hourInSeconds = 60 * 60;
   const days = Math.floor(t / dayInSeconds);
-  const hours = Math.floor(t%dayInSeconds / hourInSeconds);
-  const minutes = Math.floor(t%hourInSeconds / 60);
+  const hours = Math.floor((t % dayInSeconds) / hourInSeconds);
+  const minutes = Math.floor((t % hourInSeconds) / 60);
   const seconds = Math.floor(t % 60);
   let res = "";
-  if (days > 0)
-    res += days + "d ";
-  if (days <= 3 && hours > 0) //NOTE: 3 is arbitrary
+  if (days > 0) res += days + "d ";
+  if (days <= 3 && hours > 0)
+    //NOTE: 3 is arbitrary
     res += hours + "h ";
   if (days == 0 && minutes > 0)
-    res += (hours > 0 ? padDigits(minutes) + "m " : minutes + ":");
-  if (days == 0 && hours == 0)
-  {
+    res += hours > 0 ? padDigits(minutes) + "m " : minutes + ":";
+  if (days == 0 && hours == 0) {
     res += padDigits(seconds);
-    if (minutes == 0)
-      res += "s"; //seconds indicator, since this is the only number printed
+    if (minutes == 0) res += "s"; //seconds indicator, since this is the only number printed
   }
   return res.trim(); //remove potential last space
 }
diff --git a/client/src/utils/gameStorage.js b/client/src/utils/gameStorage.js
index f995828d..453388ed 100644
--- a/client/src/utils/gameStorage.js
+++ b/client/src/utils/gameStorage.js
@@ -19,8 +19,7 @@
 import { ajax } from "@/utils/ajax";
 import { store } from "@/store";
 
-function dbOperation(callback)
-{
+function dbOperation(callback) {
   let db = null;
   let DBOpenRequest = window.indexedDB.open("vchess", 4);
 
@@ -28,7 +27,7 @@ function dbOperation(callback)
     alert(store.state.tr["Database error:"] + " " + event.target.errorCode);
   };
 
-  DBOpenRequest.onsuccess = function(event) {
+  DBOpenRequest.onsuccess = function() {
     db = DBOpenRequest.result;
     callback(db);
     db.close();
@@ -37,28 +36,32 @@ function dbOperation(callback)
   DBOpenRequest.onupgradeneeded = function(event) {
     let db = event.target.result;
     db.onerror = function(event) {
-      alert(store.state.tr["Error while loading database:"] + " " + event.target.errorCode);
+      alert(
+        store.state.tr["Error while loading database:"] +
+          " " +
+          event.target.errorCode
+      );
     };
     // Create objectStore for vchess->games
     let objectStore = db.createObjectStore("games", { keyPath: "id" });
     objectStore.createIndex("score", "score"); //to search by game result
-  }
+  };
 }
 
-export const GameStorage =
-{
+export const GameStorage = {
   // Optional callback to get error status
-  add: function(game, callback)
-  {
-    dbOperation((db) => {
+  add: function(game, callback) {
+    dbOperation(db => {
       let transaction = db.transaction("games", "readwrite");
-      if (callback)
-      {
+      if (callback) {
         transaction.oncomplete = function() {
           callback({}); //everything's fine
-        }
+        };
         transaction.onerror = function() {
-          callback({errmsg: store.state.tr["Game retrieval failed:"] + " " + transaction.error});
+          callback({
+            errmsg:
+              store.state.tr["Game retrieval failed:"] + " " + transaction.error
+          });
         };
       }
       let objectStore = transaction.objectStore("games");
@@ -68,98 +71,81 @@ export const GameStorage =
 
   // TODO: also option to takeback a move ?
   // obj: chat, move, fen, clocks, score[Msg], initime, ...
-  update: function(gameId, obj)
-  {
-    if (Number.isInteger(gameId) || !isNaN(parseInt(gameId)))
-    {
+  update: function(gameId, obj) {
+    if (Number.isInteger(gameId) || !isNaN(parseInt(gameId))) {
       // corr: only move, fen and score
-      ajax(
-        "/games",
-        "PUT",
-        {
-          gid: gameId,
-          newObj:
-          {
-            // Some fields may be undefined:
-            chat: obj.chat,
-            move: obj.move,
-            fen: obj.fen,
-            score: obj.score,
-            scoreMsg: obj.scoreMsg,
-            drawOffer: obj.drawOffer,
-          }
+      ajax("/games", "PUT", {
+        gid: gameId,
+        newObj: {
+          // Some fields may be undefined:
+          chat: obj.chat,
+          move: obj.move,
+          fen: obj.fen,
+          score: obj.score,
+          scoreMsg: obj.scoreMsg,
+          drawOffer: obj.drawOffer
         }
-      );
-    }
-    else
-    {
+      });
+    } else {
       // live
-      dbOperation((db) => {
-        let objectStore = db.transaction("games", "readwrite").objectStore("games");
+      dbOperation(db => {
+        let objectStore = db
+          .transaction("games", "readwrite")
+          .objectStore("games");
         objectStore.get(gameId).onsuccess = function(event) {
           const game = event.target.result;
           Object.keys(obj).forEach(k => {
-            if (k == "move")
-              game.moves.push(obj[k]);
-            else
-              game[k] = obj[k];
+            if (k == "move") game.moves.push(obj[k]);
+            else game[k] = obj[k];
           });
           objectStore.put(game); //save updated data
-        }
+        };
       });
     }
   },
 
   // Retrieve all local games (running, completed, imported...)
-  getAll: function(callback)
-  {
-    dbOperation((db) => {
-      let objectStore = db.transaction('games').objectStore('games');
+  getAll: function(callback) {
+    dbOperation(db => {
+      let objectStore = db.transaction("games").objectStore("games");
       let games = [];
       objectStore.openCursor().onsuccess = function(event) {
         let cursor = event.target.result;
         // if there is still another cursor to go, keep running this code
-        if (cursor)
-        {
+        if (cursor) {
           games.push(cursor.value);
           cursor.continue();
-        }
-        else
-          callback(games);
-      }
+        } else callback(games);
+      };
     });
   },
 
   // Retrieve any game from its identifiers (locally or on server)
   // NOTE: need callback because result is obtained asynchronously
-  get: function(gameId, callback)
-  {
+  get: function(gameId, callback) {
     // corr games identifiers are integers
-    if (Number.isInteger(gameId) || !isNaN(parseInt(gameId)))
-    {
-      ajax("/games", "GET", {gid:gameId}, res => {
+    if (Number.isInteger(gameId) || !isNaN(parseInt(gameId))) {
+      ajax("/games", "GET", { gid: gameId }, res => {
         let game = res.game;
         game.moves.forEach(m => {
           m.squares = JSON.parse(m.squares);
         });
         callback(game);
       });
-    }
-    else //local game
-    {
-      dbOperation((db) => {
-        let objectStore = db.transaction('games').objectStore('games');
+    } //local game
+    else {
+      dbOperation(db => {
+        let objectStore = db.transaction("games").objectStore("games");
         objectStore.get(gameId).onsuccess = function(event) {
           callback(event.target.result);
-        }
+        };
       });
     }
   },
 
-  getCurrent: function(callback)
-  {
-    dbOperation((db) => {
-      let objectStore = db.transaction('games').objectStore('games');
+  getCurrent: function(callback) {
+    dbOperation(db => {
+      let objectStore = db.transaction("games").objectStore("games");
       objectStore.get("*").onsuccess = function(event) {
         callback(event.target.result);
       };
@@ -167,20 +153,21 @@ export const GameStorage =
   },
 
   // Delete a game in indexedDB
-  remove: function(gameId, callback)
-  {
-    dbOperation((db) => {
+  remove: function(gameId, callback) {
+    dbOperation(db => {
       let transaction = db.transaction(["games"], "readwrite");
-      if (callback)
-      {
+      if (callback) {
         transaction.oncomplete = function() {
           callback({}); //everything's fine
-        }
+        };
         transaction.onerror = function() {
-          callback({errmsg: store.state.tr["Game removal failed:"] + " " + transaction.error});
+          callback({
+            errmsg:
+              store.state.tr["Game removal failed:"] + " " + transaction.error
+          });
         };
       }
       transaction.objectStore("games").delete(gameId);
     });
-  },
+  }
 };
diff --git a/client/src/utils/modalClick.js b/client/src/utils/modalClick.js
index 8e0dca7f..96935787 100644
--- a/client/src/utils/modalClick.js
+++ b/client/src/utils/modalClick.js
@@ -1,7 +1,5 @@
-export function processModalClick(e)
-{
+export function processModalClick(e) {
   // Close a modal when click on it but outside focused element
   const data = e.target.dataset;
-  if (!!data.checkbox)
-    document.getElementById(data.checkbox).checked = false;
+  if (data.checkbox) document.getElementById(data.checkbox).checked = false;
 }
diff --git a/client/src/utils/printDiagram.js b/client/src/utils/printDiagram.js
index bf078cee..aebdc247 100644
--- a/client/src/utils/printDiagram.js
+++ b/client/src/utils/printDiagram.js
@@ -1,14 +1,11 @@
 import { ArrayFun } from "@/utils/array";
 
 // Turn (human) marks into coordinates
-function getMarkArray(marks)
-{
-  if (!marks || marks == "-")
-    return [];
+function getMarkArray(marks) {
+  if (!marks || marks == "-") return [];
   let markArray = ArrayFun.init(V.size.x, V.size.y, false);
   const squares = marks.split(",");
-  for (let i=0; i<squares.length; i++)
-  {
+  for (let i = 0; i < squares.length; i++) {
     const coords = V.SquareToCoords(squares[i]);
     markArray[coords.x][coords.y] = true;
   }
@@ -16,41 +13,31 @@ function getMarkArray(marks)
 }
 
 // Turn (human) shadow indications into coordinates
-function getShadowArray(shadow)
-{
-  if (!shadow || shadow == "-")
-    return [];
+function getShadowArray(shadow) {
+  if (!shadow || shadow == "-") return [];
   let shadowArray = ArrayFun.init(V.size.x, V.size.y, false);
   const squares = shadow.split(",");
-  for (let i=0; i<squares.length; i++)
-  {
+  for (let i = 0; i < squares.length; i++) {
     const rownum = V.size.x - parseInt(squares[i]);
-    if (!isNaN(rownum))
-    {
+    if (!isNaN(rownum)) {
       // Shadow a full row
-      for (let i=0; i<V.size.y; i++)
-        shadowArray[rownum][i] = true;
+      for (let i = 0; i < V.size.y; i++) shadowArray[rownum][i] = true;
       continue;
     }
-    if (squares[i].length == 1)
-    {
+    if (squares[i].length == 1) {
       // Shadow a full column
       const colnum = V.ColumnToCoord(squares[i]);
-      for (let i=0; i<V.size.x; i++)
-        shadowArray[i][colnum] = true;
+      for (let i = 0; i < V.size.x; i++) shadowArray[i][colnum] = true;
       continue;
     }
-    if (squares[i].indexOf("-") >= 0)
-    {
+    if (squares[i].indexOf("-") >= 0) {
       // Shadow a range of squares, horizontally or vertically
       const firstLastSq = squares[i].split("-");
-      const range =
-      [
+      const range = [
         V.SquareToCoords(firstLastSq[0]),
         V.SquareToCoords(firstLastSq[1])
       ];
-      const step =
-      [
+      const step = [
         range[1].x == range[0].x
           ? 0
           : (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
@@ -59,9 +46,11 @@ function getShadowArray(shadow)
           : (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
       ];
       // Convention: range always from smaller to larger number
-      for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
-        x += step[0], y += step[1])
-      {
+      for (
+        let x = range[0].x, y = range[0].y;
+        x <= range[1].x && y <= range[1].y;
+        x += step[0], y += step[1]
+      ) {
         shadowArray[x][y] = true;
       }
       continue;
@@ -75,30 +64,31 @@ function getShadowArray(shadow)
 
 // args: object with position (mandatory), and
 // orientation, marks, shadow (optional)
-export function getDiagram(args)
-{
+export function getDiagram(args) {
   // Obtain the array of pieces images names:
   const board = V.GetBoard(args.position);
   const orientation = args.orientation || "w";
   const markArray = getMarkArray(args.marks);
   const shadowArray = getShadowArray(args.shadow);
   let boardDiv = "";
-  const [startX,startY,inc] = orientation == 'w'
-    ? [0, 0, 1]
-    : [V.size.x-1, V.size.y-1, -1];
-  for (let i=startX; i>=0 && i<V.size.x; i+=inc)
-  {
+  const [startX, startY, inc] =
+    orientation == "w" ? [0, 0, 1] : [V.size.x - 1, V.size.y - 1, -1];
+  for (let i = startX; i >= 0 && i < V.size.x; i += inc) {
     boardDiv += "<div class='row'>";
-    for (let j=startY; j>=0 && j<V.size.y; j+=inc)
-    {
-      boardDiv += "<div class='board board" + V.size.y + " " +
-        ((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") +
+    for (let j = startY; j >= 0 && j < V.size.y; j += inc) {
+      boardDiv +=
+        "<div class='board board" +
+        V.size.y +
+        " " +
+        ((i + j) % 2 == 0 ? "light-square-diag" : "dark-square-diag") +
         (shadowArray.length > 0 && shadowArray[i][j] ? " in-shadow" : "") +
         "'>";
-      if (board[i][j] != V.EMPTY)
-      {
-        boardDiv += "<img " +
-          "src='/images/pieces/" + V.getPpath(board[i][j]) + ".svg' " +
+      if (board[i][j] != V.EMPTY) {
+        boardDiv +=
+          "<img " +
+          "src='/images/pieces/" +
+          V.getPpath(board[i][j]) +
+          ".svg' " +
           "class='piece'/>";
       }
       if (markArray.length > 0 && markArray[i][j])
diff --git a/client/src/utils/scoring.js b/client/src/utils/scoring.js
index 841de4bf..9c6d25fa 100644
--- a/client/src/utils/scoring.js
+++ b/client/src/utils/scoring.js
@@ -1,8 +1,7 @@
 // Default score message if none provided
 export function getScoreMessage(score) {
   let eogMessage = "Undefined"; //not translated: unused
-  switch (score)
-  {
+  switch (score) {
     case "1-0":
       eogMessage = "White win";
       break;
diff --git a/client/src/utils/squareId.js b/client/src/utils/squareId.js
index 6469bc3f..58ca0862 100644
--- a/client/src/utils/squareId.js
+++ b/client/src/utils/squareId.js
@@ -1,13 +1,11 @@
 // Get the identifier of a HTML square from its numeric coordinates o.x,o.y.
-export function getSquareId(o)
-{
+export function getSquareId(o) {
   // NOTE: a separator is required to allow any size of board
-  return  "sq-" + o.x + "-" + o.y;
+  return "sq-" + o.x + "-" + o.y;
 }
 
 // Inverse function
-export function getSquareFromId(id)
-{
-  const idParts = id.split('-');
+export function getSquareFromId(id) {
+  const idParts = id.split("-");
   return [parseInt(idParts[1]), parseInt(idParts[2])];
 }
diff --git a/client/src/utils/timeControl.js b/client/src/utils/timeControl.js
index 72d5557f..9106b96c 100644
--- a/client/src/utils/timeControl.js
+++ b/client/src/utils/timeControl.js
@@ -1,49 +1,46 @@
-function timeUnitToSeconds(value, unit)
-{
+function timeUnitToSeconds(value, unit) {
   let seconds = value;
-  switch (unit)
-  {
-    case 'd':
-      seconds *= 24;
-    case 'h':
-      seconds *= 60;
-    case 'm':
+  switch (unit) {
+    case "d":
+      seconds *= 86400; //24*60*60
+      break;
+    case "h":
+      seconds *= 3600;
+      break;
+    case "m":
       seconds *= 60;
+      break;
   }
   return seconds;
 }
 
-function isLargerUnit(unit1, unit2)
-{
-  return (unit1 == 'd' && unit2 != 'd')
-    || (unit1 == 'h' && ['s','m'].includes(unit2))
-    || (unit1 == 'm' && unit2 == 's');
+function isLargerUnit(unit1, unit2) {
+  return (
+    (unit1 == "d" && unit2 != "d") ||
+    (unit1 == "h" && ["s", "m"].includes(unit2)) ||
+    (unit1 == "m" && unit2 == "s")
+  );
 }
 
-export function extractTime(cadence)
-{
-  let tcParts = cadence.replace(/ /g,"").split('+');
+export function extractTime(cadence) {
+  let tcParts = cadence.replace(/ /g, "").split("+");
   // Concatenate usual time control suffixes, in case of none is provided
   tcParts[0] += "m";
   tcParts[1] += "s";
   const mainTimeArray = tcParts[0].match(/^([0-9]+)([smhd]+)$/);
-  if (!mainTimeArray)
-    return null;
+  if (!mainTimeArray) return null;
   const mainTimeValue = parseInt(mainTimeArray[1]);
   const mainTimeUnit = mainTimeArray[2][0];
   const mainTime = timeUnitToSeconds(mainTimeValue, mainTimeUnit);
   let increment = 0;
-  if (tcParts.length >= 2)
-  {
+  if (tcParts.length >= 2) {
     const incrementArray = tcParts[1].match(/^([0-9]+)([smhd]+)$/);
-    if (!incrementArray)
-      return null;
+    if (!incrementArray) return null;
     const incrementValue = parseInt(incrementArray[1]);
     const incrementUnit = incrementArray[2][0];
     // Increment unit cannot be larger than main unit:
-    if (isLargerUnit(incrementUnit, mainTimeUnit))
-      return null;
+    if (isLargerUnit(incrementUnit, mainTimeUnit)) return null;
     increment = timeUnitToSeconds(incrementValue, incrementUnit);
   }
-  return {mainTime:mainTime, increment:increment};
+  return { mainTime: mainTime, increment: increment };
 }
diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js
index d7e775cf..e7daf7f8 100644
--- a/client/src/variants/Alice.js
+++ b/client/src/variants/Alice.js
@@ -1,67 +1,57 @@
 import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
 
 // NOTE: alternative implementation, probably cleaner = use only 1 board
 // TODO? atLeastOneMove() would be more efficient if rewritten here (less sideBoard computations)
-export const VariantRules = class AliceRules extends ChessRules
-{
-  static get ALICE_PIECES()
-  {
+export const VariantRules = class AliceRules extends ChessRules {
+  static get ALICE_PIECES() {
     return {
-      's': 'p',
-      't': 'q',
-      'u': 'r',
-      'c': 'b',
-      'o': 'n',
-      'l': 'k',
+      s: "p",
+      t: "q",
+      u: "r",
+      c: "b",
+      o: "n",
+      l: "k"
     };
   }
-  static get ALICE_CODES()
-  {
+  static get ALICE_CODES() {
     return {
-      'p': 's',
-      'q': 't',
-      'r': 'u',
-      'b': 'c',
-      'n': 'o',
-      'k': 'l',
+      p: "s",
+      q: "t",
+      r: "u",
+      b: "c",
+      n: "o",
+      k: "l"
     };
   }
 
-  static getPpath(b)
-  {
+  static getPpath(b) {
     return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
   }
 
-  static get PIECES()
-  {
+  static get PIECES() {
     return ChessRules.PIECES.concat(Object.keys(V.ALICE_PIECES));
   }
 
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const rows = V.ParseFen(fen).position.split("/");
-    if (this.kingPos["w"][0] < 0 || this.kingPos["b"][0] < 0)
-    {
+    if (this.kingPos["w"][0] < 0 || this.kingPos["b"][0] < 0) {
       // INIT_COL_XXX won't be required if Alice kings are found (means 'king moved')
-      for (let i=0; i<rows.length; i++)
-      {
+      for (let i = 0; i < rows.length; i++) {
         let k = 0; //column index on board
-        for (let j=0; j<rows[i].length; j++)
-        {
-          switch (rows[i].charAt(j))
-          {
-            case 'l':
-              this.kingPos['b'] = [i,k];
+        for (let j = 0; j < rows[i].length; j++) {
+          switch (rows[i].charAt(j)) {
+            case "l":
+              this.kingPos["b"] = [i, k];
               break;
-            case 'L':
-              this.kingPos['w'] = [i,k];
+            case "L":
+              this.kingPos["w"] = [i, k];
               break;
-            default:
+            default: {
               const num = parseInt(rows[i].charAt(j));
-              if (!isNaN(num))
-                k += (num-1);
+              if (!isNaN(num)) k += num - 1;
+            }
           }
           k++;
         }
@@ -70,104 +60,98 @@ export const VariantRules = class AliceRules extends ChessRules
   }
 
   // Return the (standard) color+piece notation at a square for a board
-  getSquareOccupation(i, j, mirrorSide)
-  {
-    const piece = this.getPiece(i,j);
-    if (mirrorSide==1 && Object.keys(V.ALICE_CODES).includes(piece))
+  getSquareOccupation(i, j, mirrorSide) {
+    const piece = this.getPiece(i, j);
+    if (mirrorSide == 1 && Object.keys(V.ALICE_CODES).includes(piece))
       return this.board[i][j];
-    else if (mirrorSide==2 && Object.keys(V.ALICE_PIECES).includes(piece))
-      return this.getColor(i,j) + V.ALICE_PIECES[piece];
+    if (mirrorSide == 2 && Object.keys(V.ALICE_PIECES).includes(piece))
+      return this.getColor(i, j) + V.ALICE_PIECES[piece];
     return "";
   }
 
   // Build board of the given (mirror)side
-  getSideBoard(mirrorSide)
-  {
+  getSideBoard(mirrorSide) {
     // Build corresponding board from complete board
     let sideBoard = ArrayFun.init(V.size.x, V.size.y, "");
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++)
         sideBoard[i][j] = this.getSquareOccupation(i, j, mirrorSide);
     }
     return sideBoard;
   }
 
   // NOTE: castle & enPassant https://www.chessvariants.com/other.dir/alice.html
-  getPotentialMovesFrom([x,y], sideBoard)
-  {
+  getPotentialMovesFrom([x, y], sideBoard) {
     const pieces = Object.keys(V.ALICE_CODES);
     const codes = Object.keys(V.ALICE_PIECES);
-    const mirrorSide = (pieces.includes(this.getPiece(x,y)) ? 1 : 2);
-    if (!sideBoard)
-      sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
-    const color = this.getColor(x,y);
+    const mirrorSide = pieces.includes(this.getPiece(x, y)) ? 1 : 2;
+    if (!sideBoard) sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
+    const color = this.getColor(x, y);
 
     // Search valid moves on sideBoard
     const saveBoard = this.board;
-    this.board = sideBoard[mirrorSide-1];
-    const moves = super.getPotentialMovesFrom([x,y])
-      .filter(m => {
-        // Filter out king moves which result in under-check position on
-        // current board (before mirror traversing)
-        let aprioriValid = true;
-        if (m.appear[0].p == V.KING)
-        {
-          this.play(m);
-          if (this.underCheck(color, sideBoard))
-            aprioriValid = false;
-          this.undo(m);
-        }
-        return aprioriValid;
-      });
+    this.board = sideBoard[mirrorSide - 1];
+    const moves = super.getPotentialMovesFrom([x, y]).filter(m => {
+      // Filter out king moves which result in under-check position on
+      // current board (before mirror traversing)
+      let aprioriValid = true;
+      if (m.appear[0].p == V.KING) {
+        this.play(m);
+        if (this.underCheck(color, sideBoard)) aprioriValid = false;
+        this.undo(m);
+      }
+      return aprioriValid;
+    });
     this.board = saveBoard;
 
     // Finally filter impossible moves
     const res = moves.filter(m => {
-      if (m.appear.length == 2) //castle
-      {
+      if (m.appear.length == 2) {
+        //castle
         // appear[i] must be an empty square on the other board
-        for (let psq of m.appear)
-        {
-          if (this.getSquareOccupation(psq.x,psq.y,3-mirrorSide) != V.EMPTY)
+        for (let psq of m.appear) {
+          if (this.getSquareOccupation(psq.x, psq.y, 3 - mirrorSide) != V.EMPTY)
             return false;
         }
-      }
-      else if (this.board[m.end.x][m.end.y] != V.EMPTY)
-      {
+      } else if (this.board[m.end.x][m.end.y] != V.EMPTY) {
         // Attempt to capture
-        const piece = this.getPiece(m.end.x,m.end.y);
-        if ((mirrorSide==1 && codes.includes(piece))
-          || (mirrorSide==2 && pieces.includes(piece)))
-        {
+        const piece = this.getPiece(m.end.x, m.end.y);
+        if (
+          (mirrorSide == 1 && codes.includes(piece)) ||
+          (mirrorSide == 2 && pieces.includes(piece))
+        ) {
           return false;
         }
       }
       // If the move is computed on board1, m.appear change for Alice pieces.
-      if (mirrorSide==1)
-      {
-        m.appear.forEach(psq => { //forEach: castling taken into account
+      if (mirrorSide == 1) {
+        m.appear.forEach(psq => {
+          //forEach: castling taken into account
           psq.p = V.ALICE_CODES[psq.p]; //goto board2
         });
-      }
-      else //move on board2: mark vanishing pieces as Alice
-      {
+      } //move on board2: mark vanishing pieces as Alice
+      else {
         m.vanish.forEach(psq => {
           psq.p = V.ALICE_CODES[psq.p];
         });
       }
       // Fix en-passant captures
-      if (m.vanish[0].p == V.PAWN && m.vanish.length == 2
-        && this.board[m.end.x][m.end.y] == V.EMPTY)
-      {
-        m.vanish[1].c = V.GetOppCol(this.getColor(x,y));
+      if (
+        m.vanish[0].p == V.PAWN &&
+        m.vanish.length == 2 &&
+        this.board[m.end.x][m.end.y] == V.EMPTY
+      ) {
+        m.vanish[1].c = V.GetOppCol(this.getColor(x, y));
         // In the special case of en-passant, if
         //  - board1 takes board2 : vanish[1] --> Alice
         //  - board2 takes board1 : vanish[1] --> normal
         let van = m.vanish[1];
-        if (mirrorSide==1 && codes.includes(this.getPiece(van.x,van.y)))
+        if (mirrorSide == 1 && codes.includes(this.getPiece(van.x, van.y)))
           van.p = V.ALICE_CODES[van.p];
-        else if (mirrorSide==2 && pieces.includes(this.getPiece(van.x,van.y)))
+        else if (
+          mirrorSide == 2 &&
+          pieces.includes(this.getPiece(van.x, van.y))
+        )
           van.p = V.ALICE_PIECES[van.p];
       }
       return true;
@@ -175,12 +159,9 @@ export const VariantRules = class AliceRules extends ChessRules
     return res;
   }
 
-  filterValid(moves, sideBoard)
-  {
-    if (moves.length == 0)
-      return [];
-    if (!sideBoard)
-      sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
+  filterValid(moves, sideBoard) {
+    if (moves.length == 0) return [];
+    if (!sideBoard) sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
     const color = this.turn;
     return moves.filter(m => {
       this.playSide(m, sideBoard); //no need to track flags
@@ -190,20 +171,17 @@ export const VariantRules = class AliceRules extends ChessRules
     });
   }
 
-  getAllValidMoves()
-  {
+  getAllValidMoves() {
     const color = this.turn;
-    const oppCol = V.GetOppCol(color);
     let potentialMoves = [];
     const sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
-    for (var i=0; i<V.size.x; i++)
-    {
-      for (var j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color)
-        {
-          Array.prototype.push.apply(potentialMoves,
-            this.getPotentialMovesFrom([i,j], sideBoard));
+    for (var i = 0; i < V.size.x; i++) {
+      for (var j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) {
+          Array.prototype.push.apply(
+            potentialMoves,
+            this.getPotentialMovesFrom([i, j], sideBoard)
+          );
         }
       }
     }
@@ -211,149 +189,129 @@ export const VariantRules = class AliceRules extends ChessRules
   }
 
   // Play on sideboards [TODO: only one sideBoard required]
-  playSide(move, sideBoard)
-  {
+  playSide(move, sideBoard) {
     const pieces = Object.keys(V.ALICE_CODES);
     move.vanish.forEach(psq => {
-      const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-      sideBoard[mirrorSide-1][psq.x][psq.y] = V.EMPTY;
+      const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+      sideBoard[mirrorSide - 1][psq.x][psq.y] = V.EMPTY;
     });
     move.appear.forEach(psq => {
-      const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-      const piece = (mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p]);
-      sideBoard[mirrorSide-1][psq.x][psq.y] = psq.c + piece;
-      if (piece == V.KING)
-        this.kingPos[psq.c] = [psq.x,psq.y];
+      const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+      const piece = mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p];
+      sideBoard[mirrorSide - 1][psq.x][psq.y] = psq.c + piece;
+      if (piece == V.KING) this.kingPos[psq.c] = [psq.x, psq.y];
     });
   }
 
   // Undo on sideboards
-  undoSide(move, sideBoard)
-  {
+  undoSide(move, sideBoard) {
     const pieces = Object.keys(V.ALICE_CODES);
     move.appear.forEach(psq => {
-      const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-      sideBoard[mirrorSide-1][psq.x][psq.y] = V.EMPTY;
+      const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+      sideBoard[mirrorSide - 1][psq.x][psq.y] = V.EMPTY;
     });
     move.vanish.forEach(psq => {
-      const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-      const piece = (mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p]);
-      sideBoard[mirrorSide-1][psq.x][psq.y] = psq.c + piece;
-      if (piece == V.KING)
-        this.kingPos[psq.c] = [psq.x,psq.y];
+      const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+      const piece = mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p];
+      sideBoard[mirrorSide - 1][psq.x][psq.y] = psq.c + piece;
+      if (piece == V.KING) this.kingPos[psq.c] = [psq.x, psq.y];
     });
   }
 
   // sideBoard: arg containing both boards (see getAllValidMoves())
-  underCheck(color, sideBoard)
-  {
+  underCheck(color, sideBoard) {
     const kp = this.kingPos[color];
-    const mirrorSide = (sideBoard[0][kp[0]][kp[1]] != V.EMPTY ? 1 : 2);
+    const mirrorSide = sideBoard[0][kp[0]][kp[1]] != V.EMPTY ? 1 : 2;
     let saveBoard = this.board;
-    this.board = sideBoard[mirrorSide-1];
+    this.board = sideBoard[mirrorSide - 1];
     let res = this.isAttacked(kp, [V.GetOppCol(color)]);
     this.board = saveBoard;
     return res;
   }
 
-  getCheckSquares(color)
-  {
+  getCheckSquares(color) {
     const pieces = Object.keys(V.ALICE_CODES);
     const kp = this.kingPos[color];
-    const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
+    const mirrorSide = pieces.includes(this.getPiece(kp[0], kp[1])) ? 1 : 2;
     let sideBoard = this.getSideBoard(mirrorSide);
     let saveBoard = this.board;
     this.board = sideBoard;
     let res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
-      ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
-      : [ ];
+      ? [JSON.parse(JSON.stringify(this.kingPos[color]))]
+      : [];
     this.board = saveBoard;
     return res;
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move); //standard king
     const piece = move.vanish[0].p;
     const c = move.vanish[0].c;
     // "l" = Alice king
-    if (piece == "l")
-    {
+    if (piece == "l") {
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
-      this.castleFlags[c] = [false,false];
+      this.castleFlags[c] = [false, false];
     }
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
-    if (move.vanish[0].p == "l")
-      this.kingPos[c] = [move.start.x, move.start.y];
+    if (move.vanish[0].p == "l") this.kingPos[c] = [move.start.x, move.start.y];
   }
 
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
 
     const pieces = Object.keys(V.ALICE_CODES);
     const color = this.turn;
     const kp = this.kingPos[color];
-    const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
+    const mirrorSide = pieces.includes(this.getPiece(kp[0], kp[1])) ? 1 : 2;
     let sideBoard = this.getSideBoard(mirrorSide);
     let saveBoard = this.board;
     this.board = sideBoard;
     let res = "*";
     if (!this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
       res = "1/2";
-    else
-      res = (color == "w" ? "0-1" : "1-0");
+    else res = color == "w" ? "0-1" : "1-0";
     this.board = saveBoard;
     return res;
   }
 
-  static get VALUES()
-  {
-    return Object.assign(
-      ChessRules.VALUES,
-      {
-        's': 1,
-        'u': 5,
-        'o': 3,
-        'c': 3,
-        't': 9,
-        'l': 1000,
-      }
-    );
+  static get VALUES() {
+    return Object.assign(ChessRules.VALUES, {
+      s: 1,
+      u: 5,
+      o: 3,
+      c: 3,
+      t: 9,
+      l: 1000
+    });
   }
 
-  getNotation(move)
-  {
-    if (move.appear.length == 2 && move.appear[0].p == V.KING)
-    {
-      if (move.end.y < move.start.y)
-        return "0-0-0";
-      else
-        return "0-0";
+  getNotation(move) {
+    if (move.appear.length == 2 && move.appear[0].p == V.KING) {
+      if (move.end.y < move.start.y) return "0-0-0";
+      return "0-0";
     }
 
     const finalSquare = V.CoordsToSquare(move.end);
     const piece = this.getPiece(move.start.x, move.start.y);
 
-    const captureMark = (move.vanish.length > move.appear.length ? "x" : "");
+    const captureMark = move.vanish.length > move.appear.length ? "x" : "";
     let pawnMark = "";
-    if (["p","s"].includes(piece) && captureMark.length == 1)
+    if (["p", "s"].includes(piece) && captureMark.length == 1)
       pawnMark = V.CoordToColumn(move.start.y); //start column
 
     // Piece or pawn movement
     let notation = piece.toUpperCase() + pawnMark + captureMark + finalSquare;
-    if (['s','p'].includes(piece) && !['s','p'].includes(move.appear[0].p))
-    {
+    if (["s", "p"].includes(piece) && !["s", "p"].includes(move.appear[0].p)) {
       // Promotion
       notation += "=" + move.appear[0].p.toUpperCase();
     }
     return notation;
   }
-}
+};
diff --git a/client/src/variants/Antiking.js b/client/src/variants/Antiking.js
index 610dd257..a13247a1 100644
--- a/client/src/variants/Antiking.js
+++ b/client/src/variants/Antiking.js
@@ -1,172 +1,168 @@
 import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
-export const VariantRules = class AntikingRules extends ChessRules
-{
-  static getPpath(b)
-  {
-    return b[1]=='a' ? "Antiking/"+b : b;
+export const VariantRules = class AntikingRules extends ChessRules {
+  static getPpath(b) {
+    return b[1] == "a" ? "Antiking/" + b : b;
   }
 
-  static get ANTIKING() { return 'a'; }
+  static get ANTIKING() {
+    return "a";
+  }
 
-  static get PIECES()
-  {
+  static get PIECES() {
     return ChessRules.PIECES.concat([V.ANTIKING]);
   }
 
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    this.antikingPos = {'w':[-1,-1], 'b':[-1,-1]};
+    this.antikingPos = { w: [-1, -1], b: [-1, -1] };
     const rows = V.ParseFen(fen).position.split("/");
-    for (let i=0; i<rows.length; i++)
-    {
+    for (let i = 0; i < rows.length; i++) {
       let k = 0;
-      for (let j=0; j<rows[i].length; j++)
-      {
-        switch (rows[i].charAt(j))
-        {
-          case 'a':
-            this.antikingPos['b'] = [i,k];
+      for (let j = 0; j < rows[i].length; j++) {
+        switch (rows[i].charAt(j)) {
+          case "a":
+            this.antikingPos["b"] = [i, k];
             break;
-          case 'A':
-            this.antikingPos['w'] = [i,k];
+          case "A":
+            this.antikingPos["w"] = [i, k];
             break;
-          default:
+          default: {
             const num = parseInt(rows[i].charAt(j));
-            if (!isNaN(num))
-              k += (num-1);
+            if (!isNaN(num)) k += num - 1;
+          }
         }
         k++;
       }
     }
   }
 
-  canTake([x1,y1], [x2,y2])
-  {
-    const piece1 = this.getPiece(x1,y1);
-    const piece2 = this.getPiece(x2,y2);
-    const color1 = this.getColor(x1,y1);
-    const color2 = this.getColor(x2,y2);
-    return piece2 != "a" &&
-      ((piece1 != "a" && color1 != color2) || (piece1 == "a" && color1 == color2));
+  canTake([x1, y1], [x2, y2]) {
+    const piece1 = this.getPiece(x1, y1);
+    const piece2 = this.getPiece(x2, y2);
+    const color1 = this.getColor(x1, y1);
+    const color2 = this.getColor(x2, y2);
+    return (
+      piece2 != "a" &&
+      ((piece1 != "a" && color1 != color2) ||
+        (piece1 == "a" && color1 == color2))
+    );
   }
 
-  getPotentialMovesFrom([x,y])
-  {
-    switch (this.getPiece(x,y))
-    {
+  getPotentialMovesFrom([x, y]) {
+    switch (this.getPiece(x, y)) {
       case V.ANTIKING:
-        return this.getPotentialAntikingMoves([x,y]);
+        return this.getPotentialAntikingMoves([x, y]);
       default:
-        return super.getPotentialMovesFrom([x,y]);
+        return super.getPotentialMovesFrom([x, y]);
     }
   }
 
-  getPotentialAntikingMoves(sq)
-  {
-    return this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+  getPotentialAntikingMoves(sq) {
+    return this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
-  isAttacked(sq, colors)
-  {
-    return (super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors));
+  isAttacked(sq, colors) {
+    return (
+      super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors)
+    );
   }
 
-  isAttackedByKing([x,y], colors)
-  {
-    if (this.getPiece(x,y) == V.ANTIKING)
-      return false; //antiking is not attacked by king
-    return this.isAttackedBySlideNJump([x,y], colors, V.KING,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+  isAttackedByKing([x, y], colors) {
+    if (this.getPiece(x, y) == V.ANTIKING) return false; //antiking is not attacked by king
+    return this.isAttackedBySlideNJump(
+      [x, y],
+      colors,
+      V.KING,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
-  isAttackedByAntiking([x,y], colors)
-  {
-    if ([V.KING,V.ANTIKING].includes(this.getPiece(x,y)))
-      return false; //(anti)king is not attacked by antiking
-    return this.isAttackedBySlideNJump([x,y], colors, V.ANTIKING,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+  isAttackedByAntiking([x, y], colors) {
+    if ([V.KING, V.ANTIKING].includes(this.getPiece(x, y))) return false; //(anti)king is not attacked by antiking
+    return this.isAttackedBySlideNJump(
+      [x, y],
+      colors,
+      V.ANTIKING,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
-  underCheck(color)
-  {
+  underCheck(color) {
     const oppCol = V.GetOppCol(color);
-    let res = this.isAttacked(this.kingPos[color], [oppCol])
-      || !this.isAttacked(this.antikingPos[color], [oppCol]);
+    let res =
+      this.isAttacked(this.kingPos[color], [oppCol]) ||
+      !this.isAttacked(this.antikingPos[color], [oppCol]);
     return res;
   }
 
-  getCheckSquares(color)
-  {
+  getCheckSquares(color) {
     let res = super.getCheckSquares(color);
     if (!this.isAttacked(this.antikingPos[color], [V.GetOppCol(color)]))
       res.push(JSON.parse(JSON.stringify(this.antikingPos[color])));
     return res;
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
     const piece = move.vanish[0].p;
     const c = move.vanish[0].c;
     // Update antiking position
-    if (piece == V.ANTIKING)
-    {
+    if (piece == V.ANTIKING) {
       this.antikingPos[c][0] = move.appear[0].x;
       this.antikingPos[c][1] = move.appear[0].y;
     }
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
     if (move.vanish[0].p == V.ANTIKING)
       this.antikingPos[c] = [move.start.x, move.start.y];
   }
 
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
 
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
-    if (!this.isAttacked(this.kingPos[color], [oppCol])
-      && this.isAttacked(this.antikingPos[color], [oppCol]))
-    {
+    if (
+      !this.isAttacked(this.kingPos[color], [oppCol]) &&
+      this.isAttacked(this.antikingPos[color], [oppCol])
+    ) {
       return "1/2";
     }
     return color == "w" ? "0-1" : "1-0";
   }
 
   static get VALUES() {
-    return Object.assign(
-      ChessRules.VALUES,
-      { 'a': 1000 }
-    );
+    return Object.assign(ChessRules.VALUES, { a: 1000 });
   }
 
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(8), "b": new Array(8) };
-    let antikingPos = { "w": -1, "b": -1 };
-    for (let c of ["w","b"])
-    {
+  static GenRandInitFen() {
+    let pieces = { w: new Array(8), b: new Array(8) };
+    let antikingPos = { w: -1, b: -1 };
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(8);
 
       // Get random squares for bishops, but avoid corners; because,
       // if an antiking blocks a cornered bishop, it can never be checkmated
-      let randIndex = 2 * randInt(1,4);
+      let randIndex = 2 * randInt(1, 4);
       const bishop1Pos = positions[randIndex];
       let randIndex_tmp = 2 * randInt(3) + 1;
       const bishop2Pos = positions[randIndex_tmp];
-      positions.splice(Math.max(randIndex,randIndex_tmp), 1);
-      positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       randIndex = randInt(6);
       const knight1Pos = positions[randIndex];
@@ -186,22 +182,34 @@ export const VariantRules = class AntikingRules extends ChessRules
       // Random squares for antikings
       antikingPos[c] = randInt(8);
 
-      pieces[c][rook1Pos] = 'r';
-      pieces[c][knight1Pos] = 'n';
-      pieces[c][bishop1Pos] = 'b';
-      pieces[c][queenPos] = 'q';
-      pieces[c][kingPos] = 'k';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][rook2Pos] = 'r';
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
     }
-    const ranks23_black = "pppppppp/" + (antikingPos["w"]>0?antikingPos["w"]:"")
-      + "A" + (antikingPos["w"]<7?7-antikingPos["w"]:"");
-    const ranks23_white = (antikingPos["b"]>0?antikingPos["b"]:"") + "a"
-      + (antikingPos["b"]<7?7-antikingPos["b"]:"") + "/PPPPPPPP";
-    return pieces["b"].join("") + "/" + ranks23_black +
+    const ranks23_black =
+      "pppppppp/" +
+      (antikingPos["w"] > 0 ? antikingPos["w"] : "") +
+      "A" +
+      (antikingPos["w"] < 7 ? 7 - antikingPos["w"] : "");
+    const ranks23_white =
+      (antikingPos["b"] > 0 ? antikingPos["b"] : "") +
+      "a" +
+      (antikingPos["b"] < 7 ? 7 - antikingPos["b"] : "") +
+      "/PPPPPPPP";
+    return (
+      pieces["b"].join("") +
+      "/" +
+      ranks23_black +
       "/8/8/" +
-      ranks23_white + "/" + pieces["w"].join("").toUpperCase() +
-      " w 0 1111 -";
+      ranks23_white +
+      "/" +
+      pieces["w"].join("").toUpperCase() +
+      " w 0 1111 -"
+    );
   }
-}
+};
diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js
index 3d4f541f..996f2d1a 100644
--- a/client/src/variants/Atomic.js
+++ b/client/src/variants/Atomic.js
@@ -1,29 +1,43 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
-export const VariantRules = class AtomicRules extends ChessRules
-{
-  getPotentialMovesFrom([x,y])
-  {
-    let moves = super.getPotentialMovesFrom([x,y]);
+export const VariantRules = class AtomicRules extends ChessRules {
+  getPotentialMovesFrom([x, y]) {
+    let moves = super.getPotentialMovesFrom([x, y]);
 
     // Handle explosions
     moves.forEach(m => {
-      if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles
-      {
+      if (m.vanish.length > 1 && m.appear.length <= 1) {
+        //avoid castles
         // Explosion! OPTION (TODO?): drop moves which explode our king here
-        let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ];
-        for (let step of steps)
-        {
+        let steps = [
+          [-1, -1],
+          [-1, 0],
+          [-1, 1],
+          [0, -1],
+          [0, 1],
+          [1, -1],
+          [1, 0],
+          [1, 1]
+        ];
+        for (let step of steps) {
           let x = m.end.x + step[0];
           let y = m.end.y + step[1];
-          if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY
-            && this.getPiece(x,y) != V.PAWN)
-          {
+          if (
+            V.OnBoard(x, y) &&
+            this.board[x][y] != V.EMPTY &&
+            this.getPiece(x, y) != V.PAWN
+          ) {
             m.vanish.push(
-              new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
+              new PiPo({
+                p: this.getPiece(x, y),
+                c: this.getColor(x, y),
+                x: x,
+                y: y
+              })
+            );
           }
         }
-        m.end = {x:m.appear[0].x, y:m.appear[0].y};
+        m.end = { x: m.appear[0].x, y: m.appear[0].y };
         m.appear.pop(); //Nothin appears in this case
       }
     });
@@ -31,56 +45,53 @@ export const VariantRules = class AtomicRules extends ChessRules
     return moves;
   }
 
-  getPotentialKingMoves([x,y])
-  {
+  getPotentialKingMoves([x, y]) {
     // King cannot capture:
     let moves = [];
     const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-    for (let step of steps)
-    {
+    for (let step of steps) {
       const i = x + step[0];
       const j = y + step[1];
-      if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
-        moves.push(this.getBasicMove([x,y], [i,j]));
+      if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY)
+        moves.push(this.getBasicMove([x, y], [i, j]));
     }
-    return moves.concat(this.getCastleMoves([x,y]));
+    return moves.concat(this.getCastleMoves([x, y]));
   }
 
-  isAttacked(sq, colors)
-  {
-    if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors))
+  isAttacked(sq, colors) {
+    if (
+      this.getPiece(sq[0], sq[1]) == V.KING &&
+      this.isAttackedByKing(sq, colors)
+    )
       return false; //king cannot take...
-    return (this.isAttackedByPawn(sq, colors)
-      || this.isAttackedByRook(sq, colors)
-      || this.isAttackedByKnight(sq, colors)
-      || this.isAttackedByBishop(sq, colors)
-      || this.isAttackedByQueen(sq, colors));
+    return (
+      this.isAttackedByPawn(sq, colors) ||
+      this.isAttackedByRook(sq, colors) ||
+      this.isAttackedByKnight(sq, colors) ||
+      this.isAttackedByBishop(sq, colors) ||
+      this.isAttackedByQueen(sq, colors)
+    );
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
-    const color = move.vanish[0].c;
-    if (move.appear.length == 0) //capture
-    {
-      const firstRank = {"w": 7, "b": 0};
-      for (let c of ["w","b"])
-      {
+    if (move.appear.length == 0) {
+      //capture
+      const firstRank = { w: 7, b: 0 };
+      for (let c of ["w", "b"]) {
         // Did we explode king of color c ? (TODO: remove move earlier)
-        if (Math.abs(this.kingPos[c][0]-move.end.x) <= 1
-          && Math.abs(this.kingPos[c][1]-move.end.y) <= 1)
-        {
-          this.kingPos[c] = [-1,-1];
-          this.castleFlags[c] = [false,false];
-        }
-        else
-        {
+        if (
+          Math.abs(this.kingPos[c][0] - move.end.x) <= 1 &&
+          Math.abs(this.kingPos[c][1] - move.end.y) <= 1
+        ) {
+          this.kingPos[c] = [-1, -1];
+          this.castleFlags[c] = [false, false];
+        } else {
           // Now check if init rook(s) exploded
-          if (Math.abs(move.end.x-firstRank[c]) <= 1)
-          {
-            if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][0]) <= 1)
+          if (Math.abs(move.end.x - firstRank[c]) <= 1) {
+            if (Math.abs(move.end.y - this.INIT_COL_ROOK[c][0]) <= 1)
               this.castleFlags[c][0] = false;
-            if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][1]) <= 1)
+            if (Math.abs(move.end.y - this.INIT_COL_ROOK[c][1]) <= 1)
               this.castleFlags[c][1] = false;
           }
         }
@@ -88,59 +99,56 @@ export const VariantRules = class AtomicRules extends ChessRules
     }
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
     const oppCol = V.GetOppCol(c);
-    if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; }))
-    {
+    if (
+      [this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => {
+        return e < 0;
+      })
+    ) {
       // There is a chance that last move blowed some king away..
-      for (let psq of move.vanish)
-      {
-        if (psq.p == 'k')
-          this.kingPos[psq.c==c ? c : oppCol] = [psq.x, psq.y];
+      for (let psq of move.vanish) {
+        if (psq.p == "k")
+          this.kingPos[psq.c == c ? c : oppCol] = [psq.x, psq.y];
       }
     }
   }
 
-  underCheck(color)
-  {
+  underCheck(color) {
     const oppCol = V.GetOppCol(color);
     let res = undefined;
     // If our king disappeared, move is not valid
-    if (this.kingPos[color][0] < 0)
-      res = true;
+    if (this.kingPos[color][0] < 0) res = true;
     // If opponent king disappeared, move is valid
-    else if (this.kingPos[oppCol][0] < 0)
-      res = false;
+    else if (this.kingPos[oppCol][0] < 0) res = false;
     // Otherwise, if we remain under check, move is not valid
-    else
-      res = this.isAttacked(this.kingPos[color], [oppCol]);
+    else res = this.isAttacked(this.kingPos[color], [oppCol]);
     return res;
   }
 
-  getCheckSquares(color)
-  {
-    let res = [ ];
-    if (this.kingPos[color][0] >= 0 //king might have exploded
-      && this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
-    {
-      res = [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
+  getCheckSquares(color) {
+    let res = [];
+    if (
+      this.kingPos[color][0] >= 0 && //king might have exploded
+      this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
+    ) {
+      res = [JSON.parse(JSON.stringify(this.kingPos[color]))];
     }
     return res;
   }
 
-  getCurrentScore()
-  {
+  getCurrentScore() {
     const color = this.turn;
     const kp = this.kingPos[color];
-    if (kp[0] < 0) //king disappeared
+    if (kp[0] < 0)
+      //king disappeared
       return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove()) // game not over
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
-    if (!this.isAttacked(kp, [V.GetOppCol(color)]))
-      return "1/2";
+    if (!this.isAttacked(kp, [V.GetOppCol(color)])) return "1/2";
     return color == "w" ? "0-1" : "1-0"; //checkmate
   }
-}
+};
diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js
index 5ac9a011..79fc73ce 100644
--- a/client/src/variants/Baroque.js
+++ b/client/src/variants/Baroque.js
@@ -2,54 +2,54 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
-export const VariantRules = class BaroqueRules extends ChessRules
-{
-  static get HasFlags() { return false; }
+export const VariantRules = class BaroqueRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
 
-  static get HasEnpassant() { return false; }
+  static get HasEnpassant() {
+    return false;
+  }
 
-  static getPpath(b)
-  {
-    if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1)
+  static getPpath(b) {
+    if (b[1] == "m")
+      //'m' for Immobilizer (I is too similar to 1)
       return "Baroque/" + b;
     return b; //usual piece
   }
 
-  static get PIECES()
-  {
+  static get PIECES() {
     return ChessRules.PIECES.concat([V.IMMOBILIZER]);
   }
 
   // No castling, but checks, so keep track of kings
-  setOtherVariables(fen)
-  {
-    this.kingPos = {'w':[-1,-1], 'b':[-1,-1]};
+  setOtherVariables(fen) {
+    this.kingPos = { w: [-1, -1], b: [-1, -1] };
     const fenParts = fen.split(" ");
     const position = fenParts[0].split("/");
-    for (let i=0; i<position.length; i++)
-    {
+    for (let i = 0; i < position.length; i++) {
       let k = 0;
-      for (let j=0; j<position[i].length; j++)
-      {
-        switch (position[i].charAt(j))
-        {
-          case 'k':
-            this.kingPos['b'] = [i,k];
+      for (let j = 0; j < position[i].length; j++) {
+        switch (position[i].charAt(j)) {
+          case "k":
+            this.kingPos["b"] = [i, k];
             break;
-          case 'K':
-            this.kingPos['w'] = [i,k];
+          case "K":
+            this.kingPos["w"] = [i, k];
             break;
-          default:
-            let num = parseInt(position[i].charAt(j));
-            if (!isNaN(num))
-              k += (num-1);
+          default: {
+            const num = parseInt(position[i].charAt(j));
+            if (!isNaN(num)) k += num - 1;
+          }
         }
         k++;
       }
     }
   }
 
-  static get IMMOBILIZER() { return 'm'; }
+  static get IMMOBILIZER() {
+    return "m";
+  }
   // Although other pieces keep their names here for coding simplicity,
   // keep in mind that:
   //  - a "rook" is a coordinator, capturing by coordinating with the king
@@ -58,114 +58,103 @@ export const VariantRules = class BaroqueRules extends ChessRules
   //  - a "queen" is a withdrawer, capturing by moving away from pieces
 
   // Is piece on square (x,y) immobilized?
-  isImmobilized([x,y])
-  {
-    const piece = this.getPiece(x,y);
-    const color = this.getColor(x,y);
+  isImmobilized([x, y]) {
+    const piece = this.getPiece(x, y);
+    const color = this.getColor(x, y);
     const oppCol = V.GetOppCol(color);
     const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-    outerLoop:
-    for (let step of adjacentSteps)
-    {
-      const [i,j] = [x+step[0],y+step[1]];
-      if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY
-        && this.getColor(i,j) == oppCol)
-      {
-        const oppPiece = this.getPiece(i,j);
-        if (oppPiece == V.IMMOBILIZER)
-        {
+    for (let step of adjacentSteps) {
+      const [i, j] = [x + step[0], y + step[1]];
+      if (
+        V.OnBoard(i, j) &&
+        this.board[i][j] != V.EMPTY &&
+        this.getColor(i, j) == oppCol
+      ) {
+        const oppPiece = this.getPiece(i, j);
+        if (oppPiece == V.IMMOBILIZER) {
           // Moving is impossible only if this immobilizer is not neutralized
-          for (let step2 of adjacentSteps)
-          {
-            const [i2,j2] = [i+step2[0],j+step2[1]];
-            if (i2 == x && j2 == y)
-              continue; //skip initial piece!
-            if (V.OnBoard(i2,j2) && this.board[i2][j2] != V.EMPTY
-              && this.getColor(i2,j2) == color)
-            {
-              if ([V.BISHOP,V.IMMOBILIZER].includes(this.getPiece(i2,j2)))
+          for (let step2 of adjacentSteps) {
+            const [i2, j2] = [i + step2[0], j + step2[1]];
+            if (i2 == x && j2 == y) continue; //skip initial piece!
+            if (
+              V.OnBoard(i2, j2) &&
+              this.board[i2][j2] != V.EMPTY &&
+              this.getColor(i2, j2) == color
+            ) {
+              if ([V.BISHOP, V.IMMOBILIZER].includes(this.getPiece(i2, j2)))
                 return false;
             }
           }
           return true; //immobilizer isn't neutralized
         }
         // Chameleons can't be immobilized twice, because there is only one immobilizer
-        if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER)
-          return true;
+        if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER) return true;
       }
     }
     return false;
   }
 
-  getPotentialMovesFrom([x,y])
-  {
+  getPotentialMovesFrom([x, y]) {
     // Pre-check: is thing on this square immobilized?
-    if (this.isImmobilized([x,y]))
-      return [];
-    switch (this.getPiece(x,y))
-    {
+    if (this.isImmobilized([x, y])) return [];
+    switch (this.getPiece(x, y)) {
       case V.IMMOBILIZER:
-        return this.getPotentialImmobilizerMoves([x,y]);
+        return this.getPotentialImmobilizerMoves([x, y]);
       default:
-        return super.getPotentialMovesFrom([x,y]);
+        return super.getPotentialMovesFrom([x, y]);
     }
   }
 
-  getSlideNJumpMoves([x,y], steps, oneStep)
-  {
-    const color = this.getColor(x,y);
-    const piece = this.getPiece(x,y);
+  getSlideNJumpMoves([x, y], steps, oneStep) {
+    const piece = this.getPiece(x, y);
     let moves = [];
-    outerLoop:
-    for (let step of steps)
-    {
+    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 !== undefined)
-          continue outerLoop;
+      while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+        moves.push(this.getBasicMove([x, y], [i, j]));
+        if (oneStep !== undefined) continue outerLoop;
         i += step[0];
         j += step[1];
       }
       // Only king can take on occupied square:
-      if (piece==V.KING && V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
-        moves.push(this.getBasicMove([x,y], [i,j]));
+      if (piece == V.KING && V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
+        moves.push(this.getBasicMove([x, y], [i, j]));
     }
     return moves;
   }
 
   // Modify capturing moves among listed pawn moves
-  addPawnCaptures(moves, byChameleon)
-  {
+  addPawnCaptures(moves, byChameleon) {
     const steps = V.steps[V.ROOK];
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
     moves.forEach(m => {
-      if (!!byChameleon && m.start.x!=m.end.x && m.start.y!=m.end.y)
-        return; //chameleon not moving as pawn
+      if (!!byChameleon && m.start.x != m.end.x && m.start.y != m.end.y) return; //chameleon not moving as pawn
       // Try capturing in every direction
-      for (let step of steps)
-      {
-        const sq2 = [m.end.x+2*step[0],m.end.y+2*step[1]];
-        if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] != V.EMPTY
-          && this.getColor(sq2[0],sq2[1]) == color)
-        {
+      for (let step of steps) {
+        const sq2 = [m.end.x + 2 * step[0], m.end.y + 2 * step[1]];
+        if (
+          V.OnBoard(sq2[0], sq2[1]) &&
+          this.board[sq2[0]][sq2[1]] != V.EMPTY &&
+          this.getColor(sq2[0], sq2[1]) == color
+        ) {
           // Potential capture
-          const sq1 = [m.end.x+step[0],m.end.y+step[1]];
-          if (this.board[sq1[0]][sq1[1]] != V.EMPTY
-            && this.getColor(sq1[0],sq1[1]) == oppCol)
-          {
-            const piece1 = this.getPiece(sq1[0],sq1[1]);
-            if (!byChameleon || piece1 == V.PAWN)
-            {
-              m.vanish.push(new PiPo({
-                x:sq1[0],
-                y:sq1[1],
-                c:oppCol,
-                p:piece1
-              }));
+          const sq1 = [m.end.x + step[0], m.end.y + step[1]];
+          if (
+            this.board[sq1[0]][sq1[1]] != V.EMPTY &&
+            this.getColor(sq1[0], sq1[1]) == oppCol
+          ) {
+            const piece1 = this.getPiece(sq1[0], sq1[1]);
+            if (!byChameleon || piece1 == V.PAWN) {
+              m.vanish.push(
+                new PiPo({
+                  x: sq1[0],
+                  y: sq1[1],
+                  c: oppCol,
+                  p: piece1
+                })
+              );
             }
           }
         }
@@ -174,37 +163,33 @@ export const VariantRules = class BaroqueRules extends ChessRules
   }
 
   // "Pincer"
-  getPotentialPawnMoves([x,y])
-  {
-    let moves = super.getPotentialRookMoves([x,y]);
+  getPotentialPawnMoves([x, y]) {
+    let moves = super.getPotentialRookMoves([x, y]);
     this.addPawnCaptures(moves);
     return moves;
   }
 
-  addRookCaptures(moves, byChameleon)
-  {
+  addRookCaptures(moves, byChameleon) {
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
     const kp = this.kingPos[color];
     moves.forEach(m => {
       // Check piece-king rectangle (if any) corners for enemy pieces
-      if (m.end.x == kp[0] || m.end.y == kp[1])
-        return; //"flat rectangle"
+      if (m.end.x == kp[0] || m.end.y == kp[1]) return; //"flat rectangle"
       const corner1 = [m.end.x, kp[1]];
       const corner2 = [kp[0], m.end.y];
-      for (let [i,j] of [corner1,corner2])
-      {
-        if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol)
-        {
-          const piece = this.getPiece(i,j);
-          if (!byChameleon || piece == V.ROOK)
-          {
-            m.vanish.push( new PiPo({
-              x:i,
-              y:j,
-              p:piece,
-              c:oppCol
-            }) );
+      for (let [i, j] of [corner1, corner2]) {
+        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == oppCol) {
+          const piece = this.getPiece(i, j);
+          if (!byChameleon || piece == V.ROOK) {
+            m.vanish.push(
+              new PiPo({
+                x: i,
+                y: j,
+                p: piece,
+                c: oppCol
+              })
+            );
           }
         }
       }
@@ -212,88 +197,84 @@ export const VariantRules = class BaroqueRules extends ChessRules
   }
 
   // Coordinator
-  getPotentialRookMoves(sq)
-  {
+  getPotentialRookMoves(sq) {
     let moves = super.getPotentialQueenMoves(sq);
     this.addRookCaptures(moves);
     return moves;
   }
 
   // Long-leaper
-  getKnightCaptures(startSquare, byChameleon)
-  {
+  getKnightCaptures(startSquare, byChameleon) {
     // Look in every direction for captures
     const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
     let moves = [];
-    const [x,y] = [startSquare[0],startSquare[1]];
-    const piece = this.getPiece(x,y); //might be a chameleon!
-    outerLoop:
-    for (let step of steps)
-    {
-      let [i,j] = [x+step[0], y+step[1]];
-      while (V.OnBoard(i,j) && this.board[i][j]==V.EMPTY)
-      {
+    const [x, y] = [startSquare[0], startSquare[1]];
+    const piece = this.getPiece(x, y); //might be a chameleon!
+    outerLoop: for (let step of steps) {
+      let [i, j] = [x + step[0], y + step[1]];
+      while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
         i += step[0];
         j += step[1];
       }
-      if (!V.OnBoard(i,j) || this.getColor(i,j)==color
-        || (!!byChameleon && this.getPiece(i,j)!=V.KNIGHT))
-      {
+      if (
+        !V.OnBoard(i, j) ||
+        this.getColor(i, j) == color ||
+        (!!byChameleon && this.getPiece(i, j) != V.KNIGHT)
+      ) {
         continue;
       }
       // last(thing), cur(thing) : stop if "cur" is our color, or beyond board limits,
       // or if "last" isn't empty and cur neither. Otherwise, if cur is empty then
       // add move until cur square; if cur is occupied then stop if !!byChameleon and
       // the square not occupied by a leaper.
-      let last = [i,j];
-      let cur = [i+step[0],j+step[1]];
-      let vanished = [ new PiPo({x:x,y:y,c:color,p:piece}) ];
-      while (V.OnBoard(cur[0],cur[1]))
-      {
-        if (this.board[last[0]][last[1]] != V.EMPTY)
-        {
-          const oppPiece = this.getPiece(last[0],last[1]);
-          if (!!byChameleon && oppPiece != V.KNIGHT)
-            continue outerLoop;
+      let last = [i, j];
+      let cur = [i + step[0], j + step[1]];
+      let vanished = [new PiPo({ x: x, y: y, c: color, p: piece })];
+      while (V.OnBoard(cur[0], cur[1])) {
+        if (this.board[last[0]][last[1]] != V.EMPTY) {
+          const oppPiece = this.getPiece(last[0], last[1]);
+          if (!!byChameleon && oppPiece != V.KNIGHT) continue outerLoop;
           // Something to eat:
-          vanished.push( new PiPo({x:last[0],y:last[1],c:oppCol,p:oppPiece}) );
+          vanished.push(
+            new PiPo({ x: last[0], y: last[1], c: oppCol, p: oppPiece })
+          );
         }
-        if (this.board[cur[0]][cur[1]] != V.EMPTY)
-        {
-          if (this.getColor(cur[0],cur[1]) == color
-            || this.board[last[0]][last[1]] != V.EMPTY) //TODO: redundant test
-          {
+        if (this.board[cur[0]][cur[1]] != V.EMPTY) {
+          if (
+            this.getColor(cur[0], cur[1]) == color ||
+            this.board[last[0]][last[1]] != V.EMPTY
+          ) {
+            //TODO: redundant test
             continue outerLoop;
           }
+        } else {
+          moves.push(
+            new Move({
+              appear: [new PiPo({ x: cur[0], y: cur[1], c: color, p: piece })],
+              vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
+              start: { x: x, y: y },
+              end: { x: cur[0], y: cur[1] }
+            })
+          );
         }
-        else
-        {
-          moves.push(new Move({
-            appear: [ new PiPo({x:cur[0],y:cur[1],c:color,p:piece}) ],
-            vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
-            start: {x:x,y:y},
-            end: {x:cur[0],y:cur[1]}
-          }));
-        }
-        last = [last[0]+step[0],last[1]+step[1]];
-        cur = [cur[0]+step[0],cur[1]+step[1]];
+        last = [last[0] + step[0], last[1] + step[1]];
+        cur = [cur[0] + step[0], cur[1] + step[1]];
       }
     }
     return moves;
   }
 
   // Long-leaper
-  getPotentialKnightMoves(sq)
-  {
+  getPotentialKnightMoves(sq) {
     return super.getPotentialQueenMoves(sq).concat(this.getKnightCaptures(sq));
   }
 
-  getPotentialBishopMoves([x,y])
-  {
-    let moves = super.getPotentialQueenMoves([x,y])
-      .concat(this.getKnightCaptures([x,y],"asChameleon"));
+  getPotentialBishopMoves([x, y]) {
+    let moves = super
+      .getPotentialQueenMoves([x, y])
+      .concat(this.getKnightCaptures([x, y], "asChameleon"));
     // No "king capture" because king cannot remain under check
     this.addPawnCaptures(moves, "asChameleon");
     this.addRookCaptures(moves, "asChameleon");
@@ -302,111 +283,119 @@ export const VariantRules = class BaroqueRules extends ChessRules
     let mergedMoves = {};
     moves.forEach(m => {
       const key = m.end.x + V.size.x * m.end.y;
-      if (!mergedMoves[key])
-        mergedMoves[key] = m;
-      else
-      {
-        for (let i=1; i<m.vanish.length; i++)
+      if (!mergedMoves[key]) mergedMoves[key] = m;
+      else {
+        for (let i = 1; i < m.vanish.length; i++)
           mergedMoves[key].vanish.push(m.vanish[i]);
       }
     });
     // Finally return an array
     moves = [];
-    Object.keys(mergedMoves).forEach(k => { moves.push(mergedMoves[k]); });
+    Object.keys(mergedMoves).forEach(k => {
+      moves.push(mergedMoves[k]);
+    });
     return moves;
   }
 
   // Withdrawer
-  addQueenCaptures(moves, byChameleon)
-  {
-    if (moves.length == 0)
-      return;
-    const [x,y] = [moves[0].start.x,moves[0].start.y];
+  addQueenCaptures(moves, byChameleon) {
+    if (moves.length == 0) return;
+    const [x, y] = [moves[0].start.x, moves[0].start.y];
     const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
     let capturingDirections = [];
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
     adjacentSteps.forEach(step => {
-      const [i,j] = [x+step[0],y+step[1]];
-      if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol
-        && (!byChameleon || this.getPiece(i,j) == V.QUEEN))
-      {
+      const [i, j] = [x + step[0], y + step[1]];
+      if (
+        V.OnBoard(i, j) &&
+        this.board[i][j] != V.EMPTY &&
+        this.getColor(i, j) == oppCol &&
+        (!byChameleon || this.getPiece(i, j) == V.QUEEN)
+      ) {
         capturingDirections.push(step);
       }
     });
     moves.forEach(m => {
       const step = [
-        m.end.x!=x ? (m.end.x-x)/Math.abs(m.end.x-x) : 0,
-        m.end.y!=y ? (m.end.y-y)/Math.abs(m.end.y-y) : 0
+        m.end.x != x ? (m.end.x - x) / Math.abs(m.end.x - x) : 0,
+        m.end.y != y ? (m.end.y - y) / Math.abs(m.end.y - y) : 0
       ];
       // NOTE: includes() and even _.isEqual() functions fail...
       // TODO: this test should be done only once per direction
-      if (capturingDirections.some(dir =>
-        { return (dir[0]==-step[0] && dir[1]==-step[1]); }))
-      {
-        const [i,j] = [x-step[0],y-step[1]];
-        m.vanish.push(new PiPo({
-          x:i,
-          y:j,
-          p:this.getPiece(i,j),
-          c:oppCol
-        }));
+      if (
+        capturingDirections.some(dir => {
+          return dir[0] == -step[0] && dir[1] == -step[1];
+        })
+      ) {
+        const [i, j] = [x - step[0], y - step[1]];
+        m.vanish.push(
+          new PiPo({
+            x: i,
+            y: j,
+            p: this.getPiece(i, j),
+            c: oppCol
+          })
+        );
       }
     });
   }
 
-  getPotentialQueenMoves(sq)
-  {
+  getPotentialQueenMoves(sq) {
     let moves = super.getPotentialQueenMoves(sq);
     this.addQueenCaptures(moves);
     return moves;
   }
 
-  getPotentialImmobilizerMoves(sq)
-  {
+  getPotentialImmobilizerMoves(sq) {
     // Immobilizer doesn't capture
     return super.getPotentialQueenMoves(sq);
   }
 
-  getPotentialKingMoves(sq)
-  {
-    return this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+  getPotentialKingMoves(sq) {
+    return this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
   // isAttacked() is OK because the immobilizer doesn't take
 
-  isAttackedByPawn([x,y], colors)
-  {
+  isAttackedByPawn([x, y], colors) {
     // Square (x,y) must be surroundable by two enemy pieces,
     // and one of them at least should be a pawn (moving).
-    const dirs = [ [1,0],[0,1] ];
+    const dirs = [
+      [1, 0],
+      [0, 1]
+    ];
     const steps = V.steps[V.ROOK];
-    for (let dir of dirs)
-    {
-      const [i1,j1] = [x-dir[0],y-dir[1]]; //"before"
-      const [i2,j2] = [x+dir[0],y+dir[1]]; //"after"
-      if (V.OnBoard(i1,j1) && V.OnBoard(i2,j2))
-      {
-        if ((this.board[i1][j1]!=V.EMPTY && colors.includes(this.getColor(i1,j1))
-          && this.board[i2][j2]==V.EMPTY)
-            ||
-          (this.board[i2][j2]!=V.EMPTY && colors.includes(this.getColor(i2,j2))
-          && this.board[i1][j1]==V.EMPTY))
-        {
+    for (let dir of dirs) {
+      const [i1, j1] = [x - dir[0], y - dir[1]]; //"before"
+      const [i2, j2] = [x + dir[0], y + dir[1]]; //"after"
+      if (V.OnBoard(i1, j1) && V.OnBoard(i2, j2)) {
+        if (
+          (this.board[i1][j1] != V.EMPTY &&
+            colors.includes(this.getColor(i1, j1)) &&
+            this.board[i2][j2] == V.EMPTY) ||
+          (this.board[i2][j2] != V.EMPTY &&
+            colors.includes(this.getColor(i2, j2)) &&
+            this.board[i1][j1] == V.EMPTY)
+        ) {
           // Search a movable enemy pawn landing on the empty square
-          for (let step of steps)
-          {
-            let [ii,jj] = (this.board[i1][j1]==V.EMPTY ? [i1,j1] : [i2,j2]);
-            let [i3,j3] = [ii+step[0],jj+step[1]];
-            while (V.OnBoard(i3,j3) && this.board[i3][j3]==V.EMPTY)
-            {
+          for (let step of steps) {
+            let [ii, jj] = this.board[i1][j1] == V.EMPTY ? [i1, j1] : [i2, j2];
+            let [i3, j3] = [ii + step[0], jj + step[1]];
+            while (V.OnBoard(i3, j3) && this.board[i3][j3] == V.EMPTY) {
               i3 += step[0];
               j3 += step[1];
             }
-            if (V.OnBoard(i3,j3) && colors.includes(this.getColor(i3,j3))
-              && this.getPiece(i3,j3) == V.PAWN && !this.isImmobilized([i3,j3]))
-            {
+            if (
+              V.OnBoard(i3, j3) &&
+              colors.includes(this.getColor(i3, j3)) &&
+              this.getPiece(i3, j3) == V.PAWN &&
+              !this.isImmobilized([i3, j3])
+            ) {
               return true;
             }
           }
@@ -416,31 +405,30 @@ export const VariantRules = class BaroqueRules extends ChessRules
     return false;
   }
 
-  isAttackedByRook([x,y], colors)
-  {
+  isAttackedByRook([x, y], colors) {
     // King must be on same column or row,
     // and a rook should be able to reach a capturing square
     // colors contains only one element, giving the oppCol and thus king position
-    const sameRow = (x == this.kingPos[colors[0]][0]);
-    const sameColumn = (y == this.kingPos[colors[0]][1]);
-    if (sameRow || sameColumn)
-    {
+    const sameRow = x == this.kingPos[colors[0]][0];
+    const sameColumn = y == this.kingPos[colors[0]][1];
+    if (sameRow || sameColumn) {
       // Look for the enemy rook (maximum 1)
-      for (let i=0; i<V.size.x; i++)
-      {
-        for (let j=0; j<V.size.y; j++)
-        {
-          if (this.board[i][j] != V.EMPTY && colors.includes(this.getColor(i,j))
-            && this.getPiece(i,j) == V.ROOK)
-          {
-            if (this.isImmobilized([i,j]))
-              return false; //because only one rook
+      for (let i = 0; i < V.size.x; i++) {
+        for (let j = 0; j < V.size.y; j++) {
+          if (
+            this.board[i][j] != V.EMPTY &&
+            colors.includes(this.getColor(i, j)) &&
+            this.getPiece(i, j) == V.ROOK
+          ) {
+            if (this.isImmobilized([i, j])) return false; //because only one rook
             // Can it reach a capturing square?
             // Easy but quite suboptimal way (TODO): generate all moves (turn is OK)
-            const moves = this.getPotentialMovesFrom([i,j]);
-            for (let move of moves)
-            {
-              if (sameRow && move.end.y == y || sameColumn && move.end.x == x)
+            const moves = this.getPotentialMovesFrom([i, j]);
+            for (let move of moves) {
+              if (
+                (sameRow && move.end.y == y) ||
+                (sameColumn && move.end.x == x)
+              )
                 return true;
             }
           }
@@ -450,36 +438,31 @@ export const VariantRules = class BaroqueRules extends ChessRules
     return false;
   }
 
-  isAttackedByKnight([x,y], colors)
-  {
+  isAttackedByKnight([x, y], colors) {
     // Square (x,y) must be on same line as a knight,
     // and there must be empty square(s) behind.
     const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-    outerLoop:
-    for (let step of steps)
-    {
-      const [i0,j0] = [x+step[0],y+step[1]];
-      if (V.OnBoard(i0,j0) && this.board[i0][j0] == V.EMPTY)
-      {
+    outerLoop: for (let step of steps) {
+      const [i0, j0] = [x + step[0], y + step[1]];
+      if (V.OnBoard(i0, j0) && this.board[i0][j0] == V.EMPTY) {
         // Try in opposite direction:
-        let [i,j] = [x-step[0],y-step[1]];
-        while (V.OnBoard(i,j))
-        {
-          while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
-          {
+        let [i, j] = [x - step[0], y - step[1]];
+        while (V.OnBoard(i, j)) {
+          while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
             i -= step[0];
             j -= step[1];
           }
-          if (V.OnBoard(i,j))
-          {
-            if (colors.includes(this.getColor(i,j)))
-            {
-              if (this.getPiece(i,j) == V.KNIGHT && !this.isImmobilized([i,j]))
+          if (V.OnBoard(i, j)) {
+            if (colors.includes(this.getColor(i, j))) {
+              if (
+                this.getPiece(i, j) == V.KNIGHT &&
+                !this.isImmobilized([i, j])
+              )
                 return true;
               continue outerLoop;
             }
             // [else] Our color, could be captured *if there was an empty space*
-            if (this.board[i+step[0]][j+step[1]] != V.EMPTY)
+            if (this.board[i + step[0]][j + step[1]] != V.EMPTY)
               continue outerLoop;
             i -= step[0];
             j -= step[1];
@@ -490,39 +473,38 @@ export const VariantRules = class BaroqueRules extends ChessRules
     return false;
   }
 
-  isAttackedByBishop([x,y], colors)
-  {
+  isAttackedByBishop([x, y], colors) {
     // We cheat a little here: since this function is used exclusively for king,
     // it's enough to check the immediate surrounding of the square.
     const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-    for (let step of adjacentSteps)
-    {
-      const [i,j] = [x+step[0],y+step[1]];
-      if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY
-        && colors.includes(this.getColor(i,j)) && this.getPiece(i,j) == V.BISHOP)
-      {
+    for (let step of adjacentSteps) {
+      const [i, j] = [x + step[0], y + step[1]];
+      if (
+        V.OnBoard(i, j) &&
+        this.board[i][j] != V.EMPTY &&
+        colors.includes(this.getColor(i, j)) &&
+        this.getPiece(i, j) == V.BISHOP
+      ) {
         return true; //bishops are never immobilized
       }
     }
     return false;
   }
 
-  isAttackedByQueen([x,y], colors)
-  {
+  isAttackedByQueen([x, y], colors) {
     // Square (x,y) must be adjacent to a queen, and the queen must have
     // some free space in the opposite direction from (x,y)
     const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-    for (let step of adjacentSteps)
-    {
-      const sq2 = [x+2*step[0],y+2*step[1]];
-      if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY)
-      {
-        const sq1 = [x+step[0],y+step[1]];
-        if (this.board[sq1[0]][sq1[1]] != V.EMPTY
-          && colors.includes(this.getColor(sq1[0],sq1[1]))
-          && this.getPiece(sq1[0],sq1[1]) == V.QUEEN
-          && !this.isImmobilized(sq1))
-        {
+    for (let step of adjacentSteps) {
+      const sq2 = [x + 2 * step[0], y + 2 * step[1]];
+      if (V.OnBoard(sq2[0], sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY) {
+        const sq1 = [x + step[0], y + step[1]];
+        if (
+          this.board[sq1[0]][sq1[1]] != V.EMPTY &&
+          colors.includes(this.getColor(sq1[0], sq1[1])) &&
+          this.getPiece(sq1[0], sq1[1]) == V.QUEEN &&
+          !this.isImmobilized(sq1)
+        ) {
           return true;
         }
       }
@@ -530,27 +512,26 @@ export const VariantRules = class BaroqueRules extends ChessRules
     return false;
   }
 
-  static get VALUES()
-  {
+  static get VALUES() {
     return {
-      'p': 1,
-      'r': 2,
-      'n': 5,
-      'b': 3,
-      'q': 3,
-      'm': 5,
-      'k': 1000
+      p: 1,
+      r: 2,
+      n: 5,
+      b: 3,
+      q: 3,
+      m: 5,
+      k: 1000
     };
   }
 
-  static get SEARCH_DEPTH() { return 2; }
+  static get SEARCH_DEPTH() {
+    return 2;
+  }
 
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(8), "b": new Array(8) };
+  static GenRandInitFen() {
+    let pieces = { w: new Array(8), b: new Array(8) };
     // Shuffle pieces on first and last rank
-    for (let c of ["w","b"])
-    {
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(8);
       // Get random squares for every piece, totally freely
 
@@ -583,37 +564,34 @@ export const VariantRules = class BaroqueRules extends ChessRules
       positions.splice(randIndex, 1);
       const immobilizerPos = positions[0];
 
-      pieces[c][bishop1Pos] = 'b';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight1Pos] = 'n';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][queenPos] = 'q';
-      pieces[c][kingPos] = 'k';
-      pieces[c][rookPos] = 'r';
-      pieces[c][immobilizerPos] = 'm';
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][rookPos] = "r";
+      pieces[c][immobilizerPos] = "m";
     }
-    return pieces["b"].join("") +
+    return (
+      pieces["b"].join("") +
       "/pppppppp/8/8/8/8/PPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0";
+      " w 0"
+    );
   }
 
-  getNotation(move)
-  {
+  getNotation(move) {
     const initialSquare = V.CoordsToSquare(move.start);
     const finalSquare = V.CoordsToSquare(move.end);
     let notation = undefined;
-    if (move.appear[0].p == V.PAWN)
-    {
+    if (move.appear[0].p == V.PAWN) {
       // Pawn: generally ambiguous short notation, so we use full description
       notation = "P" + initialSquare + finalSquare;
-    }
-    else if (move.appear[0].p == V.KING)
-      notation = "K" + (move.vanish.length>1 ? "x" : "") + finalSquare;
-    else
-      notation = move.appear[0].p.toUpperCase() + finalSquare;
-    if (move.vanish.length > 1 && move.appear[0].p != V.KING)
-      notation += "X"; //capture mark (not describing what is captured...)
+    } else if (move.appear[0].p == V.KING)
+      notation = "K" + (move.vanish.length > 1 ? "x" : "") + finalSquare;
+    else notation = move.appear[0].p.toUpperCase() + finalSquare;
+    if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X"; //capture mark (not describing what is captured...)
     return notation;
   }
-}
+};
diff --git a/client/src/variants/Berolina.js b/client/src/variants/Berolina.js
index 592b25a6..03b1b3b2 100644
--- a/client/src/variants/Berolina.js
+++ b/client/src/variants/Berolina.js
@@ -1,17 +1,12 @@
 import { ChessRules } from "@/base_rules";
 
-export const VariantRules = class  BerolinaRules extends ChessRules
-{
+export const VariantRules = class BerolinaRules extends ChessRules {
   // En-passant after 2-sq jump
-  getEpSquare(moveOrSquare)
-  {
-    if (!moveOrSquare)
-      return undefined;
-    if (typeof moveOrSquare === "string")
-    {
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
       const square = moveOrSquare;
-      if (square == "-")
-        return undefined;
+      if (square == "-") return undefined;
       // Enemy pawn initial column must be given too:
       let res = [];
       const epParts = square.split(",");
@@ -21,14 +16,12 @@ export const VariantRules = class  BerolinaRules extends ChessRules
     }
     // Argument is a move:
     const move = moveOrSquare;
-    const [sx,ex,sy] = [move.start.x,move.end.x,move.start.y];
-    if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2)
-    {
-      return
-      [
+    const [sx, ex, sy] = [move.start.x, move.end.x, move.start.y];
+    if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) == 2) {
+      return [
         {
-          x: (ex + sx)/2,
-          y: (move.end.y + sy)/2
+          x: (ex + sx) / 2,
+          y: (move.end.y + sy) / 2
         },
         move.end.y
       ];
@@ -37,57 +30,66 @@ export const VariantRules = class  BerolinaRules extends ChessRules
   }
 
   // Special pawns movements
-  getPotentialPawnMoves([x,y])
-  {
+  getPotentialPawnMoves([x, y]) {
     const color = this.turn;
     let moves = [];
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
-    const shiftX = (color == "w" ? -1 : 1);
-    const firstRank = (color == 'w' ? sizeX-1 : 0);
-    const startRank = (color == "w" ? sizeX-2 : 1);
-    const lastRank = (color == "w" ? 0 : sizeX-1);
-    const finalPieces = x + shiftX == lastRank
-      ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
-      : [V.PAWN];
+    const [sizeX, sizeY] = [V.size.x, V.size.y];
+    const shiftX = color == "w" ? -1 : 1;
+    const startRank = color == "w" ? sizeX - 2 : 1;
+    const lastRank = color == "w" ? 0 : sizeX - 1;
+    const finalPieces =
+      x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
 
     // One square diagonally
-    for (let shiftY of [-1,1])
-    {
-      if (this.board[x+shiftX][y+shiftY] == V.EMPTY)
-      {
-        for (let piece of finalPieces)
-        {
-          moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
-            {c:color,p:piece}));
+    for (let shiftY of [-1, 1]) {
+      if (this.board[x + shiftX][y + shiftY] == V.EMPTY) {
+        for (let piece of finalPieces) {
+          moves.push(
+            this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+              c: color,
+              p: piece
+            })
+          );
         }
-        if (x == startRank && y+2*shiftY>=0 && y+2*shiftY<sizeY
-          && this.board[x+2*shiftX][y+2*shiftY] == V.EMPTY)
-        {
+        if (
+          x == startRank &&
+          y + 2 * shiftY >= 0 &&
+          y + 2 * shiftY < sizeY &&
+          this.board[x + 2 * shiftX][y + 2 * shiftY] == V.EMPTY
+        ) {
           // Two squares jump
-          moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY]));
+          moves.push(
+            this.getBasicMove([x, y], [x + 2 * shiftX, y + 2 * shiftY])
+          );
         }
       }
     }
     // Capture
-    if (this.board[x+shiftX][y] != V.EMPTY
-      && this.canTake([x,y], [x+shiftX,y]))
-    {
+    if (
+      this.board[x + shiftX][y] != V.EMPTY &&
+      this.canTake([x, y], [x + shiftX, y])
+    ) {
       for (let piece of finalPieces)
-        moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
+        moves.push(
+          this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+        );
     }
 
     // En passant
     const Lep = this.epSquares.length;
-    const epSquare = this.epSquares[Lep-1]; //always at least one element
-    if (!!epSquare && epSquare[0].x == x+shiftX && epSquare[0].y == y
-      && Math.abs(epSquare[1] - y) == 1)
-    {
-      let enpassantMove = this.getBasicMove([x,y], [x+shiftX,y]);
+    const epSquare = this.epSquares[Lep - 1]; //always at least one element
+    if (
+      !!epSquare &&
+      epSquare[0].x == x + shiftX &&
+      epSquare[0].y == y &&
+      Math.abs(epSquare[1] - y) == 1
+    ) {
+      let enpassantMove = this.getBasicMove([x, y], [x + shiftX, y]);
       enpassantMove.vanish.push({
         x: x,
         y: epSquare[1],
-        p: 'p',
-        c: this.getColor(x,epSquare[1])
+        p: "p",
+        c: this.getColor(x, epSquare[1])
       });
       moves.push(enpassantMove);
     }
@@ -95,16 +97,14 @@ export const VariantRules = class  BerolinaRules extends ChessRules
     return moves;
   }
 
-  isAttackedByPawn([x,y], colors)
-  {
-    for (let c of colors)
-    {
-      let pawnShift = (c=="w" ? 1 : -1);
-      if (x+pawnShift>=0 && x+pawnShift<V.size.x)
-      {
-        if (this.getPiece(x+pawnShift,y)==V.PAWN
-          && this.getColor(x+pawnShift,y)==c)
-        {
+  isAttackedByPawn([x, y], colors) {
+    for (let c of colors) {
+      let pawnShift = c == "w" ? 1 : -1;
+      if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+        if (
+          this.getPiece(x + pawnShift, y) == V.PAWN &&
+          this.getColor(x + pawnShift, y) == c
+        ) {
           return true;
         }
       }
@@ -112,26 +112,25 @@ export const VariantRules = class  BerolinaRules extends ChessRules
     return false;
   }
 
-  getNotation(move)
-  {
+  getNotation(move) {
     const piece = this.getPiece(move.start.x, move.start.y);
-    if (piece == V.PAWN)
-    {
+    if (piece == V.PAWN) {
       // Pawn move
       const finalSquare = V.CoordsToSquare(move.end);
       let notation = "";
-      if (move.vanish.length == 2) //capture
+      if (move.vanish.length == 2)
+        //capture
         notation = "Px" + finalSquare;
-      else
-      {
+      else {
         // No capture: indicate the initial square for potential ambiguity
         const startSquare = V.CoordsToSquare(move.start);
         notation = startSquare + finalSquare;
       }
-      if (move.appear[0].p != V.PAWN) //promotion
+      if (move.appear[0].p != V.PAWN)
+        //promotion
         notation += "=" + move.appear[0].p.toUpperCase();
       return notation;
     }
     return super.getNotation(move); //all other pieces are orthodox
   }
-}
+};
diff --git a/client/src/variants/Checkered.js b/client/src/variants/Checkered.js
index afbf21e2..0e4f0a0d 100644
--- a/client/src/variants/Checkered.js
+++ b/client/src/variants/Checkered.js
@@ -1,159 +1,144 @@
 import { ChessRules } from "@/base_rules";
 
-export const VariantRules = class CheckeredRules extends ChessRules
-{
-  static getPpath(b)
-  {
-    return b[0]=='c' ? "Checkered/"+b : b;
+export const VariantRules = class CheckeredRules extends ChessRules {
+  static getPpath(b) {
+    return b[0] == "c" ? "Checkered/" + b : b;
   }
 
-  static board2fen(b)
-  {
+  static board2fen(b) {
     const checkered_codes = {
-      'p': 's',
-      'q': 't',
-      'r': 'u',
-      'b': 'c',
-      'n': 'o',
+      p: "s",
+      q: "t",
+      r: "u",
+      b: "c",
+      n: "o"
     };
-    if (b[0]=="c")
-      return checkered_codes[b[1]];
+    if (b[0] == "c") return checkered_codes[b[1]];
     return ChessRules.board2fen(b);
   }
 
-  static fen2board(f)
-  {
+  static fen2board(f) {
     // Tolerate upper-case versions of checkered pieces (why not?)
     const checkered_pieces = {
-      's': 'p',
-      'S': 'p',
-      't': 'q',
-      'T': 'q',
-      'u': 'r',
-      'U': 'r',
-      'c': 'b',
-      'C': 'b',
-      'o': 'n',
-      'O': 'n',
+      s: "p",
+      S: "p",
+      t: "q",
+      T: "q",
+      u: "r",
+      U: "r",
+      c: "b",
+      C: "b",
+      o: "n",
+      O: "n"
     };
     if (Object.keys(checkered_pieces).includes(f))
-      return 'c'+checkered_pieces[f];
+      return "c" + checkered_pieces[f];
     return ChessRules.fen2board(f);
   }
 
-  static get PIECES()
-  {
-    return ChessRules.PIECES.concat(['s','t','u','c','o']);
+  static get PIECES() {
+    return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]);
   }
 
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Local stack of non-capturing checkered moves:
     this.cmoves = [];
     const cmove = fen.split(" ")[5];
-    if (cmove == "-")
-      this.cmoves.push(null);
-    else
-    {
+    if (cmove == "-") this.cmoves.push(null);
+    else {
       this.cmoves.push({
-        start: ChessRules.SquareToCoords(cmove.substr(0,2)),
-        end: ChessRules.SquareToCoords(cmove.substr(2)),
+        start: ChessRules.SquareToCoords(cmove.substr(0, 2)),
+        end: ChessRules.SquareToCoords(cmove.substr(2))
       });
     }
   }
 
-  static IsGoodFen(fen)
-  {
-    if (!ChessRules.IsGoodFen(fen))
-      return false;
+  static IsGoodFen(fen) {
+    if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParts = fen.split(" ");
-    if (fenParts.length != 6)
-      return false;
+    if (fenParts.length != 6) return false;
     if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
       return false;
     return true;
   }
 
-  static IsGoodFlags(flags)
-  {
+  static IsGoodFlags(flags) {
     // 4 for castle + 16 for pawns
     return !!flags.match(/^[01]{20,20}$/);
   }
 
-  setFlags(fenflags)
-  {
+  setFlags(fenflags) {
     super.setFlags(fenflags); //castleFlags
-    this.pawnFlags =
-    {
-      "w": [...Array(8).fill(true)], //pawns can move 2 squares?
-      "b": [...Array(8).fill(true)],
+    this.pawnFlags = {
+      w: [...Array(8).fill(true)], //pawns can move 2 squares?
+      b: [...Array(8).fill(true)]
     };
-    if (!fenflags)
-      return;
+    if (!fenflags) return;
     const flags = fenflags.substr(4); //skip first 4 digits, for castle
-    for (let c of ['w','b'])
-    {
-      for (let i=0; i<8; i++)
-        this.pawnFlags[c][i] = (flags.charAt((c=='w'?0:8)+i) == '1');
+    for (let c of ["w", "b"]) {
+      for (let i = 0; i < 8; i++)
+        this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1";
     }
   }
 
-  aggregateFlags()
-  {
+  aggregateFlags() {
     return [this.castleFlags, this.pawnFlags];
   }
 
-  disaggregateFlags(flags)
-  {
+  disaggregateFlags(flags) {
     this.castleFlags = flags[0];
     this.pawnFlags = flags[1];
   }
 
-  getCmove(move)
-  {
-    if (move.appear[0].c == 'c' && move.vanish.length == 1)
-      return {start: move.start, end: move.end};
+  getCmove(move) {
+    if (move.appear[0].c == "c" && move.vanish.length == 1)
+      return { start: move.start, end: move.end };
     return null;
   }
 
-  canTake([x1,y1], [x2,y2])
-  {
-    const color1 = this.getColor(x1,y1);
-    const color2 = this.getColor(x2,y2);
+  canTake([x1, y1], [x2, y2]) {
+    const color1 = this.getColor(x1, y1);
+    const color2 = this.getColor(x2, y2);
     // Checkered aren't captured
-    return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn);
+    return (
+      color1 != color2 &&
+      color2 != "c" &&
+      (color1 != "c" || color2 != this.turn)
+    );
   }
 
   // Post-processing: apply "checkerization" of standard moves
-  getPotentialMovesFrom([x,y])
-  {
-    let standardMoves = super.getPotentialMovesFrom([x,y]);
+  getPotentialMovesFrom([x, y]) {
+    let standardMoves = super.getPotentialMovesFrom([x, y]);
     const lastRank = this.turn == "w" ? 0 : 7;
-    if (this.getPiece(x,y) == V.KING)
-      return standardMoves; //king has to be treated differently (for castles)
+    if (this.getPiece(x, y) == V.KING) return standardMoves; //king has to be treated differently (for castles)
     let moves = [];
     standardMoves.forEach(m => {
-      if (m.vanish[0].p == V.PAWN)
-      {
-        if (Math.abs(m.end.x-m.start.x)==2 && !this.pawnFlags[this.turn][m.start.y])
+      if (m.vanish[0].p == V.PAWN) {
+        if (
+          Math.abs(m.end.x - m.start.x) == 2 &&
+          !this.pawnFlags[this.turn][m.start.y]
+        )
           return; //skip forbidden 2-squares jumps
-        if (this.board[m.end.x][m.end.y] == V.EMPTY && m.vanish.length==2
-          && this.getColor(m.start.x,m.start.y) == 'c')
-        {
+        if (
+          this.board[m.end.x][m.end.y] == V.EMPTY &&
+          m.vanish.length == 2 &&
+          this.getColor(m.start.x, m.start.y) == "c"
+        ) {
           return; //checkered pawns cannot take en-passant
         }
       }
-      if (m.vanish.length == 1)
-        moves.push(m); //no capture
-      else
-      {
+      if (m.vanish.length == 1) moves.push(m);
+      //no capture
+      else {
         // A capture occured (m.vanish.length == 2)
         m.appear[0].c = "c";
         moves.push(m);
-        if (m.appear[0].p != m.vanish[1].p //avoid promotions (already treated):
-          && (m.vanish[0].p != V.PAWN || m.end.x != lastRank))
-        {
+        if (
+          m.appear[0].p != m.vanish[1].p && //avoid promotions (already treated):
+          (m.vanish[0].p != V.PAWN || m.end.x != lastRank)
+        ) {
           // Add transformation into captured piece
           let m2 = JSON.parse(JSON.stringify(m));
           m2.appear[0].p = m.vanish[1].p;
@@ -164,29 +149,30 @@ export const VariantRules = class CheckeredRules extends ChessRules
     return moves;
   }
 
-  canIplay(side, [x,y])
-  {
-    return (side == this.turn && [side,'c'].includes(this.getColor(x,y)));
+  canIplay(side, [x, y]) {
+    return side == this.turn && [side, "c"].includes(this.getColor(x, y));
   }
 
   // Does m2 un-do m1 ? (to disallow undoing checkered moves)
-  oppositeMoves(m1, m2)
-  {
-    return (!!m1 && m2.appear[0].c == 'c'
-      && m2.appear.length == 1 && m2.vanish.length == 1
-      && m1.start.x == m2.end.x && m1.end.x == m2.start.x
-      && m1.start.y == m2.end.y && m1.end.y == m2.start.y);
+  oppositeMoves(m1, m2) {
+    return (
+      !!m1 &&
+      m2.appear[0].c == "c" &&
+      m2.appear.length == 1 &&
+      m2.vanish.length == 1 &&
+      m1.start.x == m2.end.x &&
+      m1.end.x == m2.start.x &&
+      m1.start.y == m2.end.y &&
+      m1.end.y == m2.start.y
+    );
   }
 
-  filterValid(moves)
-  {
-    if (moves.length == 0)
-      return [];
+  filterValid(moves) {
+    if (moves.length == 0) return [];
     const color = this.turn;
     return moves.filter(m => {
       const L = this.cmoves.length; //at least 1: init from FEN
-      if (this.oppositeMoves(this.cmoves[L-1], m))
-        return false;
+      if (this.oppositeMoves(this.cmoves[L - 1], m)) return false;
       this.play(m);
       const res = !this.underCheck(color);
       this.undo(m);
@@ -194,19 +180,18 @@ export const VariantRules = class CheckeredRules extends ChessRules
     });
   }
 
-  isAttackedByPawn([x,y], colors)
-  {
-    for (let c of colors)
-    {
-      const color = (c=="c" ? this.turn : c);
-      let pawnShift = (color=="w" ? 1 : -1);
-      if (x+pawnShift>=0 && x+pawnShift<8)
-      {
-        for (let i of [-1,1])
-        {
-          if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==V.PAWN
-            && this.getColor(x+pawnShift,y+i)==c)
-          {
+  isAttackedByPawn([x, y], colors) {
+    for (let c of colors) {
+      const color = c == "c" ? this.turn : c;
+      let pawnShift = color == "w" ? 1 : -1;
+      if (x + pawnShift >= 0 && x + pawnShift < 8) {
+        for (let i of [-1, 1]) {
+          if (
+            y + i >= 0 &&
+            y + i < 8 &&
+            this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+            this.getColor(x + pawnShift, y + i) == c
+          ) {
             return true;
           }
         }
@@ -215,17 +200,17 @@ export const VariantRules = class CheckeredRules extends ChessRules
     return false;
   }
 
-  underCheck(color)
-  {
-    return this.isAttacked(this.kingPos[color], [V.GetOppCol(color),'c']);
+  underCheck(color) {
+    return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]);
   }
 
-  getCheckSquares(color)
-  {
+  getCheckSquares(color) {
     // Artifically change turn, for checkered pawns
     this.turn = V.GetOppCol(color);
-    const kingAttacked = this.isAttacked(
-      this.kingPos[color], [V.GetOppCol(color),'c']);
+    const kingAttacked = this.isAttacked(this.kingPos[color], [
+      V.GetOppCol(color),
+      "c"
+    ]);
     let res = kingAttacked
       ? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate!
       : [];
@@ -233,139 +218,125 @@ export const VariantRules = class CheckeredRules extends ChessRules
     return res;
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
     // Does this move turn off a 2-squares pawn flag?
-    const secondRank = [1,6];
+    const secondRank = [1, 6];
     if (secondRank.includes(move.start.x) && move.vanish[0].p == V.PAWN)
-      this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false;
+      this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false;
   }
 
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
 
     const color = this.turn;
     // Artifically change turn, for checkered pawns
     this.turn = V.GetOppCol(this.turn);
-    const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color),'c'])
-      ? (color == "w" ? "0-1" : "1-0")
+    const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"])
+      ? color == "w"
+        ? "0-1"
+        : "1-0"
       : "1/2";
     this.turn = V.GetOppCol(this.turn);
     return res;
   }
 
-  evalPosition()
-  {
+  evalPosition() {
     let evaluation = 0;
     //Just count material for now, considering checkered neutral (...)
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY)
-        {
-          const sqColor = this.getColor(i,j);
-          const sign = sqColor == "w" ? 1 : (sqColor=="b" ? -1 : 0);
-          evaluation += sign * V.VALUES[this.getPiece(i,j)];
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY) {
+          const sqColor = this.getColor(i, j);
+          const sign = sqColor == "w" ? 1 : sqColor == "b" ? -1 : 0;
+          evaluation += sign * V.VALUES[this.getPiece(i, j)];
         }
       }
     }
     return evaluation;
   }
 
-  static GenRandInitFen()
-  {
+  static GenRandInitFen() {
     const randFen = ChessRules.GenRandInitFen();
     // Add 16 pawns flags + empty cmove:
     return randFen.replace(" w 0 1111", " w 0 11111111111111111111 -");
   }
 
-  static ParseFen(fen)
-  {
-    const fenParsed = ChessRules.ParseFen(fen);
-    return Object.assign({},
-      ChessRules.ParseFen(fen),
-      {cmove: fen.split(" ")[5]});
+  static ParseFen(fen) {
+    return Object.assign({}, ChessRules.ParseFen(fen), {
+      cmove: fen.split(" ")[5]
+    });
   }
 
-  getFen()
-  {
+  getFen() {
     const L = this.cmoves.length;
-    const cmoveFen = (!this.cmoves[L-1]
+    const cmoveFen = !this.cmoves[L - 1]
       ? "-"
-      : ChessRules.CoordsToSquare(this.cmoves[L-1].start)
-        + ChessRules.CoordsToSquare(this.cmoves[L-1].end));
+      : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) +
+        ChessRules.CoordsToSquare(this.cmoves[L - 1].end);
     return super.getFen() + " " + cmoveFen;
   }
 
-  getFlagsFen()
-  {
+  getFlagsFen() {
     let fen = super.getFlagsFen();
     // Add pawns flags
-    for (let c of ['w','b'])
-    {
-      for (let i=0; i<8; i++)
-        fen += this.pawnFlags[c][i] ? '1' : '0';
+    for (let c of ["w", "b"]) {
+      for (let i = 0; i < 8; i++) fen += this.pawnFlags[c][i] ? "1" : "0";
     }
     return fen;
   }
 
   // TODO (design): this cmove update here or in (un)updateVariables ?
-  play(move)
-  {
-    this.cmoves.push( this.getCmove(move) );
+  play(move) {
+    this.cmoves.push(this.getCmove(move));
     super.play(move);
   }
 
-  undo(move)
-  {
+  undo(move) {
     this.cmoves.pop();
     super.undo(move);
   }
 
-  getNotation(move)
-  {
-    if (move.appear.length == 2)
-    {
+  getNotation(move) {
+    if (move.appear.length == 2) {
       // Castle
-      if (move.end.y < move.start.y)
-        return "0-0-0";
-      else
-        return "0-0";
+      if (move.end.y < move.start.y) return "0-0-0";
+      return "0-0";
     }
 
     // Translate final square
     const finalSquare = V.CoordsToSquare(move.end);
 
     const piece = this.getPiece(move.start.x, move.start.y);
-    if (piece == V.PAWN)
-    {
+    if (piece == V.PAWN) {
       // Pawn move
       let notation = "";
-      if (move.vanish.length > 1)
-      {
+      if (move.vanish.length > 1) {
         // Capture
         const startColumn = V.CoordToColumn(move.start.y);
-        notation = startColumn + "x" + finalSquare +
-          "=" + move.appear[0].p.toUpperCase();
-      }
-      else //no capture
-      {
+        notation =
+          startColumn +
+          "x" +
+          finalSquare +
+          "=" +
+          move.appear[0].p.toUpperCase();
+      } //no capture
+      else {
         notation = finalSquare;
-        if (move.appear.length > 0 && piece != move.appear[0].p) //promotion
+        if (move.appear.length > 0 && piece != move.appear[0].p)
+          //promotion
           notation += "=" + move.appear[0].p.toUpperCase();
       }
       return notation;
     }
-
-    else
-    {
-      // Piece movement
-      return piece.toUpperCase() + (move.vanish.length > 1 ? "x" : "") + finalSquare
-        + (move.vanish.length > 1 ? "=" + move.appear[0].p.toUpperCase() : "");
-    }
+    // Piece movement
+    return (
+      piece.toUpperCase() +
+      (move.vanish.length > 1 ? "x" : "") +
+      finalSquare +
+      (move.vanish.length > 1 ? "=" + move.appear[0].p.toUpperCase() : "")
+    );
   }
-}
+};
diff --git a/client/src/variants/Chess960.js b/client/src/variants/Chess960.js
index 74f4d1d5..7e10e2a5 100644
--- a/client/src/variants/Chess960.js
+++ b/client/src/variants/Chess960.js
@@ -1,5 +1,4 @@
 import { ChessRules } from "@/base_rules";
-export const VariantRules = class Chess960Rules extends ChessRules
-{
+export const VariantRules = class Chess960Rules extends ChessRules {
   // Standard rules
-}
+};
diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js
index 82997274..89b6f830 100644
--- a/client/src/variants/Crazyhouse.js
+++ b/client/src/variants/Crazyhouse.js
@@ -1,24 +1,18 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
 
-export const VariantRules = class CrazyhouseRules extends ChessRules
-{
-  static IsGoodFen(fen)
-  {
-    if (!ChessRules.IsGoodFen(fen))
-      return false;
+export const VariantRules = class CrazyhouseRules extends ChessRules {
+  static IsGoodFen(fen) {
+    if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
     // 5) Check reserves
     if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{10,10}$/))
       return false;
     // 6) Check promoted array
-    if (!fenParsed.promoted)
-      return false;
-    if (fenParsed.promoted == "-")
-      return true; //no promoted piece on board
+    if (!fenParsed.promoted) return false;
+    if (fenParsed.promoted == "-") return true; //no promoted piece on board
     const squares = fenParsed.promoted.split(",");
-    for (let square of squares)
-    {
+    for (let square of squares) {
       const c = V.SquareToCoords(square);
       if (c.y < 0 || c.y > V.size.y || isNaN(c.x) || c.x < 0 || c.x > V.size.x)
         return false;
@@ -26,132 +20,108 @@ export const VariantRules = class CrazyhouseRules extends ChessRules
     return true;
   }
 
-  static ParseFen(fen)
-  {
+  static ParseFen(fen) {
     const fenParts = fen.split(" ");
-    return Object.assign(
-      ChessRules.ParseFen(fen),
-      {
-        reserve: fenParts[5],
-        promoted: fenParts[6],
-      }
-    );
+    return Object.assign(ChessRules.ParseFen(fen), {
+      reserve: fenParts[5],
+      promoted: fenParts[6]
+    });
   }
 
-  static GenRandInitFen()
-  {
+  static GenRandInitFen() {
     return ChessRules.GenRandInitFen() + " 0000000000 -";
   }
 
-  getFen()
-  {
-    return super.getFen() + " " + this.getReserveFen() + " " + this.getPromotedFen();
+  getFen() {
+    return (
+      super.getFen() + " " + this.getReserveFen() + " " + this.getPromotedFen()
+    );
   }
 
-  getReserveFen()
-  {
+  getReserveFen() {
     let counts = new Array(10);
-    for (let i=0; i<V.PIECES.length-1; i++) //-1: no king reserve
-    {
+    for (
+      let i = 0;
+      i < V.PIECES.length - 1;
+      i++ //-1: no king reserve
+    ) {
       counts[i] = this.reserve["w"][V.PIECES[i]];
-      counts[5+i] = this.reserve["b"][V.PIECES[i]];
+      counts[5 + i] = this.reserve["b"][V.PIECES[i]];
     }
     return counts.join("");
   }
 
-  getPromotedFen()
-  {
+  getPromotedFen() {
     let res = "";
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.promoted[i][j])
-          res += V.CoordsToSquare({x:i,y:j});
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.promoted[i][j]) res += V.CoordsToSquare({ x: i, y: j });
       }
     }
-    if (res.length > 0)
-      res = res.slice(0,-1); //remove last comma
-    else
-      res = "-";
+    if (res.length > 0) res = res.slice(0, -1);
+    //remove last comma
+    else res = "-";
     return res;
   }
 
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const fenParsed = V.ParseFen(fen);
     // Also init reserves (used by the interface to show landable pieces)
-    this.reserve =
-    {
-      "w":
-      {
+    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.QUEEN]: parseInt(fenParsed.reserve[4])
       },
-      "b":
-      {
+      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.QUEEN]: parseInt(fenParsed.reserve[9])
       }
     };
     this.promoted = ArrayFun.init(V.size.x, V.size.y, false);
-    if (fenParsed.promoted != "-")
-    {
-      for (let square of fenParsed.promoted.split(","))
-      {
-        const [x,y] = V.SquareToCoords(square);
-        promoted[x][y] = true;
+    if (fenParsed.promoted != "-") {
+      for (let square of fenParsed.promoted.split(",")) {
+        const [x, y] = V.SquareToCoords(square);
+        this.promoted[x][y] = true;
       }
     }
   }
 
-  getColor(i,j)
-  {
-    if (i >= V.size.x)
-      return (i==V.size.x ? "w" : "b");
+  getColor(i, j) {
+    if (i >= V.size.x) return i == V.size.x ? "w" : "b";
     return this.board[i][j].charAt(0);
   }
 
-  getPiece(i,j)
-  {
-    if (i >= V.size.x)
-      return V.RESERVE_PIECES[j];
+  getPiece(i, j) {
+    if (i >= V.size.x) return V.RESERVE_PIECES[j];
     return this.board[i][j].charAt(1);
   }
 
   // Used by the interface:
-  getReservePpath(color, index)
-  {
+  getReservePpath(color, index) {
     return color + V.RESERVE_PIECES[index];
   }
 
   // Ordering on reserve pieces
-  static get RESERVE_PIECES()
-  {
-    return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+  static get RESERVE_PIECES() {
+    return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
   }
 
-  getReserveMoves([x,y])
-  {
+  getReserveMoves([x, y]) {
     const color = this.turn;
     const p = V.RESERVE_PIECES[y];
-    if (this.reserve[color][p] == 0)
-      return [];
+    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++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] == V.EMPTY)
-        {
+    const pawnShift = p == V.PAWN ? 1 : 0;
+    for (let i = pawnShift; i < V.size.x - pawnShift; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] == V.EMPTY) {
           let mv = new Move({
             appear: [
               new PiPo({
@@ -162,8 +132,8 @@ export const VariantRules = class CrazyhouseRules extends ChessRules
               })
             ],
             vanish: [],
-            start: {x:x, y:y}, //a bit artificial...
-            end: {x:i, y:j}
+            start: { x: x, y: y }, //a bit artificial...
+            end: { x: i, y: j }
           });
           moves.push(mv);
         }
@@ -172,94 +142,79 @@ export const VariantRules = class CrazyhouseRules extends ChessRules
     return moves;
   }
 
-  getPotentialMovesFrom([x,y])
-  {
-    if (x >= V.size.x)
-    {
+  getPotentialMovesFrom([x, y]) {
+    if (x >= V.size.x) {
       // Reserves, outside of board: x == sizeX(+1)
-      return this.getReserveMoves([x,y]);
+      return this.getReserveMoves([x, y]);
     }
     // Standard moves
-    return super.getPotentialMovesFrom([x,y]);
+    return super.getPotentialMovesFrom([x, y]);
   }
 
-  getAllValidMoves()
-  {
+  getAllValidMoves() {
     let moves = super.getAllValidMoves();
     const color = this.turn;
-    for (let i=0; i<V.RESERVE_PIECES.length; i++)
-      moves = moves.concat(this.getReserveMoves([V.size.x+(color=="w"?0:1),i]));
+    for (let i = 0; i < V.RESERVE_PIECES.length; i++)
+      moves = moves.concat(
+        this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
+      );
     return this.filterValid(moves);
   }
 
-  atLeastOneMove()
-  {
-    if (!super.atLeastOneMove())
-    {
-      const color = this.turn;
+  atLeastOneMove() {
+    if (!super.atLeastOneMove()) {
       // Search one reserve move
-      for (let i=0; i<V.RESERVE_PIECES.length; i++)
-      {
+      for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
         let moves = this.filterValid(
-          this.getReserveMoves([V.size.x+(this.turn=="w"?0:1), i]) );
-        if (moves.length > 0)
-          return true;
+          this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
+        );
+        if (moves.length > 0) return true;
       }
       return false;
     }
     return true;
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
-    if (move.vanish.length == 2 && move.appear.length == 2)
-      return; //skip castle
+    if (move.vanish.length == 2 && move.appear.length == 2) return; //skip castle
     const color = move.appear[0].c;
-    if (move.vanish.length == 0)
-    {
+    if (move.vanish.length == 0) {
       this.reserve[color][move.appear[0].p]--;
       return;
     }
     move.movePromoted = this.promoted[move.start.x][move.start.y];
-    move.capturePromoted = this.promoted[move.end.x][move.end.y]
+    move.capturePromoted = this.promoted[move.end.x][move.end.y];
     this.promoted[move.start.x][move.start.y] = false;
-    this.promoted[move.end.x][move.end.y] = move.movePromoted
-      || (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN);
-    if (move.capturePromoted)
-      this.reserve[color][V.PAWN]++;
-    else if (move.vanish.length == 2)
-      this.reserve[color][move.vanish[1].p]++;
+    this.promoted[move.end.x][move.end.y] =
+      move.movePromoted ||
+      (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN);
+    if (move.capturePromoted) this.reserve[color][V.PAWN]++;
+    else if (move.vanish.length == 2) this.reserve[color][move.vanish[1].p]++;
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
-    if (move.vanish.length == 2 && move.appear.length == 2)
-      return;
+    if (move.vanish.length == 2 && move.appear.length == 2) return;
     const color = this.turn;
-    if (move.vanish.length == 0)
-    {
+    if (move.vanish.length == 0) {
       this.reserve[color][move.appear[0].p]++;
       return;
     }
-    if (move.movePromoted)
-      this.promoted[move.start.x][move.start.y] = true;
+    if (move.movePromoted) this.promoted[move.start.x][move.start.y] = true;
     this.promoted[move.end.x][move.end.y] = move.capturePromoted;
-    if (move.capturePromoted)
-      this.reserve[color][V.PAWN]--;
-    else if (move.vanish.length == 2)
-      this.reserve[color][move.vanish[1].p]--;
+    if (move.capturePromoted) this.reserve[color][V.PAWN]--;
+    else if (move.vanish.length == 2) this.reserve[color][move.vanish[1].p]--;
   }
 
-  static get SEARCH_DEPTH() { return 2; } //high branching factor
+  static get SEARCH_DEPTH() {
+    return 2;
+  } //high branching factor
 
-  evalPosition()
-  {
+  evalPosition() {
     let evaluation = super.evalPosition();
     // Add reserves:
-    for (let i=0; i<V.RESERVE_PIECES.length; i++)
-    {
+    for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
       const p = V.RESERVE_PIECES[i];
       evaluation += this.reserve["w"][p] * V.VALUES[p];
       evaluation -= this.reserve["b"][p] * V.VALUES[p];
@@ -267,20 +222,16 @@ export const VariantRules = class CrazyhouseRules extends ChessRules
     return evaluation;
   }
 
-  getNotation(move)
-  {
-    if (move.vanish.length > 0)
-      return super.getNotation(move);
+  getNotation(move) {
+    if (move.vanish.length > 0) return super.getNotation(move);
     // Rebirth:
     const piece =
-      (move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "");
+      move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
 
-  getLongNotation(move)
-  {
-    if (move.vanish.length > 0)
-      return super.getLongNotation(move);
+  getLongNotation(move) {
+    if (move.vanish.length > 0) return super.getLongNotation(move);
     return "@" + V.CoordsToSquare(move.end);
   }
-}
+};
diff --git a/client/src/variants/Dark.js b/client/src/variants/Dark.js
index c0bfb09c..358362c7 100644
--- a/client/src/variants/Dark.js
+++ b/client/src/variants/Dark.js
@@ -1,52 +1,45 @@
 import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
-export const VariantRules = class DarkRules extends ChessRules
-{
+export const VariantRules = class DarkRules extends ChessRules {
   // Standard rules, in the shadow
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
+    const [sizeX, sizeY] = [V.size.x, V.size.y];
     this.enlightened = {
-      "w": ArrayFun.init(sizeX,sizeY),
-      "b": ArrayFun.init(sizeX,sizeY)
+      w: ArrayFun.init(sizeX, sizeY),
+      b: ArrayFun.init(sizeX, sizeY)
     };
     // Setup enlightened: squares reachable by each side
     // (TODO: one side would be enough ?)
     this.updateEnlightened();
   }
 
-  updateEnlightened()
-  {
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
+  updateEnlightened() {
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
         this.enlightened["w"][i][j] = false;
         this.enlightened["b"][i][j] = false;
       }
     }
-    const pawnShift = {"w":-1, "b":1};
+    const pawnShift = { w: -1, b: 1 };
     // Initialize with pieces positions (which are seen)
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY)
-        {
-          const color = this.getColor(i,j);
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY) {
+          const color = this.getColor(i, j);
           this.enlightened[color][i][j] = true;
           // Add potential squares visible by "impossible pawn capture"
-          if (this.getPiece(i,j) == V.PAWN)
-          {
-            for (let shiftY of [-1,1])
-            {
-              if (V.OnBoard(i+pawnShift[color],j+shiftY)
-                && this.board[i+pawnShift[color]][j+shiftY] == V.EMPTY)
-              {
-                this.enlightened[color][i+pawnShift[color]][j+shiftY] = true;
+          if (this.getPiece(i, j) == V.PAWN) {
+            for (let shiftY of [-1, 1]) {
+              if (
+                V.OnBoard(i + pawnShift[color], j + shiftY) &&
+                this.board[i + pawnShift[color]][j + shiftY] == V.EMPTY
+              ) {
+                this.enlightened[color][i + pawnShift[color]][
+                  j + shiftY
+                ] = true;
               }
             }
           }
@@ -66,64 +59,53 @@ export const VariantRules = class DarkRules extends ChessRules
   }
 
   // Has to be redefined to avoid an infinite loop
-  getAllValidMoves()
-  {
+  getAllValidMoves() {
     const color = this.turn;
-    const oppCol = V.GetOppCol(color);
     let potentialMoves = [];
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color)
-          Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j]));
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color)
+          Array.prototype.push.apply(
+            potentialMoves,
+            this.getPotentialMovesFrom([i, j])
+          );
       }
     }
     return potentialMoves; //because there are no checks
   }
 
-  atLeastOneMove()
-  {
-    if (this.kingPos[this.turn][0] < 0)
-      return false;
+  atLeastOneMove() {
+    if (this.kingPos[this.turn][0] < 0) return false;
     return true; //TODO: is it right?
   }
 
-  underCheck(color)
-  {
+  underCheck() {
     return false; //there is no check
   }
 
-  getCheckSquares(color)
-  {
+  getCheckSquares() {
     return [];
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
-    if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
-    {
+    if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) {
       // We took opponent king ! (because if castle vanish[1] is a rook)
-      this.kingPos[this.turn] = [-1,-1];
+      this.kingPos[this.turn] = [-1, -1];
     }
 
     // Update lights for both colors:
     this.updateEnlightened();
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
     const oppCol = V.GetOppCol(c);
-    if (this.kingPos[oppCol][0] < 0)
-    {
+    if (this.kingPos[oppCol][0] < 0) {
       // Last move took opponent's king
-      for (let psq of move.vanish)
-      {
-        if (psq.p == 'k')
-        {
+      for (let psq of move.vanish) {
+        if (psq.p == "k") {
           this.kingPos[oppCol] = [psq.x, psq.y];
           break;
         }
@@ -134,96 +116,85 @@ export const VariantRules = class DarkRules extends ChessRules
     this.updateEnlightened();
   }
 
-  getCurrentScore()
-  {
+  getCurrentScore() {
     const color = this.turn;
     const kp = this.kingPos[color];
-    if (kp[0] < 0) //king disappeared
-      return (color == "w" ? "0-1" : "1-0");
-    if (this.atLeastOneMove()) // game not over
+    if (kp[0] < 0)
+      //king disappeared
+      return color == "w" ? "0-1" : "1-0";
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
     return "1/2"; //no moves but kings still there (seems impossible)
   }
 
-  static get THRESHOLD_MATE()
-  {
+  static get THRESHOLD_MATE() {
     return 500; //checkmates evals may be slightly below 1000
   }
 
   // In this special situation, we just look 1 half move ahead
-  getComputerMove()
-  {
+  getComputerMove() {
     const maxeval = V.INFINITY;
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
-    const pawnShift = (color == "w" ? -1 : 1);
+    const pawnShift = color == "w" ? -1 : 1;
 
     // Do not cheat: the current enlightment is all we can see
     const myLight = JSON.parse(JSON.stringify(this.enlightened[color]));
 
     // Can a slider on (i,j) apparently take my king?
     // NOTE: inaccurate because assume yes if some squares are shadowed
-    const sliderTake = ([i,j], piece) => {
+    const sliderTake = ([i, j], piece) => {
       const kp = this.kingPos[color];
       let step = undefined;
-      if (piece == V.BISHOP)
-      {
-        if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j))
-        {
-          step =
-          [
-            (i-kp[0]) / Math.abs(i-kp[0]),
-            (j-kp[1]) / Math.abs(j-kp[1])
+      if (piece == V.BISHOP) {
+        if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j)) {
+          step = [
+            (i - kp[0]) / Math.abs(i - kp[0]),
+            (j - kp[1]) / Math.abs(j - kp[1])
           ];
         }
+      } else if (piece == V.ROOK) {
+        if (kp[0] == i) step = [0, (j - kp[1]) / Math.abs(j - kp[1])];
+        else if (kp[1] == j) step = [(i - kp[0]) / Math.abs(i - kp[0]), 0];
       }
-      else if (piece == V.ROOK)
-      {
-        if (kp[0] == i)
-          step = [0, (j-kp[1]) / Math.abs(j-kp[1])];
-        else if (kp[1] == j)
-          step = [(i-kp[0]) / Math.abs(i-kp[0]), 0];
-      }
-      if (!step)
-        return false;
+      if (!step) return false;
       // Check for obstacles
       let obstacle = false;
       for (
-        let x=kp[0]+step[0], y=kp[1]+step[1];
+        let x = kp[0] + step[0], y = kp[1] + step[1];
         x != i && y != j;
-        x += step[0], y += step[1])
-      {
-        if (myLight[x][y] && this.board[x][y] != V.EMPTY)
-        {
+        x += step[0], y += step[1]
+      ) {
+        if (myLight[x][y] && this.board[x][y] != V.EMPTY) {
           obstacle = true;
           break;
         }
       }
-      if (!obstacle)
-        return true;
+      if (!obstacle) return true;
       return false;
     };
 
     // Do I see something which can take my king ?
     const kingThreats = () => {
       const kp = this.kingPos[color];
-      for (let i=0; i<V.size.x; i++)
-      {
-        for (let j=0; j<V.size.y; j++)
-        {
-          if (myLight[i][j] && this.board[i][j] != V.EMPTY
-            && this.getColor(i,j) != color)
-          {
-            switch (this.getPiece(i,j))
-            {
+      for (let i = 0; i < V.size.x; i++) {
+        for (let j = 0; j < V.size.y; j++) {
+          if (
+            myLight[i][j] &&
+            this.board[i][j] != V.EMPTY &&
+            this.getColor(i, j) != color
+          ) {
+            switch (this.getPiece(i, j)) {
               case V.PAWN:
-                if (kp[0] + pawnShift == i && Math.abs(kp[1]-j) == 1)
+                if (kp[0] + pawnShift == i && Math.abs(kp[1] - j) == 1)
                   return true;
                 break;
               case V.KNIGHT:
-                if ((Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
-                  (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2))
-                {
+                if (
+                  (Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
+                  (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2)
+                ) {
                   return true;
                 }
                 break;
@@ -232,15 +203,13 @@ export const VariantRules = class DarkRules extends ChessRules
                   return true;
                 break;
               case V.BISHOP:
-                if (sliderTake([i,j], V.BISHOP))
-                  return true;
+                if (sliderTake([i, j], V.BISHOP)) return true;
                 break;
               case V.ROOK:
-                if (sliderTake([i,j], V.ROOK))
-                  return true;
+                if (sliderTake([i, j], V.ROOK)) return true;
                 break;
               case V.QUEEN:
-                if (sliderTake([i,j], V.BISHOP) || sliderTake([i,j], V.ROOK))
+                if (sliderTake([i, j], V.BISHOP) || sliderTake([i, j], V.ROOK))
                   return true;
                 break;
             }
@@ -251,37 +220,33 @@ export const VariantRules = class DarkRules extends ChessRules
     };
 
     let moves = this.getAllValidMoves();
-    for (let move of moves)
-    {
+    for (let move of moves) {
       this.play(move);
-      if (this.kingPos[oppCol][0] >= 0 && kingThreats())
-      {
+      if (this.kingPos[oppCol][0] >= 0 && kingThreats()) {
         // We didn't take opponent king, and our king will be captured: bad
         move.eval = -maxeval;
       }
       this.undo(move);
 
-      if (!!move.eval)
-        continue;
+      if (move.eval) continue;
 
       move.eval = 0; //a priori...
 
       // Can I take something ? If yes, do it if it seems good...
-      if (move.vanish.length == 2 && move.vanish[1].c != color) //avoid castle
-      {
+      if (move.vanish.length == 2 && move.vanish[1].c != color) {
+        //avoid castle
         const myPieceVal = V.VALUES[move.appear[0].p];
         const hisPieceVal = V.VALUES[move.vanish[1].p];
-        if (myPieceVal <= hisPieceVal)
-          move.eval = hisPieceVal - myPieceVal + 2; //favor captures
-        else
-        {
+        if (myPieceVal <= hisPieceVal) move.eval = hisPieceVal - myPieceVal + 2;
+        //favor captures
+        else {
           // Taking a pawn with minor piece,
           // or minor piece or pawn with a rook,
           // or anything but a queen with a queen,
           // or anything with a king.
           // ==> Do it at random, although
           //     this is clearly inferior to what a human can deduce...
-          move.eval = (Math.random() < 0.5 ? 1 : -1);
+          move.eval = Math.random() < 0.5 ? 1 : -1;
         }
       }
     }
@@ -289,10 +254,10 @@ export const VariantRules = class DarkRules extends ChessRules
     // TODO: also need to implement the case when an opponent piece (in light)
     // is threatening something - maybe not the king, but e.g. pawn takes rook.
 
-    moves.sort((a,b) => b.eval - a.eval);
+    moves.sort((a, b) => b.eval - a.eval);
     let candidates = [0];
-    for (let j=1; j<moves.length && moves[j].eval == moves[0].eval; j++)
+    for (let j = 1; j < moves.length && moves[j].eval == moves[0].eval; j++)
       candidates.push(j);
     return moves[candidates[randInt(candidates.length)]];
   }
-}
+};
diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js
index 8d860aeb..75db0e11 100644
--- a/client/src/variants/Extinction.js
+++ b/client/src/variants/Extinction.js
@@ -1,17 +1,13 @@
 import { ChessRules } from "@/base_rules";
 
-export const VariantRules = class  ExtinctionRules extends ChessRules
-{
-  setOtherVariables(fen)
-  {
+export const VariantRules = class ExtinctionRules extends ChessRules {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const pos = V.ParseFen(fen).position;
     // NOTE: no need for safety "|| []", because each piece type must be present
     // (otherwise game is already over!)
-    this.material =
-    {
-      "w":
-      {
+    this.material = {
+      w: {
         [V.KING]: pos.match(/K/g).length,
         [V.QUEEN]: pos.match(/Q/g).length,
         [V.ROOK]: pos.match(/R/g).length,
@@ -19,8 +15,7 @@ export const VariantRules = class  ExtinctionRules extends ChessRules
         [V.BISHOP]: pos.match(/B/g).length,
         [V.PAWN]: pos.match(/P/g).length
       },
-      "b":
-      {
+      b: {
         [V.KING]: pos.match(/k/g).length,
         [V.QUEEN]: pos.match(/q/g).length,
         [V.ROOK]: pos.match(/r/g).length,
@@ -31,29 +26,37 @@ export const VariantRules = class  ExtinctionRules extends ChessRules
     };
   }
 
-  getPotentialPawnMoves([x,y])
-  {
-    let moves = super.getPotentialPawnMoves([x,y]);
+  getPotentialPawnMoves([x, y]) {
+    let moves = super.getPotentialPawnMoves([x, y]);
     // Add potential promotions into king
     const color = this.turn;
-    const shift = (color == "w" ? -1 : 1);
-    const lastRank = (color == "w" ? 0 : V.size.x-1);
+    const shift = color == "w" ? -1 : 1;
+    const lastRank = color == "w" ? 0 : V.size.x - 1;
 
-    if (x+shift == lastRank)
-    {
+    if (x + shift == lastRank) {
       // Normal move
-      if (this.board[x+shift][y] == V.EMPTY)
-        moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
+      if (this.board[x + shift][y] == V.EMPTY)
+        moves.push(
+          this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
+        );
       // Captures
-      if (y>0 && this.board[x+shift][y-1] != V.EMPTY
-        && this.canTake([x,y], [x+shift,y-1]))
-      {
-        moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
+      if (
+        y > 0 &&
+        this.board[x + shift][y - 1] != V.EMPTY &&
+        this.canTake([x, y], [x + shift, y - 1])
+      ) {
+        moves.push(
+          this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
+        );
       }
-      if (y<V.size.y-1 && this.board[x+shift][y+1] != V.EMPTY
-        && this.canTake([x,y], [x+shift,y+1]))
-      {
-        moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:V.KING}));
+      if (
+        y < V.size.y - 1 &&
+        this.board[x + shift][y + 1] != V.EMPTY &&
+        this.canTake([x, y], [x + shift, y + 1])
+      ) {
+        moves.push(
+          this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
+        );
       }
     }
 
@@ -61,71 +64,67 @@ export const VariantRules = class  ExtinctionRules extends ChessRules
   }
 
   // TODO: verify this assertion
-  atLeastOneMove()
-  {
+  atLeastOneMove() {
     return true; //always at least one possible move
   }
 
-  underCheck(color)
-  {
+  underCheck() {
     return false; //there is no check
   }
 
-  getCheckSquares(color)
-  {
+  getCheckSquares() {
     return [];
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
     // Treat the promotion case: (not the capture part)
-    if (move.appear[0].p != move.vanish[0].p)
-    {
+    if (move.appear[0].p != move.vanish[0].p) {
       this.material[move.appear[0].c][move.appear[0].p]++;
       this.material[move.appear[0].c][V.PAWN]--;
     }
-    if (move.vanish.length==2 && move.appear.length==1) //capture
+    if (move.vanish.length == 2 && move.appear.length == 1)
+      //capture
       this.material[move.vanish[1].c][move.vanish[1].p]--;
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
-    if (move.appear[0].p != move.vanish[0].p)
-    {
+    if (move.appear[0].p != move.vanish[0].p) {
       this.material[move.appear[0].c][move.appear[0].p]--;
       this.material[move.appear[0].c][V.PAWN]++;
     }
-    if (move.vanish.length==2 && move.appear.length==1)
+    if (move.vanish.length == 2 && move.appear.length == 1)
       this.material[move.vanish[1].c][move.vanish[1].p]++;
   }
 
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over?
-    {
+  getCurrentScore() {
+    if (this.atLeastOneMove()) {
+      // game not over?
       const color = this.turn;
-      if (Object.keys(this.material[color]).some(
-        p => { return this.material[color][p] == 0; }))
-      {
-        return (this.turn == "w" ? "0-1" : "1-0");
+      if (
+        Object.keys(this.material[color]).some(p => {
+          return this.material[color][p] == 0;
+        })
+      ) {
+        return this.turn == "w" ? "0-1" : "1-0";
       }
       return "*";
     }
 
-    return (this.turn == "w" ? "0-1" : "1-0"); //NOTE: currently unreachable...
+    return this.turn == "w" ? "0-1" : "1-0"; //NOTE: currently unreachable...
   }
 
-  evalPosition()
-  {
+  evalPosition() {
     const color = this.turn;
-    if (Object.keys(this.material[color]).some(
-      p => { return this.material[color][p] == 0; }))
-    {
+    if (
+      Object.keys(this.material[color]).some(p => {
+        return this.material[color][p] == 0;
+      })
+    ) {
       // Very negative (resp. positive) if white (reps. black) pieces set is incomplete
-      return (color=="w"?-1:1) * V.INFINITY;
+      return (color == "w" ? -1 : 1) * V.INFINITY;
     }
     return super.evalPosition();
   }
-}
+};
diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js
index a53f59e9..d28f639f 100644
--- a/client/src/variants/Grand.js
+++ b/client/src/variants/Grand.js
@@ -4,17 +4,13 @@ import { randInt } from "@/utils/alea";
 
 // NOTE: initial setup differs from the original; see
 // https://www.chessvariants.com/large.dir/freeling.html
-export const VariantRules = class GrandRules extends ChessRules
-{
-  static getPpath(b)
-  {
-    return ([V.MARSHALL,V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
+export const VariantRules = class GrandRules extends ChessRules {
+  static getPpath(b) {
+    return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
   }
 
-  static IsGoodFen(fen)
-  {
-    if (!ChessRules.IsGoodFen(fen))
-      return false;
+  static IsGoodFen(fen) {
+    if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
     // 5) Check captures
     if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{14,14}$/))
@@ -22,115 +18,99 @@ export const VariantRules = class GrandRules extends ChessRules
     return true;
   }
 
-  static IsGoodEnpassant(enpassant)
-  {
-    if (enpassant != "-")
-    {
+  static IsGoodEnpassant(enpassant) {
+    if (enpassant != "-") {
       const squares = enpassant.split(",");
-      if (squares.length > 2)
-        return false;
-      for (let sq of squares)
-      {
+      if (squares.length > 2) return false;
+      for (let sq of squares) {
         const ep = V.SquareToCoords(sq);
-        if (isNaN(ep.x) || !V.OnBoard(ep))
-          return false;
+        if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
       }
     }
     return true;
   }
 
-  static ParseFen(fen)
-  {
+  static ParseFen(fen) {
     const fenParts = fen.split(" ");
-    return Object.assign(
-      ChessRules.ParseFen(fen),
-      { captured: fenParts[5] }
-    );
+    return Object.assign(ChessRules.ParseFen(fen), { captured: fenParts[5] });
   }
 
-  getFen()
-  {
+  getFen() {
     return super.getFen() + " " + this.getCapturedFen();
   }
 
-  getCapturedFen()
-  {
+  getCapturedFen() {
     let counts = [...Array(14).fill(0)];
     let i = 0;
-    for (let j=0; j<V.PIECES.length; j++)
-    {
-      if (V.PIECES[j] == V.KING) //no king captured
+    for (let j = 0; j < V.PIECES.length; j++) {
+      if (V.PIECES[j] == V.KING)
+        //no king captured
         continue;
       counts[i] = this.captured["w"][V.PIECES[i]];
-      counts[7+i] = this.captured["b"][V.PIECES[i]];
+      counts[7 + i] = this.captured["b"][V.PIECES[i]];
       i++;
     }
     return counts.join("");
   }
 
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const fenParsed = V.ParseFen(fen);
     // Initialize captured pieces' counts from FEN
-    this.captured =
-    {
-      "w":
-      {
+    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.MARSHALL]: parseInt(fenParsed.captured[5]),
-        [V.CARDINAL]: parseInt(fenParsed.captured[6]),
+        [V.CARDINAL]: parseInt(fenParsed.captured[6])
       },
-      "b":
-      {
+      b: {
         [V.PAWN]: parseInt(fenParsed.captured[7]),
         [V.ROOK]: parseInt(fenParsed.captured[8]),
         [V.KNIGHT]: parseInt(fenParsed.captured[9]),
         [V.BISHOP]: parseInt(fenParsed.captured[10]),
         [V.QUEEN]: parseInt(fenParsed.captured[11]),
         [V.MARSHALL]: parseInt(fenParsed.captured[12]),
-        [V.CARDINAL]: parseInt(fenParsed.captured[13]),
+        [V.CARDINAL]: parseInt(fenParsed.captured[13])
       }
     };
   }
 
-  static get size() { return {x:10,y:10}; }
+  static get size() {
+    return { x: 10, y: 10 };
+  }
 
-  static get MARSHALL() { return 'm'; } //rook+knight
-  static get CARDINAL() { return 'c'; } //bishop+knight
+  static get MARSHALL() {
+    return "m";
+  } //rook+knight
+  static get CARDINAL() {
+    return "c";
+  } //bishop+knight
 
-  static get PIECES()
-  {
-    return ChessRules.PIECES.concat([V.MARSHALL,V.CARDINAL]);
+  static get PIECES() {
+    return ChessRules.PIECES.concat([V.MARSHALL, V.CARDINAL]);
   }
 
   // There may be 2 enPassant squares (if pawn jump 3 squares)
-  getEnpassantFen()
-  {
+  getEnpassantFen() {
     const L = this.epSquares.length;
-    if (!this.epSquares[L-1])
-      return "-"; //no en-passant
+    if (!this.epSquares[L - 1]) return "-"; //no en-passant
     let res = "";
-    this.epSquares[L-1].forEach(sq => {
+    this.epSquares[L - 1].forEach(sq => {
       res += V.CoordsToSquare(sq) + ",";
     });
-    return res.slice(0,-1); //remove last comma
+    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")
-    {
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
       const square = moveOrSquare;
-      if (square == "-")
-        return undefined;
+      if (square == "-") return undefined;
       let res = [];
       square.split(",").forEach(sq => {
         res.push(V.SquareToCoords(sq));
@@ -139,18 +119,19 @@ export const VariantRules = class GrandRules extends ChessRules
     }
     // 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 move
-      {
+    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 move
         res.push({
-          x: sx + 2*step,
+          x: sx + 2 * step,
           y: sy
         });
       }
@@ -159,95 +140,94 @@ export const VariantRules = class GrandRules extends ChessRules
     return undefined; //default
   }
 
-  getPotentialMovesFrom([x,y])
-  {
-    switch (this.getPiece(x,y))
-    {
+  getPotentialMovesFrom([x, y]) {
+    switch (this.getPiece(x, y)) {
       case V.MARSHALL:
-        return this.getPotentialMarshallMoves([x,y]);
+        return this.getPotentialMarshallMoves([x, y]);
       case V.CARDINAL:
-        return this.getPotentialCardinalMoves([x,y]);
+        return this.getPotentialCardinalMoves([x, y]);
       default:
-        return super.getPotentialMovesFrom([x,y])
+        return super.getPotentialMovesFrom([x, y]);
     }
   }
 
   // Special pawn rules: promotions to captured friendly pieces,
   // optional on ranks 8-9 and mandatory on rank 10.
-  getPotentialPawnMoves([x,y])
-  {
+  getPotentialPawnMoves([x, y]) {
     const color = this.turn;
     let moves = [];
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
-    const shiftX = (color == "w" ? -1 : 1);
-    const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
-    const lastRanks = (color == "w" ? [0,1,2] : [sizeX-1,sizeX-2,sizeX-3]);
-    const promotionPieces =
-      [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.MARSHALL,V.CARDINAL];
+    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 lastRanks =
+      color == "w" ? [0, 1, 2] : [sizeX - 1, sizeX - 2, sizeX - 3];
+    const promotionPieces = [
+      V.ROOK,
+      V.KNIGHT,
+      V.BISHOP,
+      V.QUEEN,
+      V.MARSHALL,
+      V.CARDINAL
+    ];
 
     // Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank
     let finalPieces = undefined;
-    if (lastRanks.includes(x + shiftX))
-    {
+    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];
-    if (this.board[x+shiftX][y] == V.EMPTY)
-    {
+      if (x + shiftX != lastRanks[0]) finalPieces.push(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 (startRanks.includes(x))
-      {
-        if (this.board[x+2*shiftX][y] == V.EMPTY)
-        {
+        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)
-          {
+          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]));
+            moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
           }
         }
       }
     }
     // Captures
-    for (let shiftY of [-1,1])
-    {
-      if (y + shiftY >= 0 && y + shiftY < sizeY
-        && this.board[x+shiftX][y+shiftY] != V.EMPTY
-        && this.canTake([x,y], [x+shiftX,y+shiftY]))
-      {
-        for (let piece of finalPieces)
-        {
-          moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
-            {c:color,p:piece}));
+    for (let shiftY of [-1, 1]) {
+      if (
+        y + shiftY >= 0 &&
+        y + shiftY < sizeY &&
+        this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+        this.canTake([x, y], [x + shiftX, y + shiftY])
+      ) {
+        for (let piece of finalPieces) {
+          moves.push(
+            this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+              c: color,
+              p: piece
+            })
+          );
         }
       }
     }
 
     // En passant
     const Lep = this.epSquares.length;
-    const epSquare = this.epSquares[Lep-1];
-    if (!!epSquare)
-    {
-      for (let epsq of epSquare)
-      {
+    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)
-        {
-          var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]);
+        if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
+          var 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);
+          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)
+            p: "p",
+            c: this.getColor(px, epsq.y)
           });
           moves.push(enpassantMove);
         }
@@ -259,56 +239,65 @@ export const VariantRules = class GrandRules extends ChessRules
 
   // TODO: different castle?
 
-  getPotentialMarshallMoves(sq)
-  {
+  getPotentialMarshallMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
-      this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
+      this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
+    );
   }
 
-  getPotentialCardinalMoves(sq)
-  {
+  getPotentialCardinalMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
-      this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
+      this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
+    );
   }
 
-  isAttacked(sq, colors)
-  {
-    return super.isAttacked(sq, colors)
-      || this.isAttackedByMarshall(sq, colors)
-      || this.isAttackedByCardinal(sq, colors);
+  isAttacked(sq, colors) {
+    return (
+      super.isAttacked(sq, colors) ||
+      this.isAttackedByMarshall(sq, colors) ||
+      this.isAttackedByCardinal(sq, colors)
+    );
   }
 
-  isAttackedByMarshall(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK])
-      || this.isAttackedBySlideNJump(
-        sq, colors, V.MARSHALL, V.steps[V.KNIGHT], "oneStep");
+  isAttackedByMarshall(sq, colors) {
+    return (
+      this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK]) ||
+      this.isAttackedBySlideNJump(
+        sq,
+        colors,
+        V.MARSHALL,
+        V.steps[V.KNIGHT],
+        "oneStep"
+      )
+    );
   }
 
-  isAttackedByCardinal(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP])
-      || this.isAttackedBySlideNJump(
-        sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep");
+  isAttackedByCardinal(sq, colors) {
+    return (
+      this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP]) ||
+      this.isAttackedBySlideNJump(
+        sq,
+        colors,
+        V.CARDINAL,
+        V.steps[V.KNIGHT],
+        "oneStep"
+      )
+    );
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
-    if (move.vanish.length == 2 && move.appear.length == 1)
-    {
+    if (move.vanish.length == 2 && move.appear.length == 1) {
       // Capture: update this.captured
       this.captured[move.vanish[1].c][move.vanish[1].p]++;
     }
-    if (move.vanish[0].p != move.appear[0].p)
-    {
+    if (move.vanish[0].p != move.appear[0].p) {
       // Promotion: update this.captured
       this.captured[move.vanish[0].c][move.appear[0].p]--;
     }
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
     if (move.vanish.length == 2 && move.appear.length == 1)
       this.captured[move.vanish[1].c][move.vanish[1].p]--;
@@ -316,23 +305,22 @@ export const VariantRules = class GrandRules extends ChessRules
       this.captured[move.vanish[0].c][move.appear[0].p]++;
   }
 
-  static get VALUES()
-  {
+  static get VALUES() {
     return Object.assign(
       ChessRules.VALUES,
-      {'c': 5, 'm': 7} //experimental
+      { c: 5, m: 7 } //experimental
     );
   }
 
-  static get SEARCH_DEPTH() { return 2; }
+  static get SEARCH_DEPTH() {
+    return 2;
+  }
 
   // TODO: this function could be generalized and shared better (how ?!...)
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(10), "b": new Array(10) };
+  static GenRandInitFen() {
+    let pieces = { w: new Array(10), b: new Array(10) };
     // Shuffle pieces on first and last rank
-    for (let c of ["w","b"])
-    {
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(10);
 
       // Get random squares for bishops
@@ -342,8 +330,8 @@ export const VariantRules = class GrandRules extends ChessRules
       let randIndex_tmp = 2 * randInt(5) + 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);
+      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       // Get random squares for knights
       randIndex = randInt(8);
@@ -374,20 +362,22 @@ export const VariantRules = class GrandRules extends ChessRules
       let rook2Pos = positions[2];
 
       // 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][marshallPos] = 'm';
-      pieces[c][cardinalPos] = 'c';
-      pieces[c][kingPos] = 'k';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][rook2Pos] = 'r';
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][marshallPos] = "m";
+      pieces[c][cardinalPos] = "c";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
     }
-    return pieces["b"].join("") +
+    return (
+      pieces["b"].join("") +
       "/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0 1111 - 00000000000000";
+      " w 0 1111 - 00000000000000"
+    );
   }
-}
+};
diff --git a/client/src/variants/Losers.js b/client/src/variants/Losers.js
index d815ee7a..81d2730a 100644
--- a/client/src/variants/Losers.js
+++ b/client/src/variants/Losers.js
@@ -2,63 +2,71 @@ import { ChessRules } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
-export const VariantRules = class LosersRules extends ChessRules
-{
-  static get HasFlags() { return false; }
+export const VariantRules = class LosersRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
 
-  getPotentialPawnMoves([x,y])
-  {
-    let moves = super.getPotentialPawnMoves([x,y]);
+  getPotentialPawnMoves([x, y]) {
+    let moves = super.getPotentialPawnMoves([x, y]);
 
     // Complete with promotion(s) into king, if possible
     const color = this.turn;
-    const shift = (color == "w" ? -1 : 1);
-    const lastRank = (color == "w" ? 0 : V.size.x-1);
-    if (x+shift == lastRank)
-    {
+    const shift = color == "w" ? -1 : 1;
+    const lastRank = color == "w" ? 0 : V.size.x - 1;
+    if (x + shift == lastRank) {
       // Normal move
-      if (this.board[x+shift][y] == V.EMPTY)
-        moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
+      if (this.board[x + shift][y] == V.EMPTY)
+        moves.push(
+          this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
+        );
       // Captures
-      if (y>0 && this.canTake([x,y], [x+shift,y-1])
-        && this.board[x+shift][y-1] != V.EMPTY)
-      {
-        moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
+      if (
+        y > 0 &&
+        this.canTake([x, y], [x + shift, y - 1]) &&
+        this.board[x + shift][y - 1] != V.EMPTY
+      ) {
+        moves.push(
+          this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
+        );
       }
-      if (y<V.size.y-1 && this.canTake([x,y], [x+shift,y+1])
-        && this.board[x+shift][y+1] != V.EMPTY)
-      {
-        moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:V.KING}));
+      if (
+        y < V.size.y - 1 &&
+        this.canTake([x, y], [x + shift, y + 1]) &&
+        this.board[x + shift][y + 1] != V.EMPTY
+      ) {
+        moves.push(
+          this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
+        );
       }
     }
 
     return moves;
   }
 
-  getPotentialKingMoves(sq)
-  {
+  getPotentialKingMoves(sq) {
     // No castle:
-    return this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+    return this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
   // Stop at the first capture found (if any)
-  atLeastOneCapture()
-  {
+  atLeastOneCapture() {
     const color = this.turn;
     const oppCol = V.GetOppCol(color);
-    for (let i=0; i<V.size.x; i++)
-    {
-      for (let j=0; j<V.size.y; j++)
-      {
-        if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
-        {
-          const moves = this.getPotentialMovesFrom([i,j]);
-          if (moves.length > 0)
-          {
-            for (let k=0; k<moves.length; k++)
-            {
-              if (moves[k].vanish.length==2 && this.filterValid([moves[k]]).length > 0)
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+          const moves = this.getPotentialMovesFrom([i, j]);
+          if (moves.length > 0) {
+            for (let k = 0; k < moves.length; k++) {
+              if (
+                moves[k].vanish.length == 2 &&
+                this.filterValid([moves[k]]).length > 0
+              )
                 return true;
             }
           }
@@ -69,77 +77,75 @@ export const VariantRules = class LosersRules extends ChessRules
   }
 
   // Trim all non-capturing moves
-  static KeepCaptures(moves)
-  {
-    return moves.filter(m => { return m.vanish.length == 2; });
+  static KeepCaptures(moves) {
+    return moves.filter(m => {
+      return m.vanish.length == 2;
+    });
   }
 
-  getPossibleMovesFrom(sq)
-  {
-    let moves = this.filterValid( this.getPotentialMovesFrom(sq) );
+  getPossibleMovesFrom(sq) {
+    let moves = this.filterValid(this.getPotentialMovesFrom(sq));
     // This is called from interface: we need to know if a capture is possible
-    if (this.atLeastOneCapture())
-      moves = V.KeepCaptures(moves);
+    if (this.atLeastOneCapture()) moves = V.KeepCaptures(moves);
     return moves;
   }
 
-  getAllValidMoves()
-  {
+  getAllValidMoves() {
     let moves = super.getAllValidMoves();
-    if (moves.some(m => { return m.vanish.length == 2; }))
+    if (
+      moves.some(m => {
+        return m.vanish.length == 2;
+      })
+    )
       moves = V.KeepCaptures(moves);
     return moves;
   }
 
-  underCheck(color)
-  {
+  underCheck() {
     return false; //No notion of check
   }
 
-  getCheckSquares(move)
-  {
+  getCheckSquares() {
     return [];
   }
 
   // No variables update because no royal king + no castling
-  updateVariables(move) { }
-  unupdateVariables(move) { }
+  updateVariables() {}
+  unupdateVariables() {}
 
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
 
     // No valid move: the side who cannot move wins
-    return (this.turn == "w" ? "1-0" : "0-1");
+    return this.turn == "w" ? "1-0" : "0-1";
   }
 
-  static get VALUES()
-  {
+  static get VALUES() {
     // Experimental...
     return {
-      'p': 1,
-      'r': 7,
-      'n': 3,
-      'b': 3,
-      'q': 5,
-      'k': 5
+      p: 1,
+      r: 7,
+      n: 3,
+      b: 3,
+      q: 5,
+      k: 5
     };
   }
 
-  static get SEARCH_DEPTH() { return 4; }
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
 
-  evalPosition()
-  {
-    return - super.evalPosition(); //better with less material
+  evalPosition() {
+    return -super.evalPosition(); //better with less material
   }
 
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(8), "b": new Array(8) };
+  static GenRandInitFen() {
+    let pieces = { w: new Array(8), b: new Array(8) };
     // Shuffle pieces on first and last rank
-    for (let c of ["w","b"])
-    {
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(8);
 
       // Get random squares for bishops
@@ -149,8 +155,8 @@ export const VariantRules = class LosersRules extends ChessRules
       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);
+      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       // Get random squares for knights
       randIndex = randInt(6);
@@ -175,18 +181,20 @@ export const VariantRules = class LosersRules extends ChessRules
       let rook2Pos = positions[1];
 
       // 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][kingPos] = 'k';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][rook2Pos] = 'r';
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
     }
-    return pieces["b"].join("") +
+    return (
+      pieces["b"].join("") +
       "/pppppppp/8/8/8/8/PPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0 -"; //en-passant allowed, but no flags
+      " w 0 -"
+    ); //en-passant allowed, but no flags
   }
-}
+};
diff --git a/client/src/variants/Magnetic.js b/client/src/variants/Magnetic.js
index 3789de38..0110c49e 100644
--- a/client/src/variants/Magnetic.js
+++ b/client/src/variants/Magnetic.js
@@ -1,98 +1,92 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
-export const VariantRules = class  MagneticRules extends ChessRules
-{
-  static get HasEnpassant() { return false; }
+export const VariantRules = class MagneticRules extends ChessRules {
+  static get HasEnpassant() {
+    return false;
+  }
 
-  getPotentialMovesFrom([x,y])
-  {
-    let standardMoves = super.getPotentialMovesFrom([x,y]);
+  getPotentialMovesFrom([x, y]) {
+    let standardMoves = super.getPotentialMovesFrom([x, y]);
     let moves = [];
     standardMoves.forEach(m => {
       let newMove_s = this.applyMagneticLaws(m);
-      if (newMove_s.length == 1)
-        moves.push(newMove_s[0]);
-      else //promotion
-        moves = moves.concat(newMove_s);
+      if (newMove_s.length == 1) moves.push(newMove_s[0]);
+      //promotion
+      else moves = moves.concat(newMove_s);
     });
     return moves;
   }
 
   // Complete a move with magnetic actions
   // TODO: job is done multiple times for (normal) promotions.
-  applyMagneticLaws(move)
-  {
-    if (move.appear[0].p == V.KING && move.appear.length==1)
-      return [move]; //kings are not charged
-    const aIdx = (move.appear[0].p != V.KING ? 0 : 1); //if castling, rook is charged
-    const [x,y] = [move.appear[aIdx].x, move.appear[aIdx].y];
+  applyMagneticLaws(move) {
+    if (move.appear[0].p == V.KING && move.appear.length == 1) return [move]; //kings are not charged
+    const aIdx = move.appear[0].p != V.KING ? 0 : 1; //if castling, rook is charged
+    const [x, y] = [move.appear[aIdx].x, move.appear[aIdx].y];
     const color = this.turn;
-    const lastRank = (color=="w" ? 0 : 7);
+    const lastRank = color == "w" ? 0 : 7;
     const standardMove = JSON.parse(JSON.stringify(move));
     this.play(standardMove);
-    for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
-    {
-      let [i,j] = [x+step[0],y+step[1]];
-      while (V.OnBoard(i,j))
-      {
-        if (this.board[i][j] != V.EMPTY)
-        {
+    for (let step of [
+      [-1, 0],
+      [1, 0],
+      [0, -1],
+      [0, 1]
+    ]) {
+      let [i, j] = [x + step[0], y + step[1]];
+      while (V.OnBoard(i, j)) {
+        if (this.board[i][j] != V.EMPTY) {
           // Found something. Same color or not?
-          if (this.getColor(i,j) != color)
-          {
+          if (this.getColor(i, j) != color) {
             // Attraction
-            if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
-            {
+            if (
+              (Math.abs(i - x) >= 2 || Math.abs(j - y) >= 2) &&
+              this.getPiece(i, j) != V.KING
+            ) {
               move.vanish.push(
                 new PiPo({
-                  p:this.getPiece(i,j),
-                  c:this.getColor(i,j),
-                  x:i,
-                  y:j
+                  p: this.getPiece(i, j),
+                  c: this.getColor(i, j),
+                  x: i,
+                  y: j
                 })
               );
               move.appear.push(
                 new PiPo({
-                  p:this.getPiece(i,j),
-                  c:this.getColor(i,j),
-                  x:x+step[0],
-                  y:y+step[1]
+                  p: this.getPiece(i, j),
+                  c: this.getColor(i, j),
+                  x: x + step[0],
+                  y: y + step[1]
                 })
               );
             }
-          }
-          else
-          {
+          } else {
             // Repulsion
-            if (this.getPiece(i,j) != V.KING)
-            {
+            if (this.getPiece(i, j) != V.KING) {
               // Push it until we meet an obstacle or edge of the board
-              let [ii,jj] = [i+step[0],j+step[1]];
-              while (V.OnBoard(ii,jj))
-              {
-                if (this.board[ii][jj] != V.EMPTY)
-                  break;
+              let [ii, jj] = [i + step[0], j + step[1]];
+              while (V.OnBoard(ii, jj)) {
+                if (this.board[ii][jj] != V.EMPTY) break;
                 ii += step[0];
                 jj += step[1];
               }
               ii -= step[0];
               jj -= step[1];
-              if (Math.abs(ii-i)>=1 || Math.abs(jj-j)>=1)
-              {
+              if (Math.abs(ii - i) >= 1 || Math.abs(jj - j) >= 1) {
                 move.vanish.push(
                   new PiPo({
-                    p:this.getPiece(i,j),
-                    c:this.getColor(i,j),
-                    x:i,
-                    y:j
+                    p: this.getPiece(i, j),
+                    c: this.getColor(i, j),
+                    x: i,
+                    y: j
                   })
                 );
                 move.appear.push(
                   new PiPo({
-                    p:this.getPiece(i,j),
-                    c:this.getColor(i,j),
-                    x:ii,
-                    y:jj
+                    p: this.getPiece(i, j),
+                    c: this.getColor(i, j),
+                    x: ii,
+                    y: jj
                   })
                 );
               }
@@ -107,15 +101,15 @@ export const VariantRules = class  MagneticRules extends ChessRules
     this.undo(standardMove);
     let moves = [];
     // Scan move for pawn (max 1) on 8th rank
-    for (let i=1; i<move.appear.length; i++)
-    {
-      if (move.appear[i].p==V.PAWN && move.appear[i].c==color
-        && move.appear[i].x==lastRank)
-      {
+    for (let i = 1; i < move.appear.length; i++) {
+      if (
+        move.appear[i].p == V.PAWN &&
+        move.appear[i].c == color &&
+        move.appear[i].x == lastRank
+      ) {
         move.appear[i].p = V.ROOK;
         moves.push(move);
-        for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
-        {
+        for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN]) {
           let cmove = JSON.parse(JSON.stringify(move));
           cmove.appear[i].p = piece;
           moves.push(cmove);
@@ -129,63 +123,59 @@ export const VariantRules = class  MagneticRules extends ChessRules
         break;
       }
     }
-    if (moves.length == 0) //no pawn on 8th rank
+    if (moves.length == 0)
+      //no pawn on 8th rank
       moves.push(move);
     return moves;
   }
 
-  atLeastOneMove()
-  {
-    if (this.kingPos[this.turn][0] < 0)
-      return false;
+  atLeastOneMove() {
+    if (this.kingPos[this.turn][0] < 0) return false;
     return true; //TODO: is it right?
   }
 
-  underCheck(color)
-  {
+  underCheck() {
     return false; //there is no check
   }
 
-  getCheckSquares(move)
-  {
+  getCheckSquares() {
     return [];
   }
 
-  updateVariables(move)
-  {
+  updateVariables(move) {
     super.updateVariables(move);
     const c = move.vanish[0].c;
-    if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
-    {
+    if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) {
       // We took opponent king !
       const oppCol = V.GetOppCol(c);
-      this.kingPos[oppCol] = [-1,-1];
-      this.castleFlags[oppCol] = [false,false];
+      this.kingPos[oppCol] = [-1, -1];
+      this.castleFlags[oppCol] = [false, false];
     }
     // Did we magnetically move our (init) rooks or opponents' ones ?
-    const firstRank = (c == "w" ? 7 : 0);
+    const firstRank = c == "w" ? 7 : 0;
     const oppFirstRank = 7 - firstRank;
     const oppCol = V.GetOppCol(c);
     move.vanish.forEach(psq => {
       if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y))
-        this.castleFlags[c][psq.y==this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
-      else if (psq.x == oppFirstRank && this.INIT_COL_ROOK[oppCol].includes(psq.y))
-        this.castleFlags[oppCol][psq.y==this.INIT_COL_ROOK[oppCol][0] ? 0 : 1] = false;
+        this.castleFlags[c][psq.y == this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
+      else if (
+        psq.x == oppFirstRank &&
+        this.INIT_COL_ROOK[oppCol].includes(psq.y)
+      )
+        this.castleFlags[oppCol][
+          psq.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1
+        ] = false;
     });
   }
 
-  unupdateVariables(move)
-  {
+  unupdateVariables(move) {
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
     const oppCol = V.GetOppCol(c);
-    if (this.kingPos[oppCol][0] < 0)
-    {
+    if (this.kingPos[oppCol][0] < 0) {
       // Last move took opponent's king
-      for (let psq of move.vanish)
-      {
-        if (psq.p == 'k')
-        {
+      for (let psq of move.vanish) {
+        if (psq.p == "k") {
           this.kingPos[oppCol] = [psq.x, psq.y];
           break;
         }
@@ -193,19 +183,19 @@ export const VariantRules = class  MagneticRules extends ChessRules
     }
   }
 
-  getCurrentScore()
-  {
+  getCurrentScore() {
     const color = this.turn;
     const kp = this.kingPos[color];
-    if (kp[0] < 0) //king disappeared
-      return (color == "w" ? "0-1" : "1-0");
-    if (this.atLeastOneMove()) // game not over
+    if (kp[0] < 0)
+      //king disappeared
+      return color == "w" ? "0-1" : "1-0";
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
     return "1/2"; //no moves but kings still there
   }
 
-  static get THRESHOLD_MATE()
-  {
+  static get THRESHOLD_MATE() {
     return 500; //checkmates evals may be slightly below 1000
   }
-}
+};
diff --git a/client/src/variants/Marseille.js b/client/src/variants/Marseille.js
index 31c363d2..19d35a63 100644
--- a/client/src/variants/Marseille.js
+++ b/client/src/variants/Marseille.js
@@ -1,106 +1,94 @@
 import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
-export const VariantRules = class MarseilleRules extends ChessRules
-{
-  static IsGoodEnpassant(enpassant)
-  {
-    if (enpassant != "-")
-    {
+export const VariantRules = class MarseilleRules extends ChessRules {
+  static IsGoodEnpassant(enpassant) {
+    if (enpassant != "-") {
       const squares = enpassant.split(",");
-      if (squares.length > 2)
-        return false;
-      for (let sq of squares)
-      {
+      if (squares.length > 2) return false;
+      for (let sq of squares) {
         const ep = V.SquareToCoords(sq);
-        if (isNaN(ep.x) || !V.OnBoard(ep))
-          return false;
+        if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
       }
     }
     return true;
   }
 
-  getTurnFen()
-  {
+  getTurnFen() {
     return this.turn + this.subTurn;
   }
 
   // There may be 2 enPassant squares (if 2 pawns jump 2 squares in same turn)
-  getEnpassantFen()
-  {
+  getEnpassantFen() {
     const L = this.epSquares.length;
-    if (this.epSquares[L-1].every(epsq => epsq === undefined))
-      return "-"; //no en-passant
+    if (this.epSquares[L - 1].every(epsq => epsq === undefined)) return "-"; //no en-passant
     let res = "";
-    this.epSquares[L-1].forEach(epsq => {
-      if (!!epsq)
-        res += V.CoordsToSquare(epsq) + ",";
+    this.epSquares[L - 1].forEach(epsq => {
+      if (epsq) res += V.CoordsToSquare(epsq) + ",";
     });
-    return res.slice(0,-1); //remove last comma
+    return res.slice(0, -1); //remove last comma
   }
 
-  setOtherVariables(fen)
-  {
+  setOtherVariables(fen) {
     const parsedFen = V.ParseFen(fen);
     this.setFlags(parsedFen.flags);
-    if (parsedFen.enpassant == "-")
-      this.epSquares = [ [undefined] ];
-    else
-    {
+    if (parsedFen.enpassant == "-") this.epSquares = [[undefined]];
+    else {
       let res = [];
       const squares = parsedFen.enpassant.split(",");
-      for (let sq of squares)
-        res.push(V.SquareToCoords(sq));
-      this.epSquares = [ res ];
+      for (let sq of squares) res.push(V.SquareToCoords(sq));
+      this.epSquares = [res];
     }
     this.scanKingsRooks(fen);
     // Extract subTurn from turn indicator: "w" (first move), or
     // "w1" or "w2" white subturn 1 or 2, and same for black
     const fullTurn = V.ParseFen(fen).turn;
     this.turn = fullTurn[0];
-    this.subTurn = (fullTurn[1] || 0); //"w0" = special code for first move in game
+    this.subTurn = fullTurn[1] || 0; //"w0" = special code for first move in game
   }
 
-  getPotentialPawnMoves([x,y])
-  {
+  getPotentialPawnMoves([x, y]) {
     const color = this.turn;
     let moves = [];
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
-    const shiftX = (color == "w" ? -1 : 1);
-    const firstRank = (color == 'w' ? sizeX-1 : 0);
-    const startRank = (color == "w" ? sizeX-2 : 1);
-    const lastRank = (color == "w" ? 0 : sizeX-1);
-    const finalPieces = x + shiftX == lastRank
-      ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
-      : [V.PAWN];
+    const [sizeX, sizeY] = [V.size.x, V.size.y];
+    const shiftX = color == "w" ? -1 : 1;
+    const firstRank = color == "w" ? sizeX - 1 : 0;
+    const startRank = color == "w" ? sizeX - 2 : 1;
+    const lastRank = color == "w" ? 0 : sizeX - 1;
+    const finalPieces =
+      x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
 
     // One square forward
-    if (this.board[x+shiftX][y] == V.EMPTY)
-    {
-      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) {
+      for (let piece of finalPieces) {
+        moves.push(
+          this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+        );
       }
       // Next condition because pawns on 1st rank can generally jump
-      if ([startRank,firstRank].includes(x)
-        && this.board[x+2*shiftX][y] == V.EMPTY)
-      {
+      if (
+        [startRank, firstRank].includes(x) &&
+        this.board[x + 2 * shiftX][y] == V.EMPTY
+      ) {
         // Two squares jump
-        moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+        moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
       }
     }
     // Captures
-    for (let shiftY of [-1,1])
-    {
-      if (y + shiftY >= 0 && y + shiftY < sizeY
-        && this.board[x+shiftX][y+shiftY] != V.EMPTY
-        && this.canTake([x,y], [x+shiftX,y+shiftY]))
-      {
-        for (let piece of finalPieces)
-        {
-          moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
-            {c:color,p:piece}));
+    for (let shiftY of [-1, 1]) {
+      if (
+        y + shiftY >= 0 &&
+        y + shiftY < sizeY &&
+        this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+        this.canTake([x, y], [x + shiftX, y + shiftY])
+      ) {
+        for (let piece of finalPieces) {
+          moves.push(
+            this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+              c: color,
+              p: piece
+            })
+          );
         }
       }
     }
@@ -109,31 +97,33 @@ export const VariantRules = class MarseilleRules extends ChessRules
     // OK on subturn 2 only if enPassant was played at subturn 1
     // (and if there are two e.p. squares available).
     const Lep = this.epSquares.length;
-    const epSquares = this.epSquares[Lep-1]; //always at least one element
+    const epSquares = this.epSquares[Lep - 1]; //always at least one element
     let epSqs = [];
     epSquares.forEach(sq => {
-      if (!!sq)
-        epSqs.push(sq);
+      if (sq) epSqs.push(sq);
     });
-    if (epSqs.length == 0)
-      return moves;
+    if (epSqs.length == 0) return moves;
     const oppCol = V.GetOppCol(color);
-    for (let sq of epSqs)
-    {
-      if (this.subTurn == 1 || (epSqs.length == 2 &&
-        // Was this en-passant capture already played at subturn 1 ?
-        // (Or maybe the opponent filled the en-passant square with a piece)
-        this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY))
-      {
-        if (sq.x == x+shiftX && Math.abs(sq.y - y) == 1
+    for (let sq of epSqs) {
+      if (
+        this.subTurn == 1 ||
+        (epSqs.length == 2 &&
+          // Was this en-passant capture already played at subturn 1 ?
+          // (Or maybe the opponent filled the en-passant square with a piece)
+          this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY)
+      ) {
+        if (
+          sq.x == x + shiftX &&
+          Math.abs(sq.y - y) == 1 &&
           // Add condition "enemy pawn must be present"
-          && this.getPiece(x,sq.y) == V.PAWN && this.getColor(x,sq.y) == oppCol)
-        {
-          let epMove = this.getBasicMove([x,y], [sq.x,sq.y]);
+          this.getPiece(x, sq.y) == V.PAWN &&
+          this.getColor(x, sq.y) == oppCol
+        ) {
+          let epMove = this.getBasicMove([x, y], [sq.x, sq.y]);
           epMove.vanish.push({
             x: x,
             y: sq.y,
-            p: 'p',
+            p: "p",
             c: oppCol
           });
           moves.push(epMove);
@@ -144,49 +134,41 @@ export const VariantRules = class MarseilleRules extends ChessRules
     return moves;
   }
 
-  play(move)
-  {
+  play(move) {
     move.flags = JSON.stringify(this.aggregateFlags());
     move.turn = this.turn + this.subTurn;
     V.PlayOnBoard(this.board, move);
     const epSq = this.getEpSquare(move);
-    if (this.subTurn == 0) //first move in game
-    {
+    if (this.subTurn == 0) {
+      //first move in game
       this.turn = "b";
       this.subTurn = 1;
       this.epSquares.push([epSq]);
     }
     // Does this move give check on subturn 1? If yes, skip subturn 2
-    else if (this.subTurn==1 && this.underCheck(V.GetOppCol(this.turn)))
-    {
+    else if (this.subTurn == 1 && this.underCheck(V.GetOppCol(this.turn))) {
       this.turn = V.GetOppCol(this.turn);
       this.epSquares.push([epSq]);
       move.checkOnSubturn1 = true;
-    }
-    else
-    {
-      if (this.subTurn == 2)
-      {
+    } else {
+      if (this.subTurn == 2) {
         this.turn = V.GetOppCol(this.turn);
-        let lastEpsq = this.epSquares[this.epSquares.length-1];
+        let lastEpsq = this.epSquares[this.epSquares.length - 1];
         lastEpsq.push(epSq);
-      }
-      else
-        this.epSquares.push([epSq]);
+      } else this.epSquares.push([epSq]);
       this.subTurn = 3 - this.subTurn;
     }
     this.updateVariables(move);
   }
 
-  undo(move)
-  {
+  undo(move) {
     this.disaggregateFlags(JSON.parse(move.flags));
     V.UndoOnBoard(this.board, move);
-    if (move.turn[1] == '0' || move.checkOnSubturn1 || this.subTurn == 2)
+    if (move.turn[1] == "0" || move.checkOnSubturn1 || this.subTurn == 2)
       this.epSquares.pop();
-    else //this.subTurn == 1
-    {
-      let lastEpsq = this.epSquares[this.epSquares.length-1];
+    //this.subTurn == 1
+    else {
+      let lastEpsq = this.epSquares[this.epSquares.length - 1];
       lastEpsq.pop();
     }
     this.turn = move.turn[0];
@@ -197,23 +179,20 @@ export const VariantRules = class MarseilleRules extends ChessRules
   // NOTE:  GenRandInitFen() is OK,
   // since at first move turn indicator is just "w"
 
-  static get VALUES()
-  {
+  static get VALUES() {
     return {
-      'p': 1,
-      'r': 5,
-      'n': 3,
-      'b': 3,
-      'q': 7, //slightly less than in orthodox game
-      'k': 1000
+      p: 1,
+      r: 5,
+      n: 3,
+      b: 3,
+      q: 7, //slightly less than in orthodox game
+      k: 1000
     };
   }
 
   // No alpha-beta here, just adapted min-max at depth 2(+1)
-  getComputerMove()
-  {
-    if (this.subTurn == 2)
-      return null; //TODO: imperfect interface setup
+  getComputerMove() {
+    if (this.subTurn == 2) return null; //TODO: imperfect interface setup
 
     const maxeval = V.INFINITY;
     const color = this.turn;
@@ -221,35 +200,29 @@ export const VariantRules = class MarseilleRules extends ChessRules
 
     // Search best (half) move for opponent turn
     const getBestMoveEval = () => {
-      const turnBefore = this.turn + this.subTurn;
       let score = this.getCurrentScore();
-      if (score != "*")
-      {
-        if (score == "1/2")
-          return 0;
+      if (score != "*") {
+        if (score == "1/2") return 0;
         return maxeval * (score == "1-0" ? 1 : -1);
       }
       let moves = this.getAllValidMoves();
-      let res = (oppCol == "w" ? -maxeval : maxeval);
-      for (let m of moves)
-      {
+      let res = oppCol == "w" ? -maxeval : maxeval;
+      for (let m of moves) {
         this.play(m);
         score = this.getCurrentScore();
         // Now turn is oppCol,2 if m doesn't give check
         // Otherwise it's color,1. In both cases the next test makes sense
-        if (score != "*")
-        {
+        if (score != "*") {
           if (score == "1/2")
-            res = (oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0));
-          else
-          {
+            res = oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0);
+          else {
             // Found a mate
             this.undo(m);
             return maxeval * (score == "1-0" ? 1 : -1);
           }
         }
         const evalPos = this.evalPosition();
-        res = (oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos));
+        res = oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos);
         this.undo(m);
       }
       return res;
@@ -258,42 +231,39 @@ export const VariantRules = class MarseilleRules extends ChessRules
     let moves11 = this.getAllValidMoves();
     let doubleMoves = [];
     // Rank moves using a min-max at depth 2
-    for (let i=0; i<moves11.length; i++)
-    {
+    for (let i = 0; i < moves11.length; i++) {
       this.play(moves11[i]);
-      if (this.turn != color)
-      {
+      if (this.turn != color) {
         // We gave check with last move: search the best opponent move
-        doubleMoves.push({moves:[moves11[i]], eval:getBestMoveEval()});
-      }
-      else
-      {
+        doubleMoves.push({ moves: [moves11[i]], eval: getBestMoveEval() });
+      } else {
         let moves12 = this.getAllValidMoves();
-        for (let j=0; j<moves12.length; j++)
-        {
+        for (let j = 0; j < moves12.length; j++) {
           this.play(moves12[j]);
           doubleMoves.push({
-            moves:[moves11[i],moves12[j]],
-            eval:getBestMoveEval()});
+            moves: [moves11[i], moves12[j]],
+            eval: getBestMoveEval()
+          });
           this.undo(moves12[j]);
         }
       }
       this.undo(moves11[i]);
     }
 
-    doubleMoves.sort( (a,b) => {
-      return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+    doubleMoves.sort((a, b) => {
+      return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+    });
     let candidates = [0]; //indices of candidates moves
-    for (let i=1;
-      i<doubleMoves.length && doubleMoves[i].eval == doubleMoves[0].eval;
-      i++)
-    {
+    for (
+      let i = 1;
+      i < doubleMoves.length && doubleMoves[i].eval == doubleMoves[0].eval;
+      i++
+    ) {
       candidates.push(i);
     }
 
     const selected = doubleMoves[randInt(candidates.length)].moves;
-    if (selected.length == 1)
-      return selected[0];
+    if (selected.length == 1) return selected[0];
     return selected;
   }
-}
+};
diff --git a/client/src/variants/Upsidedown.js b/client/src/variants/Upsidedown.js
index bf3e37c4..538fb4a8 100644
--- a/client/src/variants/Upsidedown.js
+++ b/client/src/variants/Upsidedown.js
@@ -2,24 +2,27 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 import { ArrayFun } from "@/utils/array";
 
-export const VariantRules = class UpsidedownRules extends ChessRules
-{
-  static get HasFlags() { return false; }
+export const VariantRules = class UpsidedownRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
 
-  static get HasEnpassant() { return false; }
+  static get HasEnpassant() {
+    return false;
+  }
 
-  getPotentialKingMoves(sq)
-  {
+  getPotentialKingMoves(sq) {
     // No castle
-    return this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+    return this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
   }
 
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(8), "b": new Array(8) };
-    for (let c of ["w","b"])
-    {
+  static GenRandInitFen() {
+    let pieces = { w: new Array(8), b: new Array(8) };
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(8);
 
       let randIndex = randInt(8);
@@ -28,12 +31,9 @@ export const VariantRules = class UpsidedownRules extends ChessRules
 
       // At least a knight must be next to the king:
       let knight1Pos = undefined;
-      if (kingPos == 0)
-        knight1Pos = 1;
-      else if (kingPos == V.size.y-1)
-        knight1Pos = V.size.y-2;
-      else
-        knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1);
+      if (kingPos == 0) knight1Pos = 1;
+      else if (kingPos == V.size.y - 1) knight1Pos = V.size.y - 2;
+      else knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1);
       // Search for knight1Pos index in positions and remove it
       const knight1Index = positions.indexOf(knight1Pos);
       positions.splice(knight1Index, 1);
@@ -43,8 +43,8 @@ export const VariantRules = class UpsidedownRules extends ChessRules
       const bishop1Pos = positions[randIndex];
       let randIndex_tmp = 2 * randInt(3) + 1;
       const bishop2Pos = positions[randIndex_tmp];
-      positions.splice(Math.max(randIndex,randIndex_tmp), 1);
-      positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
 
       randIndex = randInt(4);
       const knight2Pos = positions[randIndex];
@@ -57,18 +57,20 @@ export const VariantRules = class UpsidedownRules extends ChessRules
       const rook1Pos = positions[0];
       const rook2Pos = positions[1];
 
-      pieces[c][rook1Pos] = 'r';
-      pieces[c][knight1Pos] = 'n';
-      pieces[c][bishop1Pos] = 'b';
-      pieces[c][queenPos] = 'q';
-      pieces[c][kingPos] = 'k';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][rook2Pos] = 'r';
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
     }
-    return pieces["w"].join("").toUpperCase() +
+    return (
+      pieces["w"].join("").toUpperCase() +
       "/PPPPPPPP/8/8/8/8/pppppppp/" +
       pieces["b"].join("") +
-      " w 0"; //no castle, no en-passant
+      " w 0"
+    ); //no castle, no en-passant
   }
-}
+};
diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js
index a30e7f91..933abbde 100644
--- a/client/src/variants/Wildebeest.js
+++ b/client/src/variants/Wildebeest.js
@@ -2,71 +2,73 @@ import { ChessRules } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { sample, randInt } from "@/utils/alea";
 
-export const VariantRules = class  WildebeestRules extends ChessRules
-{
-  static getPpath(b)
-  {
-    return ([V.CAMEL,V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
+export const VariantRules = class WildebeestRules extends ChessRules {
+  static getPpath(b) {
+    return ([V.CAMEL, V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
   }
 
-  static get size() { return {x:10,y:11}; }
+  static get size() {
+    return { x: 10, y: 11 };
+  }
 
-  static get CAMEL() { return 'c'; }
-  static get WILDEBEEST() { return 'w'; }
+  static get CAMEL() {
+    return "c";
+  }
+  static get WILDEBEEST() {
+    return "w";
+  }
 
-  static get PIECES()
-  {
-    return ChessRules.PIECES.concat([V.CAMEL,V.WILDEBEEST]);
+  static get PIECES() {
+    return ChessRules.PIECES.concat([V.CAMEL, V.WILDEBEEST]);
   }
 
-  static get steps()
-  {
+  static get steps() {
     return Object.assign(
       ChessRules.steps, //add camel moves:
-      {'c': [ [-3,-1],[-3,1],[-1,-3],[-1,3],[1,-3],[1,3],[3,-1],[3,1] ]}
+      {
+        c: [
+          [-3, -1],
+          [-3, 1],
+          [-1, -3],
+          [-1, 3],
+          [1, -3],
+          [1, 3],
+          [3, -1],
+          [3, 1]
+        ]
+      }
     );
   }
 
-  static IsGoodEnpassant(enpassant)
-  {
-    if (enpassant != "-")
-    {
+  static IsGoodEnpassant(enpassant) {
+    if (enpassant != "-") {
       const squares = enpassant.split(",");
-      if (squares.length > 2)
-        return false;
-      for (let sq of squares)
-      {
+      if (squares.length > 2) return false;
+      for (let sq of squares) {
         const ep = V.SquareToCoords(sq);
-        if (isNaN(ep.x) || !V.OnBoard(ep))
-          return false;
+        if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
       }
     }
     return true;
   }
 
   // There may be 2 enPassant squares (if pawn jump 3 squares)
-  getEnpassantFen()
-  {
+  getEnpassantFen() {
     const L = this.epSquares.length;
-    if (!this.epSquares[L-1])
-      return "-"; //no en-passant
+    if (!this.epSquares[L - 1]) return "-"; //no en-passant
     let res = "";
-    this.epSquares[L-1].forEach(sq => {
+    this.epSquares[L - 1].forEach(sq => {
       res += V.CoordsToSquare(sq) + ",";
     });
-    return res.slice(0,-1); //remove last comma
+    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")
-    {
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
       const square = moveOrSquare;
-      if (square == "-")
-        return undefined;
+      if (square == "-") return undefined;
       let res = [];
       square.split(",").forEach(sq => {
         res.push(V.SquareToCoords(sq));
@@ -75,18 +77,19 @@ export const VariantRules = class  WildebeestRules extends ChessRules
     }
     // 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 move
-      {
+    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 move
         res.push({
-          x: sx + 2*step,
+          x: sx + 2 * step,
           y: sy
         });
       }
@@ -95,85 +98,80 @@ export const VariantRules = class  WildebeestRules extends ChessRules
     return undefined; //default
   }
 
-  getPotentialMovesFrom([x,y])
-  {
-    switch (this.getPiece(x,y))
-    {
+  getPotentialMovesFrom([x, y]) {
+    switch (this.getPiece(x, y)) {
       case V.CAMEL:
-        return this.getPotentialCamelMoves([x,y]);
+        return this.getPotentialCamelMoves([x, y]);
       case V.WILDEBEEST:
-        return this.getPotentialWildebeestMoves([x,y]);
+        return this.getPotentialWildebeestMoves([x, y]);
       default:
-        return super.getPotentialMovesFrom([x,y])
+        return super.getPotentialMovesFrom([x, y]);
     }
   }
 
   // Pawns jump 2 or 3 squares, and promote to queen or wildebeest
-  getPotentialPawnMoves([x,y])
-  {
+  getPotentialPawnMoves([x, y]) {
     const color = this.turn;
     let moves = [];
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
-    const shiftX = (color == "w" ? -1 : 1);
-    const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
-    const lastRank = (color == "w" ? 0 : sizeX-1);
-    const finalPieces = x + shiftX == lastRank
-      ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
-      : [V.PAWN];
+    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 lastRank = color == "w" ? 0 : sizeX - 1;
+    const finalPieces =
+      x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
 
-    if (this.board[x+shiftX][y] == V.EMPTY)
-    {
+    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)
-        {
+        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)
-          {
+          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]));
+            moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
           }
         }
       }
     }
     // Captures
-    for (let shiftY of [-1,1])
-    {
-      if (y + shiftY >= 0 && y + shiftY < sizeY
-        && this.board[x+shiftX][y+shiftY] != V.EMPTY
-        && this.canTake([x,y], [x+shiftX,y+shiftY]))
-      {
-        for (let piece of finalPieces)
-        {
-          moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
-            {c:color,p:piece}));
+    for (let shiftY of [-1, 1]) {
+      if (
+        y + shiftY >= 0 &&
+        y + shiftY < sizeY &&
+        this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+        this.canTake([x, y], [x + shiftX, y + shiftY])
+      ) {
+        for (let piece of finalPieces) {
+          moves.push(
+            this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+              c: color,
+              p: piece
+            })
+          );
         }
       }
     }
 
     // En passant
     const Lep = this.epSquares.length;
-    const epSquare = this.epSquares[Lep-1];
-    if (!!epSquare)
-    {
-      for (let epsq of epSquare)
-      {
+    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)
-        {
-          var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]);
+        if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
+          var 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);
+          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)
+            p: "p",
+            c: this.getColor(px, epsq.y)
           });
           moves.push(enpassantMove);
         }
@@ -185,74 +183,87 @@ export const VariantRules = class  WildebeestRules extends ChessRules
 
   // TODO: wildebeest castle
 
-  getPotentialCamelMoves(sq)
-  {
+  getPotentialCamelMoves(sq) {
     return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], "oneStep");
   }
 
-  getPotentialWildebeestMoves(sq)
-  {
+  getPotentialWildebeestMoves(sq) {
     return this.getSlideNJumpMoves(
-      sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
+      sq,
+      V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
+      "oneStep"
+    );
   }
 
-  isAttacked(sq, colors)
-  {
-    return super.isAttacked(sq, colors)
-      || this.isAttackedByCamel(sq, colors)
-      || this.isAttackedByWildebeest(sq, colors);
+  isAttacked(sq, colors) {
+    return (
+      super.isAttacked(sq, colors) ||
+      this.isAttackedByCamel(sq, colors) ||
+      this.isAttackedByWildebeest(sq, colors)
+    );
   }
 
-  isAttackedByCamel(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors,
-      V.CAMEL, V.steps[V.CAMEL], "oneStep");
+  isAttackedByCamel(sq, colors) {
+    return this.isAttackedBySlideNJump(
+      sq,
+      colors,
+      V.CAMEL,
+      V.steps[V.CAMEL],
+      "oneStep"
+    );
   }
 
-  isAttackedByWildebeest(sq, colors)
-  {
-    return this.isAttackedBySlideNJump(sq, colors, V.WILDEBEEST,
-      V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
+  isAttackedByWildebeest(sq, colors) {
+    return this.isAttackedBySlideNJump(
+      sq,
+      colors,
+      V.WILDEBEEST,
+      V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
+      "oneStep"
+    );
   }
 
-  getCurrentScore()
-  {
-    if (this.atLeastOneMove()) // game not over
+  getCurrentScore() {
+    if (this.atLeastOneMove())
+      // game not over
       return "*";
 
     // No valid move: game is lost (stalemate is a win)
-    return (this.turn == "w" ? "0-1" : "1-0");
+    return this.turn == "w" ? "0-1" : "1-0";
   }
 
   static get VALUES() {
     return Object.assign(
       ChessRules.VALUES,
-      {'c': 3, 'w': 7} //experimental
+      { c: 3, w: 7 } //experimental
     );
   }
 
-  static get SEARCH_DEPTH() { return 2; }
+  static get SEARCH_DEPTH() {
+    return 2;
+  }
 
-  static GenRandInitFen()
-  {
-    let pieces = { "w": new Array(10), "b": new Array(10) };
-    for (let c of ["w","b"])
-    {
+  static GenRandInitFen() {
+    let pieces = { w: new Array(10), b: new Array(10) };
+    for (let c of ["w", "b"]) {
       let positions = ArrayFun.range(11);
 
       // Get random squares for bishops + camels (different colors)
-      let randIndexes = sample(ArrayFun.range(6), 2)
-        .map(i => { return 2*i; });
+      let randIndexes = sample(ArrayFun.range(6), 2).map(i => {
+        return 2 * i;
+      });
       let bishop1Pos = positions[randIndexes[0]];
       let camel1Pos = positions[randIndexes[1]];
       // The second bishop (camel) must be on a square of different color
-      let randIndexes_tmp = sample(ArrayFun.range(5), 2)
-        .map(i => { return 2*i+1; });
+      let randIndexes_tmp = sample(ArrayFun.range(5), 2).map(i => {
+        return 2 * i + 1;
+      });
       let bishop2Pos = positions[randIndexes_tmp[0]];
       let camel2Pos = positions[randIndexes_tmp[1]];
-      for (let idx of randIndexes.concat(randIndexes_tmp)
-        .sort((a,b) => { return b-a; })) //largest indices first
-      {
+      for (let idx of randIndexes.concat(randIndexes_tmp).sort((a, b) => {
+        return b - a;
+      })) {
+        //largest indices first
         positions.splice(idx, 1);
       }
 
@@ -276,21 +287,23 @@ export const VariantRules = class  WildebeestRules extends ChessRules
       let kingPos = positions[1];
       let rook2Pos = positions[2];
 
-      pieces[c][rook1Pos] = 'r';
-      pieces[c][knight1Pos] = 'n';
-      pieces[c][bishop1Pos] = 'b';
-      pieces[c][queenPos] = 'q';
-      pieces[c][camel1Pos] = 'c';
-      pieces[c][camel2Pos] = 'c';
-      pieces[c][wildebeestPos] = 'w';
-      pieces[c][kingPos] = 'k';
-      pieces[c][bishop2Pos] = 'b';
-      pieces[c][knight2Pos] = 'n';
-      pieces[c][rook2Pos] = 'r';
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][camel1Pos] = "c";
+      pieces[c][camel2Pos] = "c";
+      pieces[c][wildebeestPos] = "w";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
     }
-    return pieces["b"].join("") +
+    return (
+      pieces["b"].join("") +
       "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
       pieces["w"].join("").toUpperCase() +
-      " w 0 1111 -";
+      " w 0 1111 -"
+    );
   }
-}
+};
diff --git a/client/src/variants/Zen.js b/client/src/variants/Zen.js
index 256b80dc..f5bff8ff 100644
--- a/client/src/variants/Zen.js
+++ b/client/src/variants/Zen.js
@@ -1,26 +1,21 @@
 import { ChessRules } from "@/base_rules";
 
-export const VariantRules = class ZenRules extends ChessRules
-{
+export const VariantRules = class ZenRules extends ChessRules {
   // NOTE: enPassant, if enabled, would need to redefine carefully getEpSquare
-  static get HasEnpassant() { return false; }
+  static get HasEnpassant() {
+    return false;
+  }
 
   // TODO(?): some duplicated code in 2 next functions
-  getSlideNJumpMoves([x,y], steps, oneStep)
-  {
-    const color = this.getColor(x,y);
+  getSlideNJumpMoves([x, y], steps, oneStep) {
     let moves = [];
-    outerLoop:
-    for (let loop=0; loop<steps.length; loop++)
-    {
+    outerLoop: for (let loop = 0; loop < steps.length; loop++) {
       const step = steps[loop];
       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;
+      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];
       }
@@ -31,44 +26,49 @@ export const VariantRules = class ZenRules extends ChessRules
 
   // follow steps from x,y until something is met.
   // if met piece is opponent and same movement (asA): eat it!
-  findCaptures_aux([x,y], asA)
-  {
-    const color = this.getColor(x,y);
+  findCaptures_aux([x, y], asA) {
+    const color = this.getColor(x, y);
     var moves = [];
-    const steps = asA != V.PAWN
-      ? (asA==V.QUEEN ? V.steps[V.ROOK].concat(V.steps[V.BISHOP]) : V.steps[asA])
-      : color=='w' ? [[-1,-1],[-1,1]] : [[1,-1],[1,1]];
-    const oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
-    const lastRank = (color == 'w' ? 0 : V.size.x-1);
-    const promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
-    outerLoop:
-    for (let loop=0; loop<steps.length; loop++)
-    {
+    const steps =
+      asA != V.PAWN
+        ? asA == V.QUEEN
+          ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+          : V.steps[asA]
+        : color == "w"
+          ? [
+            [-1, -1],
+            [-1, 1]
+          ]
+          : [
+            [1, -1],
+            [1, 1]
+          ];
+    const oneStep = asA == V.KNIGHT || asA == V.PAWN; //we don't capture king
+    const lastRank = color == "w" ? 0 : V.size.x - 1;
+    const promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
+    outerLoop: for (let loop = 0; loop < steps.length; loop++) {
       const step = steps[loop];
       let i = x + step[0];
       let j = y + step[1];
-      while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
-      {
-        if (oneStep)
-          continue outerLoop;
+      while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+        if (oneStep) continue outerLoop;
         i += step[0];
         j += step[1];
       }
-      if (V.OnBoard(i,j) && this.getColor(i,j) == V.GetOppCol(color)
-        && this.getPiece(i,j) == asA)
-      {
+      if (
+        V.OnBoard(i, j) &&
+        this.getColor(i, j) == V.GetOppCol(color) &&
+        this.getPiece(i, j) == asA
+      ) {
         // eat!
-        if (this.getPiece(x,y) == V.PAWN && i == lastRank)
-        {
+        if (this.getPiece(x, y) == V.PAWN && i == lastRank) {
           // Special case of promotion:
           promotionPieces.forEach(p => {
-            moves.push(this.getBasicMove([x,y], [i,j], {c:color,p:p}));
+            moves.push(this.getBasicMove([x, y], [i, j], { c: color, p: p }));
           });
-        }
-        else
-        {
+        } else {
           // All other cases
-          moves.push(this.getBasicMove([x,y], [i,j]));
+          moves.push(this.getBasicMove([x, y], [i, j]));
         }
       }
     }
@@ -76,8 +76,7 @@ export const VariantRules = class ZenRules extends ChessRules
   }
 
   // Find possible captures from a square: look in every direction!
-  findCaptures(sq)
-  {
+  findCaptures(sq) {
     let moves = [];
 
     Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.PAWN));
@@ -89,96 +88,91 @@ export const VariantRules = class ZenRules extends ChessRules
     return moves;
   }
 
-  getPotentialPawnMoves([x,y])
-  {
-    const color = this.getColor(x,y);
+  getPotentialPawnMoves([x, y]) {
+    const color = this.getColor(x, y);
     let moves = [];
-    const [sizeX,sizeY] = [V.size.x,V.size.y];
-    const shift = (color == 'w' ? -1 : 1);
-    const startRank = (color == 'w' ? sizeY-2 : 1);
-    const firstRank = (color == 'w' ? sizeY-1 : 0);
-    const lastRank = (color == "w" ? 0 : sizeY-1);
-
-    if (x+shift != lastRank)
-    {
+    const sizeY = V.size.y;
+    const shift = color == "w" ? -1 : 1;
+    const startRank = color == "w" ? sizeY - 2 : 1;
+    const firstRank = color == "w" ? sizeY - 1 : 0;
+    const lastRank = color == "w" ? 0 : sizeY - 1;
+
+    if (x + shift != lastRank) {
       // Normal moves
-      if (this.board[x+shift][y] == V.EMPTY)
-      {
-        moves.push(this.getBasicMove([x,y], [x+shift,y]));
-        if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
-        {
+      if (this.board[x + shift][y] == V.EMPTY) {
+        moves.push(this.getBasicMove([x, y], [x + shift, y]));
+        if (
+          [startRank, firstRank].includes(x) &&
+          this.board[x + 2 * shift][y] == V.EMPTY
+        ) {
           //two squares jump
-          moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
+          moves.push(this.getBasicMove([x, y], [x + 2 * shift, y]));
         }
       }
-    }
-
-    else //promotion
-    {
-      let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+    } //promotion
+    else {
+      let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
       promotionPieces.forEach(p => {
         // Normal move
-        if (this.board[x+shift][y] == V.EMPTY)
-          moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
+        if (this.board[x + shift][y] == V.EMPTY)
+          moves.push(
+            this.getBasicMove([x, y], [x + shift, y], { c: color, p: p })
+          );
       });
     }
 
     // No en passant here
 
     // Add "zen" captures
-    Array.prototype.push.apply(moves, this.findCaptures([x,y]));
+    Array.prototype.push.apply(moves, this.findCaptures([x, y]));
 
     return moves;
   }
 
-  getPotentialRookMoves(sq)
-  {
+  getPotentialRookMoves(sq) {
     let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
     let captures = this.findCaptures(sq);
     return noCaptures.concat(captures);
   }
 
-  getPotentialKnightMoves(sq)
-  {
+  getPotentialKnightMoves(sq) {
     let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
     let captures = this.findCaptures(sq);
     return noCaptures.concat(captures);
   }
 
-  getPotentialBishopMoves(sq)
-  {
+  getPotentialBishopMoves(sq) {
     let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
     let captures = this.findCaptures(sq);
     return noCaptures.concat(captures);
   }
 
-  getPotentialQueenMoves(sq)
-  {
+  getPotentialQueenMoves(sq) {
     let noCaptures = this.getSlideNJumpMoves(
-      sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+    );
     let captures = this.findCaptures(sq);
     return noCaptures.concat(captures);
   }
 
-  getPotentialKingMoves(sq)
-  {
+  getPotentialKingMoves(sq) {
     // Initialize with normal moves
-    let noCaptures = this.getSlideNJumpMoves(sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+    let noCaptures = this.getSlideNJumpMoves(
+      sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
     let captures = this.findCaptures(sq);
     return noCaptures.concat(captures).concat(this.getCastleMoves(sq));
   }
 
-  getNotation(move)
-  {
+  getNotation(move) {
     // Recognize special moves first
-    if (move.appear.length == 2)
-    {
+    if (move.appear.length == 2) {
       // castle
-      if (move.end.y < move.start.y)
-        return "0-0-0";
-      else
-        return "0-0";
+      if (move.end.y < move.start.y) return "0-0-0";
+      return "0-0";
     }
 
     // Translate initial square (because pieces may fly unusually in this variant!)
@@ -189,40 +183,33 @@ export const VariantRules = class ZenRules extends ChessRules
 
     let notation = "";
     const piece = this.getPiece(move.start.x, move.start.y);
-    if (piece == V.PAWN)
-    {
+    if (piece == V.PAWN) {
       // pawn move (TODO: enPassant indication)
-      if (move.vanish.length > 1)
-      {
+      if (move.vanish.length > 1) {
         // capture
         notation = initialSquare + "x" + finalSquare;
-      }
-      else //no capture
-        notation = finalSquare;
-      if (piece != move.appear[0].p) //promotion
+      } //no capture
+      else notation = finalSquare;
+      if (piece != move.appear[0].p)
+        //promotion
         notation += "=" + move.appear[0].p.toUpperCase();
-    }
-
-    else
-    {
+    } else {
       // Piece movement
       notation = piece.toUpperCase();
-      if (move.vanish.length > 1)
-        notation += initialSquare + "x";
+      if (move.vanish.length > 1) notation += initialSquare + "x";
       notation += finalSquare;
     }
     return notation;
   }
 
-  static get VALUES()
-  {
+  static get VALUES() {
     return {
-      'p': 1,
-      'r': 3,
-      'n': 2,
-      'b': 2,
-      'q': 5,
-      'k': 1000
-    }
+      p: 1,
+      r: 3,
+      n: 2,
+      b: 2,
+      q: 5,
+      k: 1000
+    };
   }
-}
+};
diff --git a/client/src/views/About.vue b/client/src/views/About.vue
index a685f3c8..711cc0b0 100644
--- a/client/src/views/About.vue
+++ b/client/src/views/About.vue
@@ -8,15 +8,18 @@ main
 <script>
 import { store } from "@/store";
 export default {
-  name: 'my-about',
+  name: "my-about",
   computed: {
     content: function() {
-      return require("raw-loader!@/translations/about/" + store.state.lang + ".pug")
-        // Next two lines fix a weird issue after last update (2019-11)
-        .replace(/\\[n"]/g, " ")
-        .replace('module.exports = "', '').replace(/"$/, "");
-    },
-  },
+      return (
+        require("raw-loader!@/translations/about/" + store.state.lang + ".pug")
+          // Next two lines fix a weird issue after last update (2019-11)
+          .replace(/\\[n"]/g, " ")
+          .replace('module.exports = "', "")
+          .replace(/"$/, "")
+      );
+    }
+  }
 };
 </script>
 
diff --git a/client/src/views/Analyse.vue b/client/src/views/Analyse.vue
index 035cd089..5d22306e 100644
--- a/client/src/views/Analyse.vue
+++ b/client/src/views/Analyse.vue
@@ -11,46 +11,43 @@ main
 <script>
 import BaseGame from "@/components/BaseGame.vue";
 import { store } from "@/store";
-import { ArrayFun } from "@/utils/array";
 export default {
-  name: 'my-analyse',
+  name: "my-analyse",
   components: {
-    BaseGame,
+    BaseGame
   },
   // gameRef: to find the game in (potentially remote) storage
   data: function() {
     return {
       st: store.state,
-      gameRef: { //given in URL (rid = remote ID)
+      gameRef: {
+        //given in URL (rid = remote ID)
         vname: "",
         fen: ""
       },
       game: {
-        players:[{name:"Analyse"},{name:"Analyse"}],
-        mode: "analyze",
+        players: [{ name: "Analyse" }, { name: "Analyse" }],
+        mode: "analyze"
       },
       vr: null, //"variant rules" object initialized from FEN
-      curFen: "",
+      curFen: ""
       //people: [], //players + observers //TODO later: interactive analyze...
     };
   },
   watch: {
     // NOTE: no watcher for $route change, because if fenStart doesn't change
     // then it doesn't trigger BaseGame.re_init() and the result is weird.
-    "vr.movesCount": function(fen) {
+    "vr.movesCount": function() {
       this.curFen = this.vr.getFen();
       this.adjustFenSize();
-    },
+    }
   },
   created: function() {
     this.gameRef.vname = this.$route.params["vname"];
-    if (this.gameRef.vname == "Dark")
-    {
+    if (this.gameRef.vname == "Dark") {
       alert(this.st.tr["Analyse in Dark mode makes no sense!"]);
       history.back(); //or this.$router.go(-1)
-    }
-    else
-    {
+    } else {
       this.gameRef.fen = this.$route.query["fen"].replace(/_/g, " ");
       this.initialize();
     }
@@ -79,7 +76,7 @@ export default {
     gotoFen: function() {
       this.gameRef.fen = this.curFen;
       this.loadGame();
-    },
-  },
+    }
+  }
 };
 </script>
diff --git a/client/src/views/Auth.vue b/client/src/views/Auth.vue
index a750b3e3..a44687c1 100644
--- a/client/src/views/Auth.vue
+++ b/client/src/views/Auth.vue
@@ -10,33 +10,31 @@ main
 import { store } from "@/store";
 import { ajax } from "@/utils/ajax";
 export default {
-  name: 'my-auth',
+  name: "my-auth",
   data: function() {
     return {
       st: store.state,
-      errmsg: "",
+      errmsg: ""
     };
   },
   created: function() {
-		ajax(
-			"/authenticate",
-			"GET",
-			{token: this.$route.params["token"]},
-			(res) => {
-				if (!res.errmsg) //if not already logged in
-				{
-					this.st.user.id = res.id;
-					this.st.user.name = res.name;
-					this.st.user.email = res.email;
-					this.st.user.notify = res.notify;
-					localStorage["myname"] = res.name;
-					localStorage["myid"] = res.id;
-				}
-				else
-          this.errmsg = res.errmsg;
-			}
+    ajax(
+      "/authenticate",
+      "GET",
+      { token: this.$route.params["token"] },
+      res => {
+        if (!res.errmsg) {
+          //if not already logged in
+          this.st.user.id = res.id;
+          this.st.user.name = res.name;
+          this.st.user.email = res.email;
+          this.st.user.notify = res.notify;
+          localStorage["myname"] = res.name;
+          localStorage["myid"] = res.id;
+        } else this.errmsg = res.errmsg;
+      }
     );
-  },
+  }
 };
 </script>
 
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index 5255cf43..a0e264ed 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -42,28 +42,29 @@ import { GameStorage } from "@/utils/gameStorage";
 import { ppt } from "@/utils/datetime";
 import { extractTime } from "@/utils/timeControl";
 import { getRandString } from "@/utils/alea";
-import { ArrayFun } from "@/utils/array";
 import { processModalClick } from "@/utils/modalClick";
 import { getScoreMessage } from "@/utils/scoring";
 import params from "@/parameters";
 export default {
-  name: 'my-game',
+  name: "my-game",
   components: {
     BaseGame,
-    Chat,
+    Chat
   },
   // gameRef: to find the game in (potentially remote) storage
   data: function() {
     return {
       st: store.state,
-      gameRef: { //given in URL (rid = remote ID)
+      gameRef: {
+        //given in URL (rid = remote ID)
         id: "",
         rid: ""
       },
-      game: { //passed to BaseGame
-        players:[{name:""},{name:""}],
+      game: {
+        //passed to BaseGame
+        players: [{ name: "" }, { name: "" }],
         chats: [],
-        rendered: false,
+        rendered: false
       },
       virtualClocks: [0, 0], //initialized with true game.clocks
       vr: null, //"variant rules" object initialized from FEN
@@ -76,46 +77,54 @@ export default {
       connexionString: "",
       // Related to (killing of) self multi-connects:
       newConnect: {},
-      killed: {},
+      killed: {}
     };
   },
   watch: {
-    "$route": function(to, from) {
+    $route: function(to) {
       this.gameRef.id = to.params["id"];
       this.gameRef.rid = to.query["rid"];
       this.loadGame();
-    },
+    }
   },
   // NOTE: some redundant code with Hall.vue (mostly related to people array)
   created: function() {
     // Always add myself to players' list
     const my = this.st.user;
-    this.$set(this.people, my.sid, {id:my.id, name:my.name});
+    this.$set(this.people, my.sid, { id: my.id, name: my.name });
     this.gameRef.id = this.$route.params["id"];
     this.gameRef.rid = this.$route.query["rid"]; //may be undefined
     // Initialize connection
-    this.connexionString = params.socketUrl +
-      "/?sid=" + this.st.user.sid +
-      "&tmpId=" + getRandString() +
-      "&page=" + encodeURIComponent(this.$route.path);
+    this.connexionString =
+      params.socketUrl +
+      "/?sid=" +
+      this.st.user.sid +
+      "&tmpId=" +
+      getRandString() +
+      "&page=" +
+      encodeURIComponent(this.$route.path);
     this.conn = new WebSocket(this.connexionString);
     this.conn.onmessage = this.socketMessageListener;
     this.conn.onclose = this.socketCloseListener;
     // Socket init required before loading remote game:
-    const socketInit = (callback) => {
-      if (!!this.conn && this.conn.readyState == 1) //1 == OPEN state
+    const socketInit = callback => {
+      if (!!this.conn && this.conn.readyState == 1)
+        //1 == OPEN state
         callback();
-      else //socket not ready yet (initial loading)
-      {
+      //socket not ready yet (initial loading)
+      else {
         // NOTE: it's important to call callback without arguments,
         // otherwise first arg is Websocket object and loadGame fails.
-        this.conn.onopen = () => { return callback() };
+        this.conn.onopen = () => {
+          return callback();
+        };
       }
     };
-    if (!this.gameRef.rid) //game stored locally or on server
+    if (!this.gameRef.rid)
+      //game stored locally or on server
       this.loadGame(null, () => socketInit(this.roomInit));
-    else //game stored remotely: need socket to retrieve it
-    {
+    //game stored remotely: need socket to retrieve it
+    else {
       // NOTE: the callback "roomInit" will be lost, so we don't provide it.
       // --> It will be given when receiving "fullgame" socket event.
       // A more general approach would be to store it somewhere.
@@ -123,8 +132,9 @@ export default {
     }
   },
   mounted: function() {
-    document.getElementById("chatWrap").addEventListener(
-      "click", processModalClick);
+    document
+      .getElementById("chatWrap")
+      .addEventListener("click", processModalClick);
   },
   beforeDestroy: function() {
     this.send("disconnect");
@@ -137,14 +147,8 @@ export default {
       this.send("pollclients");
     },
     send: function(code, obj) {
-      if (!!this.conn)
-      {
-        this.conn.send(JSON.stringify(
-          Object.assign(
-            {code: code},
-            obj,
-          )
-        ));
+      if (this.conn) {
+        this.conn.send(JSON.stringify(Object.assign({ code: code }, obj)));
       }
     },
     isConnected: function(index) {
@@ -153,38 +157,38 @@ export default {
       if (this.st.user.sid == player.sid || this.st.user.id == player.uid)
         return true;
       // Try to find a match in people:
-      return Object.keys(this.people).some(sid => sid == player.sid) ||
-        Object.values(this.people).some(p => p.id == player.uid);
+      return (
+        Object.keys(this.people).some(sid => sid == player.sid) ||
+        Object.values(this.people).some(p => p.id == player.uid)
+      );
     },
     socketMessageListener: function(msg) {
-      if (!this.conn)
-        return;
+      if (!this.conn) return;
       const data = JSON.parse(msg.data);
-      switch (data.code)
-      {
+      switch (data.code) {
         case "pollclients":
           data.sockIds.forEach(sid => {
-            this.$set(this.people, sid, {id:0, name:""});
-            if (sid != this.st.user.sid)
-            {
-              this.send("askidentity", {target:sid});
+            this.$set(this.people, sid, { id: 0, name: "" });
+            if (sid != this.st.user.sid) {
+              this.send("askidentity", { target: sid });
               // Ask potentially missed last state, if opponent and I play
-              if (!!this.game.mycolor
-                && this.game.type == "live" && this.game.score == "*"
-                && this.game.players.some(p => p.sid == sid))
-              {
-                this.send("asklastate", {target:sid});
+              if (
+                !!this.game.mycolor &&
+                this.game.type == "live" &&
+                this.game.score == "*" &&
+                this.game.players.some(p => p.sid == sid)
+              ) {
+                this.send("asklastate", { target: sid });
               }
             }
           });
           break;
         case "connect":
           if (!this.people[data.from])
-            this.$set(this.people, data.from, {name:"", id:0});
-          if (!this.people[data.from].name)
-          {
+            this.$set(this.people, data.from, { name: "", id: 0 });
+          if (!this.people[data.from].name) {
             this.newConnect[data.from] = true; //for self multi-connects tests
-            this.send("askidentity", {target:data.from});
+            this.send("askidentity", { target: data.from });
           }
           break;
         case "disconnect":
@@ -199,40 +203,39 @@ export default {
           //this.conn.close();
           this.conn = null;
           break;
-        case "askidentity":
-        {
+        case "askidentity": {
           // Request for identification (TODO: anonymous shouldn't need to reply)
           const me = {
             // Decompose to avoid revealing email
             name: this.st.user.name,
             sid: this.st.user.sid,
-            id: this.st.user.id,
+            id: this.st.user.id
           };
-          this.send("identity", {data:me, target:data.from});
+          this.send("identity", { data: me, target: data.from });
           break;
         }
-        case "identity":
-        {
+        case "identity": {
           const user = data.data;
-          if (!!user.name) //otherwise anonymous
-          {
+          if (user.name) {
+            //otherwise anonymous
             // If I multi-connect, kill current connexion if no mark (I'm older)
-            if (this.newConnect[user.sid] && user.id > 0
-              && user.id == this.st.user.id && user.sid != this.st.user.sid)
-            {
-              if (!this.killed[this.st.user.sid])
-              {
-                this.send("killme", {sid:this.st.user.sid});
+            if (
+              this.newConnect[user.sid] &&
+              user.id > 0 &&
+              user.id == this.st.user.id &&
+              user.sid != this.st.user.sid
+            ) {
+              if (!this.killed[this.st.user.sid]) {
+                this.send("killme", { sid: this.st.user.sid });
                 this.killed[this.st.user.sid] = true;
               }
             }
-            if (user.sid != this.st.user.sid) //I already know my identity...
-            {
-              this.$set(this.people, user.sid,
-                {
-                  id: user.id,
-                  name: user.name,
-                });
+            if (user.sid != this.st.user.sid) {
+              //I already know my identity...
+              this.$set(this.people, user.sid, {
+                id: user.id,
+                name: user.name
+              });
             }
           }
           delete this.newConnect[user.sid];
@@ -240,9 +243,10 @@ export default {
         }
         case "askgame":
           // Send current (live) game if not asked by any of the players
-          if (this.game.type == "live"
-            && this.game.players.every(p => p.sid != data.from[0]))
-          {
+          if (
+            this.game.type == "live" &&
+            this.game.players.every(p => p.sid != data.from[0])
+          ) {
             const myGame = {
               id: this.game.id,
               fen: this.game.fen,
@@ -250,13 +254,13 @@ export default {
               vid: this.game.vid,
               cadence: this.game.cadence,
               score: this.game.score,
-              rid: this.st.user.sid, //useful in Hall if I'm an observer
+              rid: this.st.user.sid //useful in Hall if I'm an observer
             };
-            this.send("game", {data:myGame, target:data.from});
+            this.send("game", { data: myGame, target: data.from });
           }
           break;
         case "askfullgame":
-          this.send("fullgame", {data:this.game, target:data.from});
+          this.send("fullgame", { data: this.game, target: data.from });
           break;
         case "fullgame":
           // Callback "roomInit" to poll clients only after game is loaded
@@ -264,46 +268,48 @@ export default {
           break;
         case "asklastate":
           // Sending last state if I played a move or score != "*"
-          if ((this.game.moves.length > 0 && this.vr.turn != this.game.mycolor)
-              || this.game.score != "*" || this.drawOffer == "sent")
-          {
+          if (
+            (this.game.moves.length > 0 && this.vr.turn != this.game.mycolor) ||
+            this.game.score != "*" ||
+            this.drawOffer == "sent"
+          ) {
             // Send our "last state" informations to opponent
             const L = this.game.moves.length;
-            const myIdx = ["w","b"].indexOf(this.game.mycolor);
+            const myIdx = ["w", "b"].indexOf(this.game.mycolor);
             const myLastate = {
               // NOTE: lastMove (when defined) includes addTime
-              lastMove: (L>0 ? this.game.moves[L-1] : undefined),
+              lastMove: L > 0 ? this.game.moves[L - 1] : undefined,
               // Since we played a move (or abort or resign),
               // only drawOffer=="sent" is possible
               drawSent: this.drawOffer == "sent",
               score: this.game.score,
               movesCount: L,
-              initime: this.game.initime[1-myIdx], //relevant only if I played
+              initime: this.game.initime[1 - myIdx] //relevant only if I played
             };
-            this.send("lastate", {data:myLastate, target:data.from});
+            this.send("lastate", { data: myLastate, target: data.from });
           }
           break;
         case "lastate": //got opponent infos about last move
           this.lastate = data.data;
-          if (this.game.rendered) //game is rendered (Board component)
+          if (this.game.rendered)
+            //game is rendered (Board component)
             this.processLastate();
           //else: will be processed when game is ready
           break;
-        case "newmove":
-        {
+        case "newmove": {
           const move = data.data;
-          if (!!move.cancelDrawOffer) //opponent refuses draw
-          {
+          if (move.cancelDrawOffer) {
+            //opponent refuses draw
             this.drawOffer = "";
             // NOTE for corr games: drawOffer reset by player in turn
             if (this.game.type == "live" && !!this.game.mycolor)
-              GameStorage.update(this.gameRef.id, {drawOffer: ""});
+              GameStorage.update(this.gameRef.id, { drawOffer: "" });
           }
           this.$set(this.game, "moveToPlay", move);
           break;
         }
         case "resign":
-          this.gameOver(data.side=="b" ? "1-0" : "0-1", "Resign");
+          this.gameOver(data.side == "b" ? "1-0" : "0-1", "Resign");
           break;
         case "abort":
           this.gameOver("?", "Abort");
@@ -324,138 +330,129 @@ export default {
     },
     socketCloseListener: function() {
       this.conn = new WebSocket(this.connexionString);
-      this.conn.addEventListener('message', this.socketMessageListener);
-      this.conn.addEventListener('close', this.socketCloseListener);
+      this.conn.addEventListener("message", this.socketMessageListener);
+      this.conn.addEventListener("close", this.socketCloseListener);
     },
     // lastate was received, but maybe game wasn't ready yet:
     processLastate: function() {
       const data = this.lastate;
       this.lastate = undefined; //security...
       const L = this.game.moves.length;
-      if (data.movesCount > L)
-      {
+      if (data.movesCount > L) {
         // Just got last move from him
-        this.$set(this.game, "moveToPlay", Object.assign({initime: data.initime}, data.lastMove));
+        this.$set(
+          this.game,
+          "moveToPlay",
+          Object.assign({ initime: data.initime }, data.lastMove)
+        );
       }
-      if (data.drawSent)
-        this.drawOffer = "received";
-      if (data.score != "*")
-      {
+      if (data.drawSent) this.drawOffer = "received";
+      if (data.score != "*") {
         this.drawOffer = "";
-        if (this.game.score == "*")
-          this.gameOver(data.score);
+        if (this.game.score == "*") this.gameOver(data.score);
       }
     },
     clickDraw: function() {
-      if (!this.game.mycolor)
-        return; //I'm just spectator
-      if (["received","threerep"].includes(this.drawOffer))
-      {
-        if (!confirm(this.st.tr["Accept draw?"]))
-          return;
-        const message = (this.drawOffer == "received"
-          ? "Mutual agreement"
-          : "Three repetitions");
-        this.send("draw", {data:message});
+      if (!this.game.mycolor) return; //I'm just spectator
+      if (["received", "threerep"].includes(this.drawOffer)) {
+        if (!confirm(this.st.tr["Accept draw?"])) return;
+        const message =
+          this.drawOffer == "received"
+            ? "Mutual agreement"
+            : "Three repetitions";
+        this.send("draw", { data: message });
         this.gameOver("1/2", message);
-      }
-      else if (this.drawOffer == "") //no effect if drawOffer == "sent"
-      {
-        if (this.game.mycolor != this.vr.turn)
-          return alert(this.st.tr["Draw offer only in your turn"]);
-        if (!confirm(this.st.tr["Offer draw?"]))
+      } else if (this.drawOffer == "") {
+        //no effect if drawOffer == "sent"
+        if (this.game.mycolor != this.vr.turn) {
+          alert(this.st.tr["Draw offer only in your turn"]);
           return;
+        }
+        if (!confirm(this.st.tr["Offer draw?"])) return;
         this.drawOffer = "sent";
         this.send("drawoffer");
-        GameStorage.update(this.gameRef.id, {drawOffer: this.game.mycolor});
+        GameStorage.update(this.gameRef.id, { drawOffer: this.game.mycolor });
       }
     },
     abortGame: function() {
-      if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"]))
-        return;
+      if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"])) return;
       this.gameOver("?", "Abort");
       this.send("abort");
     },
-    resign: function(e) {
+    resign: function() {
       if (!this.game.mycolor || !confirm(this.st.tr["Resign the game?"]))
         return;
-      this.send("resign", {data:this.game.mycolor});
-      this.gameOver(this.game.mycolor=="w" ? "0-1" : "1-0", "Resign");
+      this.send("resign", { data: this.game.mycolor });
+      this.gameOver(this.game.mycolor == "w" ? "0-1" : "1-0", "Resign");
     },
     // 3 cases for loading a game:
     //  - from indexedDB (running or completed live game I play)
     //  - from server (one correspondance game I play[ed] or not)
     //  - from remote peer (one live game I don't play, finished or not)
     loadGame: function(game, callback) {
-      const afterRetrieval = async (game) => {
+      const afterRetrieval = async game => {
         const vModule = await import("@/variants/" + game.vname + ".js");
         window.V = vModule.VariantRules;
         this.vr = new V(game.fen);
-        const gtype = (game.cadence.indexOf('d') >= 0 ? "corr" : "live");
+        const gtype = game.cadence.indexOf("d") >= 0 ? "corr" : "live";
         const tc = extractTime(game.cadence);
         const myIdx = game.players.findIndex(p => {
           return p.sid == this.st.user.sid || p.uid == this.st.user.id;
         });
-        const mycolor = [undefined,"w","b"][myIdx+1]; //undefined for observers
-        if (!game.chats)
-          game.chats = []; //live games don't have chat history
-        if (gtype == "corr")
-        {
-          if (game.players[0].color == "b")
-          {
+        const mycolor = [undefined, "w", "b"][myIdx + 1]; //undefined for observers
+        if (!game.chats) game.chats = []; //live games don't have chat history
+        if (gtype == "corr") {
+          if (game.players[0].color == "b") {
             // Adopt the same convention for live and corr games: [0] = white
-            [ game.players[0], game.players[1] ] =
-              [ game.players[1], game.players[0] ];
+            [game.players[0], game.players[1]] = [
+              game.players[1],
+              game.players[0]
+            ];
           }
           // corr game: needs to compute the clocks + initime
           // NOTE: clocks in seconds, initime in milliseconds
           game.clocks = [tc.mainTime, tc.mainTime];
-          game.moves.sort((m1,m2) => m1.idx - m2.idx); //in case of
-          if (game.score == "*") //otherwise no need to bother with time
-          {
+          game.moves.sort((m1, m2) => m1.idx - m2.idx); //in case of
+          if (game.score == "*") {
+            //otherwise no need to bother with time
             game.initime = [0, 0];
             const L = game.moves.length;
-            if (L >= 3)
-            {
+            if (L >= 3) {
               let addTime = [0, 0];
-              for (let i=2; i<L; i++)
-              {
-                addTime[i%2] += tc.increment -
-                  (game.moves[i].played - game.moves[i-1].played) / 1000;
+              for (let i = 2; i < L; i++) {
+                addTime[i % 2] +=
+                  tc.increment -
+                  (game.moves[i].played - game.moves[i - 1].played) / 1000;
               }
-              for (let i=0; i<=1; i++)
-                game.clocks[i] += addTime[i];
+              for (let i = 0; i <= 1; i++) game.clocks[i] += addTime[i];
             }
-            if (L >= 1)
-              game.initime[L%2] = game.moves[L-1].played;
+            if (L >= 1) game.initime[L % 2] = game.moves[L - 1].played;
           }
-          const reformattedMoves = game.moves.map( (m) => {
+          const reformattedMoves = game.moves.map(m => {
             const s = m.squares;
             return {
               appear: s.appear,
               vanish: s.vanish,
               start: s.start,
-              end: s.end,
+              end: s.end
             };
           });
           // Sort chat messages from newest to oldest
-          game.chats.sort( (c1,c2) => { return c2.added - c1.added; });
-          if (myIdx >= 0 && game.chats.length > 0)
-          {
+          game.chats.sort((c1, c2) => {
+            return c2.added - c1.added;
+          });
+          if (myIdx >= 0 && game.chats.length > 0) {
             // TODO: group multi-moves into an array, to deduce color from index
             // and not need this (also repeated in BaseGame::re_setVariables())
             let vr_tmp = new V(game.fenStart); //vr is already at end of game
-            for (let i=0; i<reformattedMoves.length; i++)
-            {
+            for (let i = 0; i < reformattedMoves.length; i++) {
               game.moves[i].color = vr_tmp.turn;
               vr_tmp.play(reformattedMoves[i]);
             }
             // Blue background on chat button if last chat message arrived after my last move.
             let dtLastMove = 0;
-            for (let midx = game.moves.length-1; midx >= 0; midx--)
-            {
-              if (game.moves[midx].color == mycolor)
-              {
+            for (let midx = game.moves.length - 1; midx >= 0; midx--) {
+              if (game.moves[midx].color == mycolor) {
                 dtLastMove = game.moves[midx].played;
                 break;
               }
@@ -466,44 +463,42 @@ export default {
           // Now that we used idx and played, re-format moves as for live games
           game.moves = reformattedMoves;
         }
-        if (gtype == "live" && game.clocks[0] < 0) //game unstarted
-        {
+        if (gtype == "live" && game.clocks[0] < 0) {
+          //game unstarted
           game.clocks = [tc.mainTime, tc.mainTime];
-          if (game.score == "*")
-          {
+          if (game.score == "*") {
             game.initime[0] = Date.now();
-            if (myIdx >= 0)
-            {
+            if (myIdx >= 0) {
               // I play in this live game; corr games don't have clocks+initime
-              GameStorage.update(game.id,
-              {
+              GameStorage.update(game.id, {
                 clocks: game.clocks,
-                initime: game.initime,
+                initime: game.initime
               });
             }
           }
         }
-        if (!!game.drawOffer)
-        {
-          if (game.drawOffer == "t") //three repetitions
+        if (game.drawOffer) {
+          if (game.drawOffer == "t")
+            //three repetitions
             this.drawOffer = "threerep";
-          else
-          {
-            if (myIdx < 0)
-              this.drawOffer = "received"; //by any of the players
-            else
-            {
+          else {
+            if (myIdx < 0) this.drawOffer = "received";
+            //by any of the players
+            else {
               // I play in this game:
-              if ((game.drawOffer == "w" && myIdx==0) || (game.drawOffer=="b" && myIdx==1))
+              if (
+                (game.drawOffer == "w" && myIdx == 0) ||
+                (game.drawOffer == "b" && myIdx == 1)
+              )
                 this.drawOffer = "sent";
-              else //all other cases
-                this.drawOffer = "received";
+              //all other cases
+              else this.drawOffer = "received";
             }
           }
         }
-        if (!!game.scoreMsg)
-          game.scoreMsg = this.st.tr[game.scoreMsg]; //stored in english
-        this.game = Object.assign({},
+        if (game.scoreMsg) game.scoreMsg = this.st.tr[game.scoreMsg]; //stored in english
+        this.game = Object.assign(
+          {},
           game,
           // NOTE: assign mycolor here, since BaseGame could also be VS computer
           {
@@ -512,16 +507,15 @@ export default {
             mycolor: mycolor,
             // opponent sid not strictly required (or available), but easier
             // at least oppsid or oppid is available anyway:
-            oppsid: (myIdx < 0 ? undefined : game.players[1-myIdx].sid),
-            oppid: (myIdx < 0 ? undefined : game.players[1-myIdx].uid),
+            oppsid: myIdx < 0 ? undefined : game.players[1 - myIdx].sid,
+            oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].uid
           }
         );
         this.re_setClocks();
         this.$nextTick(() => {
           this.game.rendered = true;
           // Did lastate arrive before game was rendered?
-          if (!!this.lastate)
-            this.processLastate();
+          if (this.lastate) this.processLastate();
         });
         this.repeat = {}; //reset: scan past moves' FEN:
         let repIdx = 0;
@@ -529,74 +523,84 @@ export default {
         let vr_tmp = new V(game.fenStart);
         game.moves.forEach(m => {
           vr_tmp.play(m);
-          const fenObj = V.ParseFen( vr_tmp.getFen() );
+          const fenObj = V.ParseFen(vr_tmp.getFen());
           repIdx = fenObj.position + "_" + fenObj.turn;
-          if (!!fenObj.flags)
-            repIdx += "_" + fenObj.flags;
-          this.repeat[repIdx] = (!!this.repeat[repIdx]
-            ? this.repeat[repIdx]+1
-            : 1);
+          if (fenObj.flags) repIdx += "_" + fenObj.flags;
+          this.repeat[repIdx] = this.repeat[repIdx]
+            ? this.repeat[repIdx] + 1
+            : 1;
         });
-        if (this.repeat[repIdx] >= 3)
-          this.drawOffer = "threerep";
-        if (!!callback)
-          callback();
+        if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
+        if (callback) callback();
       };
-      if (!!game)
-        return afterRetrieval(game);
-      if (!!this.gameRef.rid)
-      {
-        // Remote live game: forgetting about callback func... (TODO: design)
-        this.send("askfullgame", {target:this.gameRef.rid});
+      if (game) {
+        afterRetrieval(game);
+        return;
       }
-      else
-      {
+      if (this.gameRef.rid) {
+        // Remote live game: forgetting about callback func... (TODO: design)
+        this.send("askfullgame", { target: this.gameRef.rid });
+      } else {
         // Local or corr game
         GameStorage.get(this.gameRef.id, afterRetrieval);
       }
     },
     re_setClocks: function() {
-      if (this.game.moves.length < 2 || this.game.score != "*")
-      {
+      if (this.game.moves.length < 2 || this.game.score != "*") {
         // 1st move not completed yet, or game over: freeze time
         this.virtualClocks = this.game.clocks.map(s => ppt(s));
         return;
       }
       const currentTurn = this.vr.turn;
-      const colorIdx = ["w","b"].indexOf(currentTurn);
-      let countdown = this.game.clocks[colorIdx] -
-        (Date.now() - this.game.initime[colorIdx])/1000;
-      this.virtualClocks = [0,1].map(i => {
-        const removeTime = i == colorIdx
-          ? (Date.now() - this.game.initime[colorIdx])/1000
-          : 0;
+      const colorIdx = ["w", "b"].indexOf(currentTurn);
+      let countdown =
+        this.game.clocks[colorIdx] -
+        (Date.now() - this.game.initime[colorIdx]) / 1000;
+      this.virtualClocks = [0, 1].map(i => {
+        const removeTime =
+          i == colorIdx ? (Date.now() - this.game.initime[colorIdx]) / 1000 : 0;
         return ppt(this.game.clocks[i] - removeTime);
       });
       let clockUpdate = setInterval(() => {
-        if (countdown < 0 || this.vr.turn != currentTurn || this.game.score != "*")
-        {
+        if (
+          countdown < 0 ||
+          this.vr.turn != currentTurn ||
+          this.game.score != "*"
+        ) {
           clearInterval(clockUpdate);
           if (countdown < 0)
-            this.gameOver(this.vr.turn=="w" ? "0-1" : "1-0", this.st.tr["Time"]);
-        }
-        else
-          this.$set(this.virtualClocks, colorIdx, ppt(Math.max(0, --countdown)));
+            this.gameOver(
+              this.vr.turn == "w" ? "0-1" : "1-0",
+              this.st.tr["Time"]
+            );
+        } else
+          this.$set(
+            this.virtualClocks,
+            colorIdx,
+            ppt(Math.max(0, --countdown))
+          );
       }, 1000);
     },
     // Post-process a move (which was just played in BaseGame)
     processMove: function(move) {
-      if (this.game.type == "corr" && move.color == this.game.mycolor)
-      {
-        if (!confirm(this.st.tr["Move played:"] + " " + move.notation + "\n" + this.st.tr["Are you sure?"]))
-        {
-          return this.$set(this.game, "moveToUndo", move);
+      if (this.game.type == "corr" && move.color == this.game.mycolor) {
+        if (
+          !confirm(
+            this.st.tr["Move played:"] +
+              " " +
+              move.notation +
+              "\n" +
+              this.st.tr["Are you sure?"]
+          )
+        ) {
+          this.$set(this.game, "moveToUndo", move);
+          return;
         }
       }
-      const colorIdx = ["w","b"].indexOf(move.color);
-      const nextIdx = ["w","b"].indexOf(this.vr.turn);
+      const colorIdx = ["w", "b"].indexOf(move.color);
+      const nextIdx = ["w", "b"].indexOf(this.vr.turn);
       // https://stackoverflow.com/a/38750895
-      if (!!this.game.mycolor)
-      {
+      if (this.game.mycolor) {
         const allowed_fields = ["appear", "vanish", "start", "end"];
         // NOTE: 'var' to see this variable outside this block
         var filtered_move = Object.keys(move)
@@ -608,28 +612,24 @@ export default {
       }
       // Send move ("newmove" event) to people in the room (if our turn)
       let addTime = 0;
-      if (move.color == this.game.mycolor)
-      {
-        if (this.drawOffer == "received") //I refuse draw
+      if (move.color == this.game.mycolor) {
+        if (this.drawOffer == "received")
+          //I refuse draw
           this.drawOffer = "";
-        if (this.game.moves.length >= 2) //after first move
-        {
+        if (this.game.moves.length >= 2) {
+          //after first move
           const elapsed = Date.now() - this.game.initime[colorIdx];
           // elapsed time is measured in milliseconds
-          addTime = this.game.increment - elapsed/1000;
+          addTime = this.game.increment - elapsed / 1000;
         }
-        const sendMove = Object.assign({},
-          filtered_move,
-          {
-            addTime: addTime,
-            cancelDrawOffer: this.drawOffer=="",
-          });
-        this.send("newmove", {data: sendMove});
+        const sendMove = Object.assign({}, filtered_move, {
+          addTime: addTime,
+          cancelDrawOffer: this.drawOffer == ""
+        });
+        this.send("newmove", { data: sendMove });
         // (Add)Time indication: useful in case of lastate infos requested
         move.addTime = addTime;
-      }
-      else
-        addTime = move.addTime; //supposed transmitted
+      } else addTime = move.addTime; //supposed transmitted
       // Update current game object:
       this.game.moves.push(move);
       this.game.fen = move.fen;
@@ -640,23 +640,18 @@ export default {
       // If repetition detected, consider that a draw offer was received:
       const fenObj = V.ParseFen(move.fen);
       let repIdx = fenObj.position + "_" + fenObj.turn;
-      if (!!fenObj.flags)
-        repIdx += "_" + fenObj.flags;
-      this.repeat[repIdx] = (!!this.repeat[repIdx]
-        ? this.repeat[repIdx]+1
-        : 1);
-      if (this.repeat[repIdx] >= 3)
-        this.drawOffer = "threerep";
-      else if (this.drawOffer == "threerep")
-        this.drawOffer = "";
+      if (fenObj.flags) repIdx += "_" + fenObj.flags;
+      this.repeat[repIdx] = this.repeat[repIdx] ? this.repeat[repIdx] + 1 : 1;
+      if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
+      else if (this.drawOffer == "threerep") this.drawOffer = "";
       // Since corr games are stored at only one location, update should be
       // done only by one player for each move:
-      if (!!this.game.mycolor &&
-        (this.game.type == "live" || move.color == this.game.mycolor))
-      {
+      if (
+        !!this.game.mycolor &&
+        (this.game.type == "live" || move.color == this.game.mycolor)
+      ) {
         let drawCode = "";
-        switch (this.drawOffer)
-        {
+        switch (this.drawOffer) {
           case "threerep":
             drawCode = "t";
             break;
@@ -667,29 +662,24 @@ export default {
             drawCode = this.vr.turn;
             break;
         }
-        if (this.game.type == "corr")
-        {
-          GameStorage.update(this.gameRef.id,
-          {
+        if (this.game.type == "corr") {
+          GameStorage.update(this.gameRef.id, {
             fen: move.fen,
-            move:
-            {
+            move: {
               squares: filtered_move,
               played: Date.now(),
-              idx: this.game.moves.length - 1,
+              idx: this.game.moves.length - 1
             },
-            drawOffer: drawCode || "n", //"n" for "None" to force reset (otherwise it's ignored)
+            drawOffer: drawCode || "n" //"n" for "None" to force reset (otherwise it's ignored)
           });
-        }
-        else //live
-        {
-          GameStorage.update(this.gameRef.id,
-          {
+        } //live
+        else {
+          GameStorage.update(this.gameRef.id, {
             fen: move.fen,
             move: filtered_move,
             clocks: this.game.clocks,
             initime: this.game.initime,
-            drawOffer: drawCode,
+            drawOffer: drawCode
           });
         }
       }
@@ -699,28 +689,30 @@ export default {
       document.getElementById("chatBtn").classList.remove("somethingnew");
     },
     processChat: function(chat) {
-      this.send("newchat", {data:chat});
+      this.send("newchat", { data: chat });
       // NOTE: anonymous chats in corr games are not stored on server (TODO?)
       if (this.game.type == "corr" && this.st.user.id > 0)
-        GameStorage.update(this.gameRef.id, {chat: chat});
+        GameStorage.update(this.gameRef.id, { chat: chat });
     },
     gameOver: function(score, scoreMsg) {
       this.game.score = score;
-      this.game.scoreMsg = this.st.tr[(!!scoreMsg
-        ? scoreMsg
-        : getScoreMessage(score))];
+      this.game.scoreMsg = this.st.tr[
+        scoreMsg ? scoreMsg : getScoreMessage(score)
+      ];
       const myIdx = this.game.players.findIndex(p => {
         return p.sid == this.st.user.sid || p.uid == this.st.user.id;
       });
-      if (myIdx >= 0) //OK, I play in this game
-      {
-        GameStorage.update(this.gameRef.id,
-          {score: score, scoreMsg: scoreMsg});
+      if (myIdx >= 0) {
+        //OK, I play in this game
+        GameStorage.update(this.gameRef.id, {
+          score: score,
+          scoreMsg: scoreMsg
+        });
         // Notify the score to main Hall. TODO: only one player (currently double send)
-        this.send("result", {gid:this.game.id, score:score});
+        this.send("result", { gid: this.game.id, score: score });
       }
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue
index 6f2dfeab..190717d0 100644
--- a/client/src/views/Hall.vue
+++ b/client/src/views/Hall.vue
@@ -91,9 +91,9 @@ export default {
   components: {
     Chat,
     GameList,
-    ChallengeList,
+    ChallengeList
   },
-  data: function () {
+  data: function() {
     return {
       st: store.state,
       cdisplay: "live", //or corr
@@ -106,125 +106,129 @@ export default {
         fen: "",
         vid: localStorage.getItem("vid") || "",
         to: "", //name of challenged player (if any)
-        cadence: localStorage.getItem("cadence") || "",
+        cadence: localStorage.getItem("cadence") || ""
       },
       newChat: "",
       conn: null,
       connexionString: "",
       // Related to (killing of) self multi-connects:
       newConnect: {},
-      killed: {},
+      killed: {}
     };
   },
   watch: {
     // st.variants changes only once, at loading from [] to [...]
-    "st.variants": function(variantArray) {
+    "st.variants": function() {
       // Set potential challenges and games variant names:
       this.challenges.concat(this.games).forEach(o => {
-        if (o.vname == "")
-          o.vname = this.getVname(o.vid);
+        if (o.vname == "") o.vname = this.getVname(o.vid);
       });
-    },
+    }
   },
   computed: {
     anonymousCount: function() {
       let count = 0;
-      Object.values(this.people).forEach(p => { count += (!p.name ? 1 : 0); });
+      Object.values(this.people).forEach(p => {
+        count += !p.name ? 1 : 0;
+      });
       return count;
-    },
+    }
   },
   created: function() {
     const my = this.st.user;
-    this.$set(this.people, my.sid, {id:my.id, name:my.name, pages:["/"]});
+    this.$set(this.people, my.sid, { id: my.id, name: my.name, pages: ["/"] });
     // Ask server for current corr games (all but mines)
     ajax(
       "/games",
       "GET",
-      {uid: this.st.user.id, excluded: true},
+      { uid: this.st.user.id, excluded: true },
       response => {
-        this.games = this.games.concat(response.games.map(g => {
-          const type = this.classifyObject(g);
-          const vname = this.getVname(g.vid);
-          return Object.assign({}, g, {type: type, vname: vname});
-        }));
+        this.games = this.games.concat(
+          response.games.map(g => {
+            const type = this.classifyObject(g);
+            const vname = this.getVname(g.vid);
+            return Object.assign({}, g, { type: type, vname: vname });
+          })
+        );
       }
     );
     // Also ask for corr challenges (open + sent by/to me)
-    ajax(
-      "/challenges",
-      "GET",
-      {uid: this.st.user.id},
-      response => {
-        // Gather all senders names, and then retrieve full identity:
-        // (TODO [perf]: some might be online...)
-        let names = {};
-        response.challenges.forEach(c => {
-          if (c.uid != this.st.user.id)
-            names[c.uid] = ""; //unknwon for now
-          else if (!!c.target && c.target != this.st.user.id)
-            names[c.target] = "";
-        });
-        const addChallenges = (newChalls) => {
-          names[this.st.user.id] = this.st.user.name; //in case of
-          this.challenges = this.challenges.concat(
-            response.challenges.map(c => {
-              const from = {name: names[c.uid], id: c.uid}; //or just name
-              const type = this.classifyObject(c);
-              const vname = this.getVname(c.vid);
-              return Object.assign({},
-                {
-                  type: type,
-                  vname: vname,
-                  from: from,
-                  to: (!!c.target ? names[c.target] : ""),
-                },
-                c);
-            })
-          );
-        };
-        if (Object.keys(names).length > 0)
-        {
-          ajax("/users",
-            "GET",
-            { ids: Object.keys(names).join(",") },
-            response2 => {
-              response2.users.forEach(u => {names[u.id] = u.name});
-              addChallenges();
-            }
-          );
-        }
-        else
-          addChallenges();
-      }
-    );
+    ajax("/challenges", "GET", { uid: this.st.user.id }, response => {
+      // Gather all senders names, and then retrieve full identity:
+      // (TODO [perf]: some might be online...)
+      let names = {};
+      response.challenges.forEach(c => {
+        if (c.uid != this.st.user.id) names[c.uid] = "";
+        //unknwon for now
+        else if (!!c.target && c.target != this.st.user.id)
+          names[c.target] = "";
+      });
+      const addChallenges = () => {
+        names[this.st.user.id] = this.st.user.name; //in case of
+        this.challenges = this.challenges.concat(
+          response.challenges.map(c => {
+            const from = { name: names[c.uid], id: c.uid }; //or just name
+            const type = this.classifyObject(c);
+            const vname = this.getVname(c.vid);
+            return Object.assign(
+              {},
+              {
+                type: type,
+                vname: vname,
+                from: from,
+                to: c.target ? names[c.target] : ""
+              },
+              c
+            );
+          })
+        );
+      };
+      if (Object.keys(names).length > 0) {
+        ajax(
+          "/users",
+          "GET",
+          { ids: Object.keys(names).join(",") },
+          response2 => {
+            response2.users.forEach(u => {
+              names[u.id] = u.name;
+            });
+            addChallenges();
+          }
+        );
+      } else addChallenges();
+    });
     const connectAndPoll = () => {
       this.send("connect");
       this.send("pollclientsandgamers");
     };
     // Initialize connection
-    this.connexionString = params.socketUrl +
-      "/?sid=" + this.st.user.sid +
-      "&tmpId=" + getRandString() +
-      "&page=" + encodeURIComponent(this.$route.path);
+    this.connexionString =
+      params.socketUrl +
+      "/?sid=" +
+      this.st.user.sid +
+      "&tmpId=" +
+      getRandString() +
+      "&page=" +
+      encodeURIComponent(this.$route.path);
     this.conn = new WebSocket(this.connexionString);
     this.conn.onopen = connectAndPoll;
     this.conn.onmessage = this.socketMessageListener;
     this.conn.onclose = this.socketCloseListener;
   },
   mounted: function() {
-    ["peopleWrap","infoDiv","newgameDiv"].forEach(eltName => {
+    ["peopleWrap", "infoDiv", "newgameDiv"].forEach(eltName => {
       let elt = document.getElementById(eltName);
       elt.addEventListener("click", processModalClick);
     });
-    document.querySelectorAll("#predefinedCadences > button").forEach(
-      (b) => { b.addEventListener("click",
-        () => { this.newchallenge.cadence = b.innerHTML; }
-      )}
-    );
+    document.querySelectorAll("#predefinedCadences > button").forEach(b => {
+      b.addEventListener("click", () => {
+        this.newchallenge.cadence = b.innerHTML;
+      });
+    });
     const showCtype = localStorage.getItem("type-challenges") || "live";
     const showGtype = localStorage.getItem("type-games") || "live";
-    this.setDisplay('c', showCtype);
-    this.setDisplay('g', showGtype);
+    this.setDisplay("c", showCtype);
+    this.setDisplay("g", showGtype);
   },
   beforeDestroy: function() {
     this.send("disconnect");
@@ -232,20 +236,14 @@ export default {
   methods: {
     // Helpers:
     send: function(code, obj) {
-      if (!!this.conn)
-      {
-        this.conn.send(JSON.stringify(
-          Object.assign(
-            {code: code},
-            obj,
-          )
-        ));
+      if (this.conn) {
+        this.conn.send(JSON.stringify(Object.assign({ code: code }, obj)));
       }
     },
     getVname: function(vid) {
       const variant = this.st.variants.find(v => v.id == vid);
       // this.st.variants might be uninitialized (variant == null)
-      return (!!variant ? variant.name : "");
+      return variant ? variant.name : "";
     },
     filterChallenges: function(type) {
       return this.challenges.filter(c => c.type == type);
@@ -253,21 +251,24 @@ export default {
     filterGames: function(type) {
       return this.games.filter(g => g.type == type);
     },
-    classifyObject: function(o) { //challenge or game
-      return (o.cadence.indexOf('d') === -1 ? "live" : "corr");
+    classifyObject: function(o) {
+      //challenge or game
+      return o.cadence.indexOf("d") === -1 ? "live" : "corr";
     },
     setDisplay: function(letter, type, e) {
       this[letter + "display"] = type;
-      localStorage.setItem("type-" + (letter == 'c' ? "challenges" : "games"), type);
-      let elt = !!e
+      localStorage.setItem(
+        "type-" + (letter == "c" ? "challenges" : "games"),
+        type
+      );
+      let elt = e
         ? e.target
         : document.getElementById("btn" + letter.toUpperCase() + type);
       elt.classList.add("active");
       elt.classList.remove("somethingnew"); //in case of
-      if (!!elt.previousElementSibling)
+      if (elt.previousElementSibling)
         elt.previousElementSibling.classList.remove("active");
-      else
-        elt.nextElementSibling.classList.remove("active");
+      else elt.nextElementSibling.classList.remove("active");
     },
     isGamer: function(sid) {
       return this.people[sid].pages.some(p => p.indexOf("/game/") >= 0);
@@ -278,21 +279,17 @@ export default {
         : "Observe";
     },
     challOrWatch: function(sid) {
-      if (this.people[sid].pages.some(p => p == "/"))
-      {
+      if (this.people[sid].pages.some(p => p == "/")) {
         // Available, in Hall
         this.newchallenge.to = this.people[sid].name;
         document.getElementById("modalPeople").checked = false;
-        doClick("modalNewgame");
-      }
-      else
-      {
+        window.doClick("modalNewgame");
+      } else {
         // In some game, maybe playing maybe not: show a random one
         let gids = [];
         this.people[sid].pages.forEach(p => {
           const matchGid = p.match(/[a-zA-Z0-9]+$/);
-          if (!!matchGid)
-            gids.push(matchGid[0]);
+          if (matchGid) gids.push(matchGid[0]);
         });
         const gid = gids[Math.floor(Math.random() * gids.length)];
         this.showGame(this.games.find(g => g.id == gid));
@@ -311,90 +308,82 @@ export default {
       document.getElementById("peopleBtn").classList.remove("somethingnew");
     },
     processChat: function(chat) {
-      this.send("newchat", {data:chat});
+      this.send("newchat", { data: chat });
     },
     // Messaging center:
     socketMessageListener: function(msg) {
-      if (!this.conn)
-        return;
+      if (!this.conn) return;
       const data = JSON.parse(msg.data);
-      switch (data.code)
-      {
-        case "pollclientsandgamers":
-        {
+      switch (data.code) {
+        case "pollclientsandgamers": {
           // Since people can be both in Hall and Game,
           // need to track "askIdentity" requests:
           let identityAsked = {};
           data.sockIds.forEach(s => {
             const page = s.page || "/";
-            if (s.sid != this.st.user.sid && !identityAsked[s.sid])
-            {
+            if (s.sid != this.st.user.sid && !identityAsked[s.sid]) {
               identityAsked[s.sid] = true;
-              this.send("askidentity", {target:s.sid, page:page});
+              this.send("askidentity", { target: s.sid, page: page });
             }
             if (!this.people[s.sid])
-              this.$set(this.people, s.sid, {id:0, name:"", pages:[page]});
+              this.$set(this.people, s.sid, { id: 0, name: "", pages: [page] });
             else if (this.people[s.sid].pages.indexOf(page) < 0)
               this.people[s.sid].pages.push(page);
-            if (!s.page) //peer is in Hall
-              this.send("askchallenge", {target:s.sid});
-            else //peer is in Game
-              this.send("askgame", {target:s.sid, page:page});
+            if (!s.page)
+              //peer is in Hall
+              this.send("askchallenge", { target: s.sid });
+            //peer is in Game
+            else this.send("askgame", { target: s.sid, page: page });
           });
           break;
         }
         case "connect":
-        case "gconnect":
-        {
+        case "gconnect": {
           const page = data.page || "/";
           // NOTE: player could have been polled earlier, but might have logged in then
           // So it's a good idea to ask identity if he was anonymous.
           // But only ask game / challenge if currently disconnected.
-          if (!this.people[data.from])
-          {
-            this.$set(this.people, data.from, {name:"", id:0, pages:[page]});
+          if (!this.people[data.from]) {
+            this.$set(this.people, data.from, {
+              name: "",
+              id: 0,
+              pages: [page]
+            });
             if (data.code == "connect")
-              this.send("askchallenge", {target:data.from});
-            else
-              this.send("askgame", {target:data.from, page:page});
-          }
-          else
-          {
+              this.send("askchallenge", { target: data.from });
+            else this.send("askgame", { target: data.from, page: page });
+          } else {
             // append page if not already in list
             if (this.people[data.from].pages.indexOf(page) < 0)
               this.people[data.from].pages.push(page);
           }
-          if (this.people[data.from].id == 0)
-          {
+          if (this.people[data.from].id == 0) {
             this.newConnect[data.from] = true; //for self multi-connects tests
-            this.send("askidentity", {target:data.from, page:page});
+            this.send("askidentity", { target: data.from, page: page });
           }
           break;
         }
         case "disconnect":
-        case "gdisconnect":
+        case "gdisconnect": {
           // If the user reloads the page twice very quickly (experienced with Firefox),
           // the first reload won't have time to connect but will trigger a "close" event anyway.
           // ==> Next check is required.
-          if (!this.people[data.from])
-            return;
+          if (!this.people[data.from]) return;
           // Disconnect means no more tmpIds:
-          if (data.code == "disconnect")
-          {
+          if (data.code == "disconnect") {
             // Remove the live challenge sent by this player:
             ArrayFun.remove(this.challenges, c => c.from.sid == data.from);
-          }
-          else
-          {
+          } else {
             // Remove the matching live game if now unreachable
             const gid = data.page.match(/[a-zA-Z0-9]+$/)[0];
             const gidx = this.games.findIndex(g => g.id == gid);
-            if (gidx >= 0)
-            {
+            if (gidx >= 0) {
               const game = this.games[gidx];
-              if (game.type == "live" &&
-                game.rids.length == 1 && game.rids[0] == data.from)
-              {
+              if (
+                game.type == "live" &&
+                game.rids.length == 1 &&
+                game.rids[0] == data.from
+              ) {
                 this.games.splice(gidx, 1);
               }
             }
@@ -404,6 +393,7 @@ export default {
           if (this.people[data.from].pages.length == 0)
             this.$delete(this.people, data.from);
           break;
+        }
         case "killed":
           // I logged in elsewhere:
           alert(this.st.tr["New connexion detected: tab now offline"]);
@@ -413,157 +403,156 @@ export default {
           //this.conn.close();
           this.conn = null;
           break;
-        case "askidentity":
-        {
+        case "askidentity": {
           // Request for identification (TODO: anonymous shouldn't need to reply)
           const me = {
             // Decompose to avoid revealing email
             name: this.st.user.name,
             sid: this.st.user.sid,
-            id: this.st.user.id,
+            id: this.st.user.id
           };
-          this.send("identity", {data:me, target:data.from});
+          this.send("identity", { data: me, target: data.from });
           break;
         }
-        case "identity":
-        {
+        case "identity": {
           const user = data.data;
-          if (!!user.name) //otherwise anonymous
-          {
+          if (user.name) {
+            //otherwise anonymous
             // If I multi-connect, kill current connexion if no mark (I'm older)
-            if (this.newConnect[user.sid] && user.id > 0
-              && user.id == this.st.user.id && user.sid != this.st.user.sid)
-            {
-              if (!this.killed[this.st.user.sid])
-              {
-                this.send("killme", {sid:this.st.user.sid});
+            if (
+              this.newConnect[user.sid] &&
+              user.id > 0 &&
+              user.id == this.st.user.id &&
+              user.sid != this.st.user.sid
+            ) {
+              if (!this.killed[this.st.user.sid]) {
+                this.send("killme", { sid: this.st.user.sid });
                 this.killed[this.st.user.sid] = true;
               }
             }
-            if (user.sid != this.st.user.sid) //I already know my identity...
-            {
-              this.$set(this.people, user.sid,
-                {
-                  id: user.id,
-                  name: user.name,
-                  pages: this.people[user.sid].pages,
-                });
+            if (user.sid != this.st.user.sid) {
+              //I already know my identity...
+              this.$set(this.people, user.sid, {
+                id: user.id,
+                name: user.name,
+                pages: this.people[user.sid].pages
+              });
             }
           }
           delete this.newConnect[user.sid];
           break;
         }
-        case "askchallenge":
-        {
+        case "askchallenge": {
           // Send my current live challenge (if any)
-          const cIdx = this.challenges.findIndex(c =>
-            c.from.sid == this.st.user.sid && c.type == "live");
-          if (cIdx >= 0)
-          {
+          const cIdx = this.challenges.findIndex(
+            c => c.from.sid == this.st.user.sid && c.type == "live"
+          );
+          if (cIdx >= 0) {
             const c = this.challenges[cIdx];
             // NOTE: in principle, should only send targeted challenge to the target.
             // But we may not know yet the identity of the target (just name),
             // so cannot decide if data.from is the target or not.
-            const myChallenge =
-            {
+            const myChallenge = {
               id: c.id,
               from: this.st.user.sid,
               to: c.to,
               fen: c.fen,
               vid: c.vid,
               cadence: c.cadence,
-              added: c.added,
+              added: c.added
             };
-            this.send("challenge", {data:myChallenge, target:data.from});
+            this.send("challenge", { data: myChallenge, target: data.from });
           }
           break;
         }
         case "challenge": //after "askchallenge"
-        case "newchallenge":
-        {
+        case "newchallenge": {
           // NOTE about next condition: see "askchallenge" case.
           const chall = data.data;
-          if (!chall.to || (this.people[chall.from].id > 0 &&
-            (chall.from == this.st.user.sid || chall.to == this.st.user.name)))
-          {
+          if (
+            !chall.to ||
+            (this.people[chall.from].id > 0 &&
+              (chall.from == this.st.user.sid || chall.to == this.st.user.name))
+          ) {
             let newChall = Object.assign({}, chall);
             newChall.type = this.classifyObject(chall);
             newChall.added = Date.now();
             let fromValues = Object.assign({}, this.people[chall.from]);
             delete fromValues["pages"]; //irrelevant in this context
-            newChall.from = Object.assign({sid:chall.from}, fromValues);
+            newChall.from = Object.assign({ sid: chall.from }, fromValues);
             newChall.vname = this.getVname(newChall.vid);
             this.challenges.push(newChall);
-            if ((newChall.type == "live" && this.cdisplay == "corr") ||
-              (newChall.type == "corr" && this.cdisplay == "live"))
-            {
-              document.getElementById("btnC" + newChall.type).classList.add("somethingnew");
+            if (
+              (newChall.type == "live" && this.cdisplay == "corr") ||
+              (newChall.type == "corr" && this.cdisplay == "live")
+            ) {
+              document
+                .getElementById("btnC" + newChall.type)
+                .classList.add("somethingnew");
             }
           }
           break;
         }
-        case "refusechallenge":
-        {
+        case "refusechallenge": {
           const cid = data.data;
           ArrayFun.remove(this.challenges, c => c.id == cid);
           alert(this.st.tr["Challenge declined"]);
           break;
         }
-        case "deletechallenge":
-        {
+        case "deletechallenge": {
           // NOTE: the challenge may be already removed
           const cid = data.data;
           ArrayFun.remove(this.challenges, c => c.id == cid);
           break;
         }
         case "game": //individual request
-        case "newgame":
-        {
+        case "newgame": {
           // NOTE: it may be live or correspondance
           const game = data.data;
           let locGame = this.games.find(g => g.id == game.id);
-          if (!locGame)
-          {
+          if (!locGame) {
             let newGame = game;
             newGame.type = this.classifyObject(game);
             newGame.vname = this.getVname(game.vid);
-            if (!game.score) //if new game from Hall
+            if (!game.score)
+              //if new game from Hall
               newGame.score = "*";
             newGame.rids = [game.rid];
             delete newGame["rid"];
             this.games.push(newGame);
-            if ((newGame.type == "live" && this.gdisplay == "corr") ||
-              (newGame.type == "corr" && this.gdisplay == "live"))
-            {
-              document.getElementById("btnG" + newGame.type).classList.add("somethingnew");
+            if (
+              (newGame.type == "live" && this.gdisplay == "corr") ||
+              (newGame.type == "corr" && this.gdisplay == "live")
+            ) {
+              document
+                .getElementById("btnG" + newGame.type)
+                .classList.add("somethingnew");
             }
-          }
-          else
-          {
+          } else {
             // Append rid (if not already in list)
-            if (!locGame.rids.includes(game.rid))
-              locGame.rids.push(game.rid);
+            if (!locGame.rids.includes(game.rid)) locGame.rids.push(game.rid);
           }
           break;
         }
-        case "result":
-        {
+        case "result": {
           let g = this.games.find(g => g.id == data.gid);
-          if (!!g)
-            g.score = data.score;
+          if (g) g.score = data.score;
           break;
         }
-        case "startgame":
-        {
+        case "startgame": {
           // New game just started: data contain all information
           const gameInfo = data.data;
           if (this.classifyObject(gameInfo) == "live")
             this.startNewGame(gameInfo);
-          else
-          {
-            this.infoMessage = this.st.tr["New correspondance game:"] +
-              " <a href='#/game/" + gameInfo.id + "'>" +
-              "#/game/" + gameInfo.id + "</a>";
+          else {
+            this.infoMessage =
+              this.st.tr["New correspondance game:"] +
+              " <a href='#/game/" +
+              gameInfo.id +
+              "'>" +
+              "#/game/" +
+              gameInfo.id +
+              "</a>";
             let modalBox = document.getElementById("modalInfo");
             modalBox.checked = true;
           }
@@ -577,56 +566,62 @@ export default {
       }
     },
     socketCloseListener: function() {
-      if (!this.conn)
-        return;
+      if (!this.conn) return;
       this.conn = new WebSocket(this.connexionString);
       this.conn.addEventListener("message", this.socketMessageListener);
       this.conn.addEventListener("close", this.socketCloseListener);
     },
     // Challenge lifecycle:
     newChallenge: async function() {
+      let error = "";
       if (this.newchallenge.vid == "")
-        return alert(this.st.tr["Please select a variant"]);
-      if (!!this.newchallenge.to && this.newchallenge.to == this.st.user.name)
-        return alert(this.st.tr["Self-challenge is forbidden"]);
+        error = this.st.tr["Please select a variant"];
+      else if (!!this.newchallenge.to && this.newchallenge.to == this.st.user.name)
+        error = this.st.tr["Self-challenge is forbidden"];
+      if (error) {
+        alert(error);
+        return;
+      }
       const vname = this.getVname(this.newchallenge.vid);
       const vModule = await import("@/variants/" + vname + ".js");
       window.V = vModule.VariantRules;
-      if (!!this.newchallenge.cadence.match(/^[0-9]+$/))
+      if (this.newchallenge.cadence.match(/^[0-9]+$/))
         this.newchallenge.cadence += "+0"; //assume minutes, no increment
-      const error = checkChallenge(this.newchallenge);
-      if (!!error)
-        return alert(error);
       const ctype = this.classifyObject(this.newchallenge);
-      if (ctype == "corr" && this.st.user.id <= 0)
-        return alert(this.st.tr["Please log in to play correspondance games"]);
+      error = checkChallenge(this.newchallenge);
+      if (!error && ctype == "corr" && this.st.user.id <= 0)
+        error = this.st.tr["Please log in to play correspondance games"];
+      if (error) {
+        alert(error);
+        return;
+      }
       // NOTE: "from" information is not required here
       let chall = Object.assign({}, this.newchallenge);
-      const finishAddChallenge = (cid) => {
+      const finishAddChallenge = cid => {
         chall.id = cid || "c" + getRandString();
         // Remove old challenge if any (only one at a time of a given type):
-        const cIdx = this.challenges.findIndex(c =>
-          (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) && c.type == ctype);
-        if (cIdx >= 0)
-        {
+        const cIdx = this.challenges.findIndex(
+          c =>
+            (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) &&
+            c.type == ctype
+        );
+        if (cIdx >= 0) {
           // Delete current challenge (will be replaced now)
-          this.send("deletechallenge", {data:this.challenges[cIdx].id});
-          if (ctype == "corr")
-          {
-            ajax(
-              "/challenges",
-              "DELETE",
-              {id: this.challenges[cIdx].id}
-            );
+          this.send("deletechallenge", { data: this.challenges[cIdx].id });
+          if (ctype == "corr") {
+            ajax("/challenges", "DELETE", { id: this.challenges[cIdx].id });
           }
           this.challenges.splice(cIdx, 1);
         }
-        this.send("newchallenge", {data:Object.assign({from:this.st.user.sid}, chall)});
+        this.send("newchallenge", {
+          data: Object.assign({ from: this.st.user.sid }, chall)
+        });
         // Add new challenge:
-        chall.from = { //decompose to avoid revealing email
+        chall.from = {
+          //decompose to avoid revealing email
           sid: this.st.user.sid,
           id: this.st.user.id,
-          name: this.st.user.name,
+          name: this.st.user.name
         };
         chall.added = Date.now();
         // NOTE: vname and type are redundant (can be deduced from cadence + vid)
@@ -638,61 +633,49 @@ export default {
         localStorage.setItem("vid", chall.vid);
         document.getElementById("modalNewgame").checked = false;
       };
-      if (ctype == "live")
-      {
+      if (ctype == "live") {
         // Live challenges have a random ID
         finishAddChallenge(null);
-      }
-      else
-      {
+      } else {
         // Correspondance game: send challenge to server
-        ajax(
-          "/challenges",
-          "POST",
-          { chall: chall },
-          response => { finishAddChallenge(response.cid); }
-        );
+        ajax("/challenges", "POST", { chall: chall }, response => {
+          finishAddChallenge(response.cid);
+        });
       }
     },
     clickChallenge: function(c) {
-      const myChallenge = (c.from.sid == this.st.user.sid //live
-        || (this.st.user.id > 0 && c.from.id == this.st.user.id)); //corr
-      if (!myChallenge)
-      {
-        if (c.type == "corr" && this.st.user.id <= 0)
-          return alert(this.st.tr["Please log in to accept corr challenges"]);
+      const myChallenge =
+        c.from.sid == this.st.user.sid || //live
+        (this.st.user.id > 0 && c.from.id == this.st.user.id); //corr
+      if (!myChallenge) {
+        if (c.type == "corr" && this.st.user.id <= 0) {
+          alert(this.st.tr["Please log in to accept corr challenges"]);
+          return;
+        }
         c.accepted = true;
-        if (!!c.to) //c.to == this.st.user.name (connected)
-        {
+        if (c.to) {
+          //c.to == this.st.user.name (connected)
           // TODO: if special FEN, show diagram after loading variant
           c.accepted = confirm("Accept challenge?");
         }
-        if (c.accepted)
-        {
-          c.seat = { //again, avoid c.seat = st.user to not reveal email
+        if (c.accepted) {
+          c.seat = {
+            //again, avoid c.seat = st.user to not reveal email
             sid: this.st.user.sid,
             id: this.st.user.id,
-            name: this.st.user.name,
+            name: this.st.user.name
           };
           this.launchGame(c);
+        } else {
+          this.send("refusechallenge", { data: c.id, target: c.from.sid });
         }
-        else
-        {
-          this.send("refusechallenge", {data:c.id, target:c.from.sid});
+        this.send("deletechallenge", { data: c.id });
+      } //my challenge
+      else {
+        if (c.type == "corr") {
+          ajax("/challenges", "DELETE", { id: c.id });
         }
-        this.send("deletechallenge", {data:c.id});
-      }
-      else //my challenge
-      {
-        if (c.type == "corr")
-        {
-          ajax(
-            "/challenges",
-            "DELETE",
-            {id: c.id}
-          );
-        }
-        this.send("deletechallenge", {data:c.id});
+        this.send("deletechallenge", { data: c.id });
       }
       // In all cases, the challenge is consumed:
       ArrayFun.remove(this.challenges, ch => ch.id == c.id);
@@ -702,37 +685,35 @@ export default {
       const vModule = await import("@/variants/" + c.vname + ".js");
       window.V = vModule.VariantRules;
       // These game informations will be shared
-      let gameInfo =
-      {
+      let gameInfo = {
         id: getRandString(),
         fen: c.fen || V.GenRandInitFen(),
         players: shuffle([c.from, c.seat]), //white then black
         vid: c.vid,
-        cadence: c.cadence,
+        cadence: c.cadence
       };
       let oppsid = c.from.sid; //may not be defined if corr + offline opp
-      if (!oppsid)
-      {
-        oppsid = Object.keys(this.people).find(sid =>
-          this.people[sid].id == c.from.id);
+      if (!oppsid) {
+        oppsid = Object.keys(this.people).find(
+          sid => this.people[sid].id == c.from.id
+        );
       }
       const notifyNewgame = () => {
-        if (!!oppsid) //opponent is online
-          this.send("startgame", {data:gameInfo, target:oppsid});
+        if (oppsid)
+          //opponent is online
+          this.send("startgame", { data: gameInfo, target: oppsid });
         // Send game info (only if live) to everyone except me in this tab
-        this.send("newgame", {data:gameInfo});
+        this.send("newgame", { data: gameInfo });
       };
-      if (c.type == "live")
-      {
+      if (c.type == "live") {
         notifyNewgame();
         this.startNewGame(gameInfo);
-      }
-      else //corr: game only on server
-      {
+      } //corr: game only on server
+      else {
         ajax(
           "/games",
           "POST",
-          {gameInfo: gameInfo, cid: c.id}, //cid useful to delete challenge
+          { gameInfo: gameInfo, cid: c.id }, //cid useful to delete challenge
           response => {
             gameInfo.id = response.gameId;
             notifyNewgame();
@@ -752,14 +733,14 @@ export default {
         moves: [],
         clocks: [-1, -1], //-1 = unstarted
         initime: [0, 0], //initialized later
-        score: "*",
+        score: "*"
       });
       GameStorage.add(game);
       if (this.st.settings.sound >= 1)
-        new Audio("/sounds/newgame.mp3").play().catch(err => {});
+        new Audio("/sounds/newgame.mp3").play().catch(() => {});
       this.$router.push("/game/" + gameInfo.id);
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/views/Logout.vue b/client/src/views/Logout.vue
index 89f9a399..34dbb49b 100644
--- a/client/src/views/Logout.vue
+++ b/client/src/views/Logout.vue
@@ -10,11 +10,11 @@ main
 import { store } from "@/store";
 import { ajax } from "@/utils/ajax";
 export default {
-  name: 'my-logout',
+  name: "my-logout",
   data: function() {
     return {
       st: store.state,
-      errmsg: "",
+      errmsg: ""
     };
   },
   created: function() {
@@ -29,7 +29,7 @@ export default {
     localStorage.removeItem("myid");
     localStorage.removeItem("myname");
     ajax("/logout", "GET"); //TODO: listen for errors?
-  },
+  }
 };
 </script>
 
diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue
index 863d17fc..01b5264d 100644
--- a/client/src/views/MyGames.vue
+++ b/client/src/views/MyGames.vue
@@ -19,25 +19,24 @@ import GameList from "@/components/GameList.vue";
 export default {
   name: "my-my-games",
   components: {
-    GameList,
+    GameList
   },
   data: function() {
     return {
       st: store.state,
       display: "live",
       liveGames: [],
-      corrGames: [],
+      corrGames: []
     };
   },
   created: function() {
-    GameStorage.getAll((localGames) => {
-      localGames.forEach((g) => g.type = this.classifyObject(g));
+    GameStorage.getAll(localGames => {
+      localGames.forEach(g => (g.type = this.classifyObject(g)));
       this.liveGames = localGames;
     });
-    if (this.st.user.id > 0)
-    {
-      ajax("/games", "GET", {uid: this.st.user.id}, (res) => {
-        res.games.forEach((g) => g.type = this.classifyObject(g));
+    if (this.st.user.id > 0) {
+      ajax("/games", "GET", { uid: this.st.user.id }, res => {
+        res.games.forEach(g => (g.type = this.classifyObject(g)));
         this.corrGames = res.games;
       });
     }
@@ -50,23 +49,20 @@ export default {
     setDisplay: function(type, e) {
       this.display = type;
       localStorage.setItem("type-myGames", type);
-      let elt = !!e
-        ? e.target
-        : document.getElementById(type + "Games");
+      let elt = e ? e.target : document.getElementById(type + "Games");
       elt.classList.add("active");
-      if (!!elt.previousElementSibling)
+      if (elt.previousElementSibling)
         elt.previousElementSibling.classList.remove("active");
-      else
-        elt.nextElementSibling.classList.remove("active");
+      else elt.nextElementSibling.classList.remove("active");
     },
     // TODO: classifyObject is redundant (see Hall.vue)
     classifyObject: function(o) {
-      return (o.cadence.indexOf('d') === -1 ? "live" : "corr");
+      return o.cadence.indexOf("d") === -1 ? "live" : "corr";
     },
     showGame: function(g) {
       this.$router.push("/game/" + g.id);
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/views/News.vue b/client/src/views/News.vue
index 8f03b3af..2686552c 100644
--- a/client/src/views/News.vue
+++ b/client/src/views/News.vue
@@ -18,7 +18,7 @@ main
         @click="showModalNews"
       )
         | {{ st.tr["Write news"] }}
-      .news(v-for="n,idx in sortedNewsList" :class="{margintop:idx>0}")
+      .news(v-for="n,idx in newsList" :class="{margintop:idx>0}")
         span.ndt {{ formatDatetime(n.added) }}
         div(v-if="devs.includes(st.user.id)")
           button(@click="editNews(n)") {{ st.tr["Edit"] }}
@@ -41,33 +41,31 @@ export default {
       st: store.state,
       cursor: 0, //ID of last showed news
       hasMore: true, //a priori there could be more news to load
-      curnews: {id:0, content:""},
+      curnews: { id: 0, content: "" },
       newsList: [],
-      infoMsg: "",
+      infoMsg: ""
     };
   },
   created: function() {
-    ajax("/news", "GET", {cursor:this.cursor}, (res) => {
-      this.newsList = res.newsList;
+    ajax("/news", "GET", { cursor: this.cursor }, res => {
+      this.newsList = res.newsList.sort((n1, n2) => n1.added - n2.added);
       const L = res.newsList.length;
-      if (L > 0)
-        this.cursor = res.newsList[L-1].id;
+      if (L > 0) this.cursor = this.newsList[0].id;
     });
   },
   mounted: function() {
-    document.getElementById("newnewsDiv").addEventListener("click", processModalClick);
-  },
-  computed: {
-    sortedNewsList: function() {
-      return this.newsList.sort( (n1,n2) => n1.added - n2.added );
-    },
+    document
+      .getElementById("newnewsDiv")
+      .addEventListener("click", processModalClick);
   },
   methods: {
     formatDatetime: function(dt) {
       const dtObj = new Date(dt);
       const timePart = getTime(dtObj);
       // Show minutes but not seconds:
-      return getDate(dtObj) + " " + timePart.substr(0,timePart.lastIndexOf(":"));
+      return (
+        getDate(dtObj) + " " + timePart.substr(0, timePart.lastIndexOf(":"))
+      );
     },
     parseHtml: function(txt) {
       return !txt.match(/<[/a-zA-Z]+>/)
@@ -78,7 +76,7 @@ export default {
       const newsContent = document.getElementById("newsContent");
       // https://stackoverflow.com/questions/995168/textarea-to-resize-based-on-content-length
       newsContent.style.height = "1px";
-      newsContent.style.height = (10+newsContent.scrollHeight)+"px";
+      newsContent.style.height = 10 + newsContent.scrollHeight + "px";
     },
     resetCurnews: function() {
       this.curnews.id = 0;
@@ -87,49 +85,39 @@ export default {
     },
     showModalNews: function() {
       this.resetCurnews();
-      doClick('modalNews');
+      window.doClick("modalNews");
     },
     sendNews: function() {
       const edit = this.curnews.id > 0;
       this.infoMsg = "Processing... Please wait";
-      ajax(
-        "/news",
-        edit ? "PUT" : "POST",
-        {news: this.curnews},
-        (res) => {
-          if (edit)
-          {
-            let n = this.newsList.find(n => n.id == this.curnews.id);
-            if (!!n)
-              n.content = this.curnews.content;
-          }
-          else
-          {
-            const newNews = {
-              content:this.curnews.content,
-              added:Date.now(),
-              uid: this.st.user.id,
-              id: res.id
-            };
-            this.newsList = this.newsList.concat([newNews]);
-          }
-          document.getElementById("modalNews").checked = false;
-          this.infoMsg = "";
-          this.resetCurnews();
+      ajax("/news", edit ? "PUT" : "POST", { news: this.curnews }, res => {
+        if (edit) {
+          let n = this.newsList.find(n => n.id == this.curnews.id);
+          if (n) n.content = this.curnews.content;
+        } else {
+          const newNews = {
+            content: this.curnews.content,
+            added: Date.now(),
+            uid: this.st.user.id,
+            id: res.id
+          };
+          this.newsList = [newNews].concat(this.newsList);
         }
-      );
+        document.getElementById("modalNews").checked = false;
+        this.infoMsg = "";
+        this.resetCurnews();
+      });
     },
     editNews: function(n) {
       this.curnews.content = n.content;
       this.curnews.id = n.id;
       // No need for added and uid fields: never updated
-      doClick('modalNews');
+      window.doClick("modalNews");
     },
     deleteNews: function(n) {
-      if (confirm(this.st.tr["Are you sure?"]))
-      {
+      if (confirm(this.st.tr["Are you sure?"])) {
         this.infoMsg = "Processing... Please wait";
-        ajax("/news", "DELETE", {id:n.id}, () => {
+        ajax("/news", "DELETE", { id: n.id }, () => {
           const nIdx = this.newsList.findIndex(nw => nw.id == n.id);
           this.newsList.splice(nIdx, 1);
           this.infoMsg = "";
@@ -138,19 +126,15 @@ export default {
       }
     },
     loadMore: function() {
-      ajax("/news", "GET", {cursor:this.cursor}, (res) => {
-        if (res.newsList.length > 0)
-        {
+      ajax("/news", "GET", { cursor: this.cursor }, res => {
+        if (res.newsList.length > 0) {
           this.newsList = this.newsList.concat(res.newsList);
           const L = res.newsList.length;
-          if (L > 0)
-            this.cursor = res.newsList[L-1].id;
-        }
-        else
-          this.hasMore = false;
+          if (L > 0) this.cursor = res.newsList[L - 1].id;
+        } else this.hasMore = false;
       });
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/src/views/Problems.vue b/client/src/views/Problems.vue
index 9fa2d796..a9f9fcb6 100644
--- a/client/src/views/Problems.vue
+++ b/client/src/views/Problems.vue
@@ -66,7 +66,7 @@ main
   .row(v-else)
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
       #controls
-        button#newProblem(onClick="doClick('modalNewprob')")
+        button#newProblem(onClick="window.doClick('modalNewprob')")
           | {{ st.tr["New problem"] }}
         label(for="checkboxMine") {{ st.tr["My problems"] }}
         input#checkboxMine(
@@ -86,7 +86,7 @@ main
           th {{ st.tr["Instructions"] }}
           th {{ st.tr["Number"] }}
         tr(
-          v-for="p in sortedProblems"
+          v-for="p in problems"
           v-show="displayProblem(p)"
           @click="setHrefPid(p)"
         )
@@ -107,14 +107,14 @@ import BaseGame from "@/components/BaseGame.vue";
 export default {
   name: "my-problems",
   components: {
-    BaseGame,
+    BaseGame
   },
   data: function() {
     return {
       st: store.state,
       emptyVar: {
         vid: 0,
-        vname: "",
+        vname: ""
       },
       // Problem currently showed, or edited:
       curproblem: {
@@ -124,7 +124,7 @@ export default {
         diag: "",
         instruction: "",
         solution: "",
-        showSolution: false,
+        showSolution: false
       },
       loadedVar: 0, //corresponding to loaded V
       selectedVar: 0, //to filter problems based on variant
@@ -134,68 +134,56 @@ export default {
       infoMsg: "",
       vr: null, //"variant rules" object initialized from FEN
       game: {
-        players:[{name:"Problem"},{name:"Problem"}],
-        mode: "analyze",
-      },
+        players: [{ name: "Problem" }, { name: "Problem" }],
+        mode: "analyze"
+      }
     };
   },
   created: function() {
-    ajax("/problems", "GET", (res) => {
-      this.problems = res.problems;
+    ajax("/problems", "GET", res => {
+      // Show newest problem first:
+      this.problems = res.problems.sort((p1, p2) => p2.added - p1.added);
       if (this.st.variants.length > 0)
         this.problems.forEach(p => this.setVname(p));
       // Retrieve all problems' authors' names
       let names = {};
       this.problems.forEach(p => {
-        if (p.uid != this.st.user.id)
-          names[p.uid] = ""; //unknwon for now
-        else
-          p.uname = this.st.user.name;
+        if (p.uid != this.st.user.id) names[p.uid] = "";
+        //unknwon for now
+        else p.uname = this.st.user.name;
       });
       const showOneIfPid = () => {
         const pid = this.$route.query["id"];
-        if (!!pid)
-          this.showProblem(this.problems.find(p => p.id == pid));
+        if (pid) this.showProblem(this.problems.find(p => p.id == pid));
       };
-      if (Object.keys(names).length > 0)
-      {
-        ajax("/users",
-          "GET",
-          { ids: Object.keys(names).join(",") },
-          res2 => {
-            res2.users.forEach(u => {names[u.id] = u.name});
-            this.problems.forEach(p => p.uname = names[p.uid]);
-            showOneIfPid();
-          }
-        );
-      }
-      else
-        showOneIfPid();
+      if (Object.keys(names).length > 0) {
+        ajax("/users", "GET", { ids: Object.keys(names).join(",") }, res2 => {
+          res2.users.forEach(u => {
+            names[u.id] = u.name;
+          });
+          this.problems.forEach(p => (p.uname = names[p.uid]));
+          showOneIfPid();
+        });
+      } else showOneIfPid();
     });
   },
   mounted: function() {
-    document.getElementById("newprobDiv").addEventListener("click", processModalClick);
+    document
+      .getElementById("newprobDiv")
+      .addEventListener("click", processModalClick);
   },
   watch: {
     // st.variants changes only once, at loading from [] to [...]
-    "st.variants": function(variantArray) {
+    "st.variants": function() {
       // Set problems vname (either all are set or none)
       if (this.problems.length > 0 && this.problems[0].vname == "")
         this.problems.forEach(p => this.setVname(p));
     },
-    "$route": function(to, from) {
+    $route: function(to) {
       const pid = to.query["id"];
-      if (!!pid)
-        this.showProblem(this.problems.find(p => p.id == pid));
-      else
-        this.showOne = false
-    },
-  },
-  computed: {
-    sortedProblems: function() {
-      // Newest first:
-      return this.problems.sort( (p1,p2) => p2.added - p1.added);
-    },
+      if (pid) this.showProblem(this.problems.find(p => p.id == pid));
+      else this.showOne = false;
+    }
   },
   methods: {
     setVname: function(prob) {
@@ -204,19 +192,18 @@ export default {
     firstChars: function(text) {
       let preparedText = text
         // Replace line jumps and <br> by spaces
-        .replace(/\n/g, " " )
-        .replace(/<br\/?>/g, " " )
+        .replace(/\n/g, " ")
+        .replace(/<br\/?>/g, " ")
         .replace(/<[^>]+>/g, "") //remove remaining HTML tags
         .replace(/[ ]+/g, " ") //remove series of spaces by only one
         .trim();
       const maxLength = 32; //arbitrary...
       if (preparedText.length > maxLength)
-        return preparedText.substr(0,32) + "...";
+        return preparedText.substr(0, 32) + "...";
       return preparedText;
     },
     copyProblem: function(p1, p2) {
-      for (let key in p1)
-        p2[key] = p1[key];
+      for (let key in p1) p2[key] = p1[key];
     },
     setHrefPid: function(p) {
       // Change href => $route changes, watcher notices, call showProblem
@@ -245,14 +232,10 @@ export default {
     },
     changeVariant: function(prob) {
       this.setVname(prob);
-      this.loadVariant(
-        prob.vid,
-        () => {
-          // Set FEN if possible (might not be correct yet)
-          if (V.IsGoodFen(prob.fen))
-            this.setDiagram(prob);
-        }
-      );
+      this.loadVariant(prob.vid, () => {
+        // Set FEN if possible (might not be correct yet)
+        if (V.IsGoodFen(prob.fen)) this.setDiagram(prob);
+      });
     },
     loadVariant: async function(vid, cb) {
       // Condition: vid is a valid variant ID
@@ -274,48 +257,47 @@ export default {
       const parsedFen = V.ParseFen(prob.fen);
       const args = {
         position: parsedFen.position,
-        orientation: parsedFen.turn,
+        orientation: parsedFen.turn
       };
       prob.diag = getDiagram(args);
     },
     displayProblem: function(p) {
-      return ((this.selectedVar == 0 || p.vid == this.selectedVar) &&
-        ((this.onlyMines && p.uid == this.st.user.id)
-          || (!this.onlyMines && p.uid != this.st.user.id)));
+      return (
+        (this.selectedVar == 0 || p.vid == this.selectedVar) &&
+        ((this.onlyMines && p.uid == this.st.user.id) ||
+          (!this.onlyMines && p.uid != this.st.user.id))
+      );
     },
     showProblem: function(p) {
-      this.loadVariant(
-        p.vid,
-        () => {
-          // The FEN is already checked at this stage:
-          this.vr = new V(p.fen);
-          this.game.vname = p.vname;
-          this.game.mycolor = this.vr.turn; //diagram orientation
-          this.game.fen = p.fen;
-          this.$set(this.game, "fenStart", p.fen);
-          this.copyProblem(p, this.curproblem);
-          this.showOne = true;
-        }
-      );
+      this.loadVariant(p.vid, () => {
+        // The FEN is already checked at this stage:
+        this.vr = new V(p.fen);
+        this.game.vname = p.vname;
+        this.game.mycolor = this.vr.turn; //diagram orientation
+        this.game.fen = p.fen;
+        this.$set(this.game, "fenStart", p.fen);
+        this.copyProblem(p, this.curproblem);
+        this.showOne = true;
+      });
     },
     sendProblem: function() {
       const error = checkProblem(this.curproblem);
-      if (!!error)
-        return alert(error);
+      if (error) {
+        alert(error);
+        return;
+      }
       const edit = this.curproblem.id > 0;
       this.infoMsg = "Processing... Please wait";
       ajax(
         "/problems",
         edit ? "PUT" : "POST",
-        {prob: this.curproblem},
-        (ret) => {
-          if (edit)
-          {
+        { prob: this.curproblem },
+        ret => {
+          if (edit) {
             let editedP = this.problems.find(p => p.id == this.curproblem.id);
             this.copyProblem(this.curproblem, editedP);
-          }
-          else //new problem
-          {
+          } //new problem
+          else {
             let newProblem = Object.assign({}, this.curproblem);
             newProblem.id = ret.id;
             newProblem.uid = this.st.user.id;
@@ -328,21 +310,19 @@ export default {
       );
     },
     editProblem: function(prob) {
-      if (!prob.diag)
-        this.setDiagram(prob); //possible because V is loaded at this stage
+      if (!prob.diag) this.setDiagram(prob); //possible because V is loaded at this stage
       this.copyProblem(prob, this.curproblem);
-      doClick('modalNewprob');
+      window.doClick("modalNewprob");
     },
     deleteProblem: function(prob) {
-      if (confirm(this.st.tr["Are you sure?"]))
-      {
-        ajax("/problems", "DELETE", {id:prob.id}, () => {
+      if (confirm(this.st.tr["Are you sure?"])) {
+        ajax("/problems", "DELETE", { id: prob.id }, () => {
           ArrayFun.remove(this.problems, p => p.id == prob.id);
           this.backToList();
         });
       }
-    },
-  },
+    }
+  }
 };
 </script>
 
@@ -379,5 +359,4 @@ textarea
 @media screen and (max-width: 767px)
   #topPage
     text-align: center
-
 </style>
diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue
index d06ae269..4daebf9d 100644
--- a/client/src/views/Rules.vue
+++ b/client/src/views/Rules.vue
@@ -23,9 +23,9 @@ import ComputerGame from "@/components/ComputerGame.vue";
 import { store } from "@/store";
 import { getDiagram } from "@/utils/printDiagram";
 export default {
-  name: 'my-rules',
+  name: "my-rules",
   components: {
-    ComputerGame,
+    ComputerGame
   },
   data: function() {
     return {
@@ -37,14 +37,14 @@ export default {
         vname: "",
         mode: "versus",
         fen: "",
-        score: "*",
+        score: "*"
       }
     };
   },
   watch: {
-    "$route": function(newRoute) {
+    $route: function(newRoute) {
       this.re_setVariant(newRoute.params["vname"]);
-    },
+    }
   },
   created: function() {
     // NOTE: variant cannot be set before store is initialized
@@ -52,23 +52,27 @@ export default {
   },
   computed: {
     content: function() {
-      if (!this.gameInfo.vname)
-        return ""; //variant not set yet
+      if (!this.gameInfo.vname) return ""; //variant not set yet
       // (AJAX) Request to get rules content (plain text, HTML)
-      return require("raw-loader!@/translations/rules/" +
-          this.gameInfo.vname + "/" + this.st.lang + ".pug")
-        // Next two lines fix a weird issue after last update (2019-11)
-        .replace(/\\n/g, " ").replace(/\\"/g, '"')
-        .replace('module.exports = "', '').replace(/"$/, "")
-        .replace(/(fen:)([^:]*):/g, this.replaceByDiag);
-    },
+      return (
+        require("raw-loader!@/translations/rules/" +
+          this.gameInfo.vname +
+          "/" +
+          this.st.lang +
+          ".pug")
+          // Next two lines fix a weird issue after last update (2019-11)
+          .replace(/\\n/g, " ")
+          .replace(/\\"/g, '"')
+          .replace('module.exports = "', "")
+          .replace(/"$/, "")
+          .replace(/(fen:)([^:]*):/g, this.replaceByDiag)
+      );
+    }
   },
   methods: {
     clickReadRules: function() {
-      if (this.display != "rules")
-        this.display = "rules";
-      else if (this.gameInProgress)
-        this.display = "computer";
+      if (this.display != "rules") this.display = "rules";
+      else if (this.gameInProgress) this.display = "computer";
     },
     parseFen(fen) {
       const fenParts = fen.split(" ");
@@ -76,7 +80,7 @@ export default {
         position: fenParts[0],
         marks: fenParts[1],
         orientation: fenParts[2],
-        shadow: fenParts[3],
+        shadow: fenParts[3]
       };
     },
     // Method to replace diagrams in loaded HTML
@@ -90,8 +94,7 @@ export default {
       this.gameInfo.vname = vname;
     },
     startGame: function(mode) {
-      if (this.gameInProgress)
-        return;
+      if (this.gameInProgress) return;
       this.gameInProgress = true;
       this.display = "computer";
       this.gameInfo.mode = mode;
@@ -107,10 +110,11 @@ export default {
       this.gameInProgress = false;
     },
     gotoAnalyze: function() {
-      this.$router.push("/analyse/" + this.gameInfo.vname
-        + "/?fen=" + V.GenRandInitFen());
-    },
-  },
+      this.$router.push(
+        "/analyse/" + this.gameInfo.vname + "/?fen=" + V.GenRandInitFen()
+      );
+    }
+  }
 };
 </script>
 
diff --git a/client/src/views/Variants.vue b/client/src/views/Variants.vue
index 7f78f040..31061158 100644
--- a/client/src/views/Variants.vue
+++ b/client/src/views/Variants.vue
@@ -19,33 +19,35 @@ export default {
   data: function() {
     return {
       curPrefix: "",
-      st: store.state,
+      st: store.state
     };
   },
   computed: {
-    filteredVariants: function () {
-      const capitalizedPrefix = this.curPrefix.replace(/^\w/, c => c.toUpperCase());
+    filteredVariants: function() {
+      const capitalizedPrefix = this.curPrefix.replace(/^\w/, c =>
+        c.toUpperCase()
+      );
       const variants = this.st.variants
-      .filter( v => {
-        return v.name.startsWith(capitalizedPrefix);
-      })
-      .map( v => {
-        return {
-          name: v.name,
-          desc: v.description,
-        };
-      })
-      .sort((a,b) => {
-        return a.name.localeCompare(b.name);
-      });
+        .filter(v => {
+          return v.name.startsWith(capitalizedPrefix);
+        })
+        .map(v => {
+          return {
+            name: v.name,
+            desc: v.description
+          };
+        })
+        .sort((a, b) => {
+          return a.name.localeCompare(b.name);
+        });
       return variants;
-    },
+    }
   },
   methods: {
     getLink: function(vname) {
       return "/variants/" + vname;
-    },
-  },
+    }
+  }
 };
 </script>
 
diff --git a/client/vue.config.js b/client/vue.config.js
index a98dd2dc..63b0e4bc 100644
--- a/client/vue.config.js
+++ b/client/vue.config.js
@@ -4,7 +4,7 @@ module.exports = {
     output: {
       // Fix "window is not defined" issues with web worker.
       // https://github.com/webpack/webpack/issues/6642
-      globalObject: 'this',
-    },
-  },
+      globalObject: "this"
+    }
+  }
 };
-- 
2.44.0