From eaa5ad3e93b761fefb16b32071be0b439761f843 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Wed, 7 Apr 2021 00:57:58 +0200
Subject: [PATCH] Add 'display' DB field for nicer variants display. Remove
 join on Variants table in Games SQL requests. Group variants in DB

---
 TODO                                          |  15 +-
 client/src/translations/en.js                 |  71 ++-
 client/src/translations/es.js                 |  74 ++-
 client/src/translations/fr.js                 |  74 ++-
 .../rules/{Chess960 => Chess}/en.pug          |   2 +-
 .../rules/{Chess960 => Chess}/es.pug          |   2 +-
 .../rules/{Chess960 => Chess}/fr.pug          |   2 +-
 client/src/translations/variants/en.pug       | 497 -----------------
 client/src/translations/variants/es.pug       | 507 ------------------
 client/src/translations/variants/fr.pug       | 505 -----------------
 client/src/variants/Chess.js                  |   1 +
 client/src/variants/Chess960.js               |   5 -
 client/src/views/Game.vue                     |  44 +-
 client/src/views/Hall.vue                     |  39 +-
 client/src/views/MyGames.vue                  |  30 +-
 client/src/views/Problems.vue                 |  27 +-
 client/src/views/Rules.vue                    |   6 +-
 client/src/views/VariantList.vue              |   5 +-
 client/src/views/Variants.vue                 |  55 +-
 server/db/create.sql                          |   2 +
 server/db/populate.sql                        | 356 ++++++------
 server/models/Game.js                         |  37 +-
 server/models/Variant.js                      |   3 +
 23 files changed, 563 insertions(+), 1796 deletions(-)
 rename client/src/translations/rules/{Chess960 => Chess}/en.pug (99%)
 rename client/src/translations/rules/{Chess960 => Chess}/es.pug (99%)
 rename client/src/translations/rules/{Chess960 => Chess}/fr.pug (99%)
 delete mode 100644 client/src/translations/variants/en.pug
 delete mode 100644 client/src/translations/variants/es.pug
 delete mode 100644 client/src/translations/variants/fr.pug
 create mode 120000 client/src/variants/Chess.js
 delete mode 100644 client/src/variants/Chess960.js

diff --git a/TODO b/TODO
index b13d9b59..d3b62270 100644
--- a/TODO
+++ b/TODO
@@ -6,9 +6,18 @@ If new live game starts in background, "new game" notify OK but not first move.
 Will be used for variants with custom non-rectangular board (Hex, at least)
 Or, with other board shapes (see greenchess.net for example)
 
-Would be nice to display some better variants names (Pacosako -> Paco-Ŝako etc),
-but the "formatted" name with uppercase + all lower cases would still be required.
-(For example in variants list, or tournament variant field).
+Merge variants 1, 2 (3) into one with sub-variant selection when starting a game (New Game + vs engine + analyse)
+E.g. Checkered 1 & 2 into one, and so on.
+Need additional field in variant code... in a generic way (radiobutton or select or checkbox?)
 
+#New variants:
 Chessplode
 Tablut
+
+https://brainking.com/fr/GameRules?tp=128 Massacre Chess
+
+CWDA : need game options (also useful at least for Monster)
+PizzaKings https://www.chessvariants.com/unequal.dir/pizza-kings.html
+https://en.m.wikipedia.org/wiki/Chess_with_different_armies#Pizza_Kings%5B11%5D_(John_Lawson)
+
+https://www.chessvariants.com/other.dir/nemoroth.html :-)
diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index 38155cf2..b8c1cd94 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -338,5 +338,74 @@ export const translations = {
   "Walk on a graph": "Walk on a graph",
   "White move twice": "White move twice",
   "Win by castling long": "Win by castling long",
-  "Xiangqi 7 x 7": "Xiangqi 7 x 7"
+  "Xiangqi 7 x 7": "Xiangqi 7 x 7",
+
+  // Variants by categories:
+  "What is a chess variant?": "What is a chess variant?",
+  "Why play chess variants?": "Why play chess variants?",
+  "chess_v": ": to play under standard rules, with a random (or not) symmetric (or not) initial position.",
+  "vt0": "Simplified games to learn chess",
+  "vg0": "Variants with very few different pieces, and a simplified goal.",
+  "vt1": "Forced captures",
+  "vg1": "In a given position, there are generally less possible moves than in the orthodox games since you must capture.",
+  "vt2": "Transformations",
+  "vg2": "Pieces generally transform when capturing.",
+  "vt3": "Modified boundaries",
+  "vg3": "Boards which communicating sides.",
+  "vt4": "Different pawn movements",
+  "vg4": "Everything is as in the orthodox game, but pawns move unusually.",
+  "vt5": "Different armies",
+  "vg5": "Standard pieces versus a team of different pieces.",
+  "vt6": "Inspired by ball games",
+  "vg6": "Variants involving a ball, abstract or not, which must cross the board.",
+  "vt7": "New pieces",
+  "vg7": "A large variety of fairy pieces can be defined. Some very powerful like the Amazon, others rather weak like the Grasshopper.",
+  "vt8": "Augmenting pieces",
+  "vg8": "In the following variants, fairy (or not) pieces may appear later in the game.",
+  "vt9": "Several royal pieces",
+  "vg9": "In these games you must take care of two or more \"kings\".",
+  "vt10": "Unorthodox captures by replacement",
+  "vg10": "Non-standard captures, but using known mechanisms.",
+  "vt11": "Unorthodox captures - others",
+  "vg11": "Captures are generally achieved without replacement. That is to say, you don't replace the enemy piece on its square to capture it.",
+  "vt12": "Pieces changing side, shared pieces",
+  "vg12": "Pieces' owners (color) may change during the game",
+  "vt13": "Incomplete information",
+  "vg13": "Some speculation is required in these variants, where some game informations are hidden.",
+  "vt14": "Random factors",
+  "vg14": "These games include random effects, which can be funny, frustrating or both :)",
+  "vt15": "Inpired by knight movement",
+  "vg15": "Variants based on the knight move, which augment or transform pieces' abilities.",
+  "vt16": "Unusual initial setup",
+  "vg16": "Initial setup causes a lot of captures early in the game.",
+  "vt17": "\"Easy\" variants: simple rules",
+  "vg17": "Only minor changes are made to the orthodox rules, resulting in a very similar game.",
+  "vt18": "Simple rules, but not so easy",
+  "vg18": "Minor changes to the orthodox rules leading to a very different strategy.",
+  "vt19": "Initially empty board",
+  "vg19": "All pieces are progressively added from an empty board.",
+  "vt20": "Repositioning",
+  "vg20": "Pieces can be dropped on the board, either immediately or later in the game.",
+  "vt21": "Immobilization, hypnotism",
+  "vg21": "Pieces can be paralyzed or controlled under certain circumstances.",
+  "vt22": "Regional and historical variants",
+  "vg22": "(Partial) Game evolution in time and space.",
+  "vt23": "Kings race",
+  "vg23": "The goal is to cross the board with your king.",
+  "vt24": "Several moves in one turn",
+  "vg24": "In these variants, you can play two or more moves per turn.",
+  "vt25": "Single powerful piece versus army",
+  "vg25": "Very few but powerful pieces against a full army.",
+  "vt26": "Exchanging pieces positions",
+  "vg26": "Some or all pieces can be swapped.",
+  "vt27": "Different objective",
+  "vg27": "Orthodox rules, but the goal is not checkmate (or not only).",
+  "vt28": "Non-chess",
+  "vg28": "Some games not chess related.",
+  "vt29": "Change opponent's move",
+  "vg29": "You can change some enemy moves after they are played.",
+  "vt30": "Augmented pieces",
+  "vg30": "Pieces can temporarily borrow powers from others.",
+  "vt31": "Miscelleanous",
+  "vg31": "These variants are not classified yet, generally because they are the only one of their kind on this website.",
 };
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 9e8088b9..55dd3045 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -318,7 +318,8 @@ export const translations = {
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Tirar de las piezas",
   "Spartan versus Persians": "Espartanos contra Persas",
-  "Squares disappear": "Las casillas desaparecen",
+  "Squares disappear (v1)": "Las casillas desaparecen (v1)",
+  "Squares disappear (v2)": "Las casillas desaparecen (v2)",
   "Squat last rank (v1)": "Ocupa la última fila (v1)",
   "Squat last rank (v2)": "Ocupa la última fila (v2)",
   "Stacking Checkers variant": "Variante de damas con pilas",
@@ -337,5 +338,74 @@ export const translations = {
   "Walk on a graph": "Camino en un gráfico",
   "White move twice": "Las blancas juegan dos veces",
   "Win by castling long": "Ganar jugando al enroque largo",
-  "Xiangqi 7 x 7": "Xiangqi 7 x 7"
+  "Xiangqi 7 x 7": "Xiangqi 7 x 7",
+
+  // Variants by categories:
+  "What is a chess variant?": "¿Qué es una variante?",
+  "Why play chess variants?": "¿Por qué jugar las variantes?",
+  "chess_v": ": para jugar con reglas estándar, desde una posición inicial aleatorio (o no) simétrico (o no).",
+  "vt0": "Juegos simplificados para aprender ajedrez",
+  "vg0": "Variantes con muy pocas piezas diferentes y un propósito simplificado.",
+  "vt1": "Capturas obligatorias",
+  "vg1": "En una posición dada, generalmente hay menos movimientos posibles que en juego ortodoxo ya que tienes que capturar.",
+  "vt2": "Transformaciones",
+  "vg2": "En general, las piezas se transforman mediante la captura.",
+  "vt3": "Bordes modificados",
+  "vg3": "Tableros de ajedrez con bordes comunicantes.",
+  "vt4": "Diferentes movimientos de peones",
+  "vg4": "Todo va como en el juego ortodoxo, pero los peones se mueven de una manera inusual.",
+  "vt5": "Diferentes ejércitos",
+  "vg5": "Piezas estándar contra un equipo de diferentes piezas.",
+  "vt6": "Inspirado por juegos de pelota",
+  "vg6": "Variantes que usan un globo, abstracto o no, quien tiene que cruzar el tablero de ajedrez.",
+  "vt7": "Piezas nuevas",
+  "vg7": "Se puede introducir una amplia gama de piezas mágicas. Algunas son muy poderosos como la Amazona, otros bastante débiles como el Saltamontes.",
+  "vt8": "Piezas crecientes",
+  "vg8": "En las siguientes variantes, piezas mágicas (o no) puede aparecer más adelante en el juego.",
+  "vt9": "Varias piezas reales",
+  "vg9": "En estos juegos debes mantener a salvo a dos o más \"reyes\".",
+  "vt10": "Capturas no ortodoxas por reemplazo",
+  "vg10": "Capturas no estándar, pero utilizando mecanismos conocidos.",
+  "vt11": "Capturas no ortodoxas - otras",
+  "vg11": "Las capturas generalmente se realizan sin reemplazo. Es decir que no reemplaces las piezas opuestas en su espacio de captura.",
+  "vt12": "Piezas que cambian de lado, piezas compartidas",
+  "vg12": "Los propietarios (colores) de las piezas pueden cambiar durante el juego.",
+  "vt13": "Información incompleta",
+  "vg13": "Se requiere cierta especulación para estas variantes, donde se oculta alguna información sobre el juego.",
+  "vt14": "Factores aleatorios",
+  "vg14": "Estos juegos incluyen efectos aleatorios, que puede ser divertido, frustrante o ambos :)",
+  "vt15": "Inspiradas por el movimiento del caballo",
+  "vg15": "Variantes basadas en el movimiento del caballo, que aumenta o transforma las capacidades de las piezas.",
+  "vt16": "Disposición inicial inusual",
+  "vg16": "Del diseño inicial resultado muchas capturas al principio del juego.",
+  "vt17": "Variantes \"fáciles\": reglas simples",
+  "vg17": "Las reglas ortodoxas solo cambian ligeramente, lo que da un juego muy similar.",
+  "vt18": "Reglas simples, pero no tanto",
+  "vg18": "Pequeños cambios en las reglas del juego que conducen a estrategias muy diferentes.",
+  "vt19": "Tablero inicialmente vacío",
+  "vg19": "Todas las piezas se agregan gradualmente desde un tablero vacío.",
+  "vt20": "Reposicionamiento",
+  "vg20": "Las piezas se pueden dejar caer en el tablero de ajedrez, ya sea inmediatamente o más tarde en el juego.",
+  "vt21": "Inmovilización",
+  "vg21": "Las piezas pueden paralizarse o controlarse en ciertas circunstancias.",
+  "vt22": "Variantes regionales e históricas",
+  "vg22": "Evolución (parcial) del juego en espacio y tiempo.",
+  "vt23": "Carrera de reyes",
+  "vg23": "El objetivo es cruzar el tablero de ajedrez con tu rey.",
+  "vt24": "Varias jugadas por turno",
+  "vg24": "En estas variantes, puedes jugar dos o más movimientos por turno.",
+  "vt25": "Una sola pieza poderosa contra un ejército",
+  "vg25": "Muy pocas piezas pero muy poderosas contra todo un ejército.",
+  "vt26": "Intercambio de posiciones de piezas",
+  "vg26": "Algunas o todas las piezas pueden intercambiarse.",
+  "vt27": "Objetivo diferente",
+  "vg27": "Reglas ortodoxas, pero el objetivo no es jaque mate (o no solo).",
+  "vt28": "Aparte del Ajedrez",
+  "vg28": "Algunos juegos no están relacionados con el ajedrez.",
+  "vt29": "Cambiar el movimiento del oponente",
+  "vg29": "Puedes cambiar los movimientos de algunas jugadas del oponente después de haberlos jugado",
+  "vt30": "Piezas aumentadas",
+  "vg30": "Las piezas pueden temporalmente tomar prestados poderes de otras",
+  "vt31": "Varios",
+  "vg31": "Estas variantes aún no están clasificadas, en general porque son el único representante de su tipo en este sitio.",
 };
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index c095bb0b..48029dce 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -318,7 +318,8 @@ export const translations = {
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Tirez sur les pièces",
   "Spartan versus Persians": "Spartiates contre Perses",
-  "Squares disappear": "Les cases disparaissent",
+  "Squares disappear (v1)": "Les cases disparaissent (v1)",
+  "Squares disappear (v2)": "Les cases disparaissent (v2)",
   "Squat last rank (v1)": "Occupez la dernière rangée (v1)",
   "Squat last rank (v2)": "Occupez la dernière rangée (v2)",
   "Stacking Checkers variant": "Variante des Dames avec empilements",
@@ -337,5 +338,74 @@ export const translations = {
   "Walk on a graph": "Marche sur un graphe",
   "White move twice": "Les blancs jouent deux fois",
   "Win by castling long": "Gagnez en jouant grand roque",
-  "Xiangqi 7 x 7": "Xiangqi 7 x 7"
+  "Xiangqi 7 x 7": "Xiangqi 7 x 7",
+
+  // Variants by categories:
+  "What is a chess variant?": "Qu'est-ce qu'une variante ?",
+  "Why play chess variants?": "Pourquoi jouer aux variantes ?",
+  "chess_v": "&nbsp;: pour jouer avec les règles standard, depuis une position initiale aléatoire (ou non) symétrique (ou non).",
+  "vt0": "Jeux simplifiés pour apprendre les échecs",
+  "vg0": "Variantes avec très peu de pièces différentes, et un but simplifié.",
+  "vt1": "Captures obligatoires",
+  "vg1": "Dans une position donnée, il y a généralement moins de coups possibles que dans le jeu orthodoxe puisque vous devez capturer.",
+  "vt2": "Transformations",
+  "vg2": "En général les pièces se transforment en capturant.",
+  "vt3": "Frontières modifiées",
+  "vg3": "Échiquiers aux bords communiquants.",
+  "vt4": "Différents coups de pions",
+  "vg4": "Tout se déroule comme au jeu orthodoxe, mais les pions se déplacent d'une manière inhabituelle.",
+  "vt5": "Armées différentes",
+  "vg5": "Pièces standard contre une équipe de pièces différentes.",
+  "vt6": "Inspirées par des jeux de ballon",
+  "vg6": "Variantes utilisant un ballon, abstrait ou non, qui doit traverser l'échiquier.",
+  "vt7": "Nouvelles pièces",
+  "vg7": "Une large gamme de pièces féériques peuvent être introduites. Certaines sont très puissantes comme l'Amazone, d'autres plutôt faibles comme la Sauterelle.",
+  "vt8": "Pièces augmentantes",
+  "vg8": "Dans les variantes suivantes, des pièces féériques (ou non) peuvent apparaître plus tard dans le jeu.",
+  "vt9": "Plusieurs pièces royales",
+  "vg9": "Dans ces jeux vous devez garder à l'abri deux \"rois\" ou plus.",
+  "vt10": "Captures non orthodoxes par remplacement",
+  "vg10": "Captures non-standard, mais utilisant des mécanismes connus.",
+  "vt11": "Captures non orthodoxes - autres",
+  "vg11": "Les captures s'effectuent généralement sans remplacement. C'est-à-dire que vous ne remplacez pas les pièces adverses sur leur case de capture.",
+  "vt12": "Pièces changeant de camp, pièces partagées",
+  "vg12": "Les propriétaires (couleurs) des pièces peuvent changer pendant la partie.",
+  "vt13": "Information incomplète",
+  "vg13": "Une part de spéculation est requise pour ces variantes, où certaines informations sur le jeu sont cachées.",
+  "vt14": "Facteurs aléatoires",
+  "vg14": "Ces jeux incluent des effets aléatoires, qui peuvent être amusants, frustrants ou bien les deux :)",
+  "vt15": "Inspirées par le déplacement du cavalier",
+  "vg15": "Variantes basées sur le coup de cavalier, qui augmente ou transforme les capacités des pièces.",
+  "vt16": "Arrangement initial inhabituel",
+  "vg16": "L'agencement initial entraîne de nombreuses captures tôt dans la partie.",
+  "vt17": "Variantes \"faciles\" : règles simples",
+  "vg17": "Les règles orthodoxes ne sont que légèrement modifiées, ce qui donne un jeu très similaire.",
+  "vt18": "Règles simples, mais pas tant",
+  "vg18": "Changements mineurs dans les règles du jeu menant à des stratégies très différentes.",
+  "vt19": "Échiquier initialement vide",
+  "vg19": "Toutes les pièces sont ajoutées progressivement depuis un échiquier vide.",
+  "vt20": "Repositionnement",
+  "vg20": "Les pièces peuvent être parachutées sur l'échiquier, immédiatement ou bien plus tard dans la partie.",
+  "vt21": "Immobilisation",
+  "vg21": "Les pièces peuvent être paralysées ou contrôlées dans certaines circonstances.",
+  "vt22": "Variantes régionales et historiques",
+  "vg22": "Évolution (partielle) du jeu dans l'espace et le temps.",
+  "vt23": "Course de rois",
+  "vg23": "L'objectif est de traverser l'échiquier avec votre roi.",
+  "vt24": "Plusieurs coups par tour",
+  "vg24": "Dans ces variantes, vous pouvez jouer deux coups ou plus par tour.",
+  "vt25": "Une seule pièce puissante face à une armée",
+  "vg25": "Très peu de pièces mais très puissantes face à une armée entière.",
+  "vt26": "Échange des positions des pièces",
+  "vg26": "Certaines ou toutes les pièces peuvent être échangées.",
+  "vt27": "Objectif différent",
+  "vg27": "Règles orthodoxes, mais le but n'est pas de mater (ou pas seulement).",
+  "vt28": "Hors Échecs",
+  "vg28": "Quelques jeux a priori sans liens avec les échecs.",
+  "vt29": "Changez le coup adverse",
+  "vg29": "Vous pouvez changer certains coups adverses après qu'ils ont été joués",
+  "vt30": "Pièces augmentées",
+  "vg30": "Les pièces peuvent temporairement emprunter des pouvoir aux autres",
+  "vt31": "Divers",
+  "vg31": "Ces variantes ne sont pas encore classées, en général car elles sont l'unique représentant de leur type sur ce site.",
 };
diff --git a/client/src/translations/rules/Chess960/en.pug b/client/src/translations/rules/Chess/en.pug
similarity index 99%
rename from client/src/translations/rules/Chess960/en.pug
rename to client/src/translations/rules/Chess/en.pug
index d46c5a09..e7645eff 100644
--- a/client/src/translations/rules/Chess960/en.pug
+++ b/client/src/translations/rules/Chess/en.pug
@@ -1,5 +1,5 @@
 p.boxed
-  | Orthodox rules (with shuffled starting position).
+  | Orthodox rules (with potentially shuffled starting position).
 
 p.
   Chess is played between two players, one moving the white pieces and the
diff --git a/client/src/translations/rules/Chess960/es.pug b/client/src/translations/rules/Chess/es.pug
similarity index 99%
rename from client/src/translations/rules/Chess960/es.pug
rename to client/src/translations/rules/Chess/es.pug
index 358bc9b9..b60b7b3c 100644
--- a/client/src/translations/rules/Chess960/es.pug
+++ b/client/src/translations/rules/Chess/es.pug
@@ -1,5 +1,5 @@
 p.boxed
-  | Juego ortodoxo (con una posición inicial aleatoria).
+  | Juego ortodoxo (con una posición inicial potencialmente aleatoria).
 
 p.
   El ajedrez es un juego entre dos jugadores, uno que mueve las piezas blancas
diff --git a/client/src/translations/rules/Chess960/fr.pug b/client/src/translations/rules/Chess/fr.pug
similarity index 99%
rename from client/src/translations/rules/Chess960/fr.pug
rename to client/src/translations/rules/Chess/fr.pug
index f7436100..9a54a917 100644
--- a/client/src/translations/rules/Chess960/fr.pug
+++ b/client/src/translations/rules/Chess/fr.pug
@@ -1,5 +1,5 @@
 p.boxed
-  | Jeu orthodoxe (avec une position de départ aléatoire).
+  | Jeu orthodoxe (avec une position de départ potentiellement aléatoire).
 
 p.
   Les échecs sont un jeu entre deux joueurs, l'un déplaçant les pièces blanches
diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug
deleted file mode 100644
index 0da01791..00000000
--- a/client/src/translations/variants/en.pug
+++ /dev/null
@@ -1,497 +0,0 @@
-p.text-center
-  a(href="https://www.chessvariants.com/what.html") What is a chess variant?
-  | &nbsp;&nbsp;&nbsp;&nbsp;
-  a(href="https://www.chessvariants.com/why.html") Why play chess variants?
-
-p
-  a(href="/#/variants/Chess960") Chess960
-  | &nbsp;variant allows to play under standard rules, with a random
-  | (or not) symmetric (or not) initial position.
-
-h3 Simplified games to learn chess
-
-p Variants with very few different pieces, and a simplified goal.
--
-  var varlist = [
-    "Bishopawns",
-    "Discoduel",
-    "Dobutsu",
-    "Knightpawns",
-    "Pawns",
-    "Pawnsking",
-    "Queenpawns",
-    "Rookpawns"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Forced captures
-
-p.
-  In a given position, there are generally less possible moves than in the
-  orthodox games since you must capture.
--
-  var varlist = [
-    "Arena",
-    "Capture",
-    "Losers",
-    "Monocolor",
-    "Suicide"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Transformations
-
-p Pieces generally transform when capturing.
--
-  var varlist = [
-    "Absorption",
-    "Cannibal1",
-    "Cannibal2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Modified boundaries
-
-p Boards which communicating sides.
--
-  var varlist = [
-    "Circular",
-    "Cylinder"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Different pawn movements
-
-p Everything is as in the orthodox game, but pawns move unusually.
--
-  var varlist = [
-    "Berolina",
-    "Diamond",
-    "Vchess"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Different armies
-
-p Standard pieces versus a team of different pieces.
--
-  var varlist = [
-    "Colorbound",
-    "Empire",
-    "Horde",
-    "Orda",
-    "Shinobi",
-    "Spartan",
-    "Synochess"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inspired by ball games
-
-p Variants involving a ball, abstract or not, which must cross the board.
--
-  var varlist = [
-    "Ball",
-    "Football",
-    "Rugby"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 New pieces
-
-p.
-  A large variety of fairy pieces can be defined.
-  Some very powerful like the Amazon, some rather weak like the Grasshopper.
--
-  var varlist = [
-    "Capablanca",
-    "Eightpieces",
-    "Fullcavalry",
-    "Grand",
-    "Grasshopper",
-    "Hoppelpoppel",
-    "Newzealand",
-    "Omega",
-    "Ordamirror",
-    "Perfect",
-    "Shako",
-    "Tencubed",
-    "Wildebeest"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p.
-  In the following variants, fairy (or not) pieces may
-  appear later in the game.
--
-  var varlist = [
-    "Musketeer",
-    "Schess",
-    "Titan"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Several royal pieces
-
-p In these games you must take care of two or more "kings".
--
-  var varlist = [
-    "Coregal",
-    "Twokings",
-    "Antiking1",
-    "Antiking2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Unorthodox captures
-
-p Non-standard captures, but using known mechanisms.
--
-  var varlist = [
-    "Enpassant",
-    "Rifle",
-    "Zen"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p.
-  Captures are generally achieved without replacement. That is to say,
-  you don't replace the enemy piece on its square to capture it.
--
-  var varlist = [
-    "Allmate1",
-    "Allmate2",
-    "Baroque",
-    "Dynamo",
-    "Fugue",
-    "Interweave",
-    "Rococo",
-    "Maxima"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Pieces changing side, shared pieces
-
-p Pieces' owners (color) may change during the game
--
-  var varlist = [
-    "Benedict",
-    "Checkered1",
-    "Checkered2",
-    "Otage",
-    "Pacifist1",
-    "Pacifist2",
-    "Pacosako"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Incomplete information
-
-p.
-  Some speculation is required in these variants,
-  where some game informations are hidden.
--
-  var varlist = [
-    "Apocalypse",
-    "Dark",
-    "Hidden",
-    "Hiddenqueen",
-    "Stealthbomb1",
-    "Stealthbomb2",
-    "Synchrone1",
-    "Synchrone2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Random factors
-
-p.
-  These games include random effects,
-  which can be funny, frustrating or both :)
--
-  var varlist = [
-    "Chakart",
-    "Dice"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inpired by knight movement
-
-p.
-  Variants based on the knight move,
-  which augment or transform pieces' abilities.
--
-  var varlist = [
-    "Balaklava",
-    "Knightmate1",
-    "Knightmate2",
-    "Knightrelay1",
-    "Knightrelay2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Unusual initial setup
-
-p Initial setup causes a lot of captures early in the game.
--
-  var varlist = [
-    "Doublearmy",
-    "Upsidedown",
-    "Pawnmassacre"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 "Easy" variants: simple rules
-
-p.
-  Only minor changes are made to the orthodox rules,
-  resulting in a very similar game.
--
-  var varlist = [
-    "Coronation",
-    "Freecapture",
-    "Pocketknight"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p ...Or leading to a very different strategy:
--
-  var varlist = [
-    "Antimatter",
-    "Atomic1",
-    "Atomic2",
-    "Brotherhood",
-    "Checkless"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Initially empty board
-
-p.
-  All pieces are progressively added from an empty board.
--
-  var varlist = [
-    "Parachute",
-    "Screen"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Repositioning
-
-p Pieces can be dropped on the board, either immediately or later in the game.
--
-  var varlist = [
-    "Clorange",
-    "Crazyhouse",
-    "Madhouse",
-    "Rampage",
-    "Recycle",
-    "Shogun",
-    "Teleport1",
-    "Teleport2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Immobilization, hypnotism
-
-p Pieces can be paralyzed or controlled under certain circumstances.
--
-  var varlist = [
-    "Hypnotic",
-    "Isardam",
-    "Koopa",
-    "Madrasi",
-    "Mesmer"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Regional and historical variants
-
-p (Partial) Game evolution in time and space.
--
-  var varlist = [
-    "Janggi",
-    "Karouk",
-    "Makpong",
-    "Makruk",
-    "Minishogi",
-    "Minixiangqi",
-    "Shatranj",
-    "Shogi",
-    "Sittuyin",
-    "Xiangqi"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Kings race
-
-p The goal is to cross the board with your king.
--
-  var varlist = [
-    "Racingkings",
-    "Royalrace"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Several moves in one turn
-
-p In these variants, you can play two or more moves per turn.
--
-  var varlist = [
-    "Avalanche",
-    "Doublemove1",
-    "Doublemove2",
-    "Progressive1",
-    "Progressive2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Single powerful piece versus army
-
-p Very few but powerful pieces against a full army.
--
-  var varlist = [
-    "Maharajah",
-    "Monster"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Exchanging pieces positions
-
-p Some or all pieces can be swapped.
--
-  var varlist = [
-    "Joker",
-    "Suction",
-    "Swap",
-    "Switching"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Different objective
-
-p Orthodox rules, but the goal is not checkmate (or not only).
--
-  var varlist = [
-    "Alapo",
-    "Castle",
-    "Crossing",
-    "Extinction",
-    "Threechecks",
-    "Kinglet",
-    "Koth",
-    "Squatter1",
-    "Squatter2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Non-chess
-
-p Some games not chess related.
--
-  var varlist = [
-    "Atarigo",
-    "Avalam1",
-    "Avalam2",
-    "Emergo",
-    "Fanorona",
-    "Gomoku",
-    "Konane",
-    "Yote"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Miscelleanous
-
-p.
-  These variants are not classified yet, generally because they are the only
-  one of their kind on this website.
--
-  var varlist = [
-    "Alice",
-    "Align4",
-    "Ambiguous",
-    "Bario",
-    "Bicolour",
-    "Convert",
-    "Copycat",
-    "Evolution",
-    "Forward",
-    "Fusion",
-    "Gridolina",
-    "Hamilton",
-    "Iceage",
-    "Kingsmaker",
-    "Magnetic",
-    "Pandemonium1",
-    "Pandemonium2",
-    "Refusal1",
-    "Refusal2",
-    "Relayup",
-    "Rollerball",
-    "Selfabsorb",
-    "Takenmake",
-    "Wormhole1",
-    "Wormhole2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug
deleted file mode 100644
index cc246bd5..00000000
--- a/client/src/translations/variants/es.pug
+++ /dev/null
@@ -1,507 +0,0 @@
-p.text-center
-  a(href="https://www.chessvariants.com/what.html") ¿Qué es una variante?
-  | &nbsp;&nbsp;&nbsp;&nbsp;
-  a(href="https://www.chessvariants.com/why.html")
-    | ¿Por qué jugar las variantes?
-
-p
-  | La variante 
-  a(href="/#/variants/Chess960") Chess960
-  | &nbsp;te permite jugar con reglas estándar, desde una posición
-  | inicial aleatorio (o no) simétrico (o no).
-
-h3 Juegos simplificados para aprender ajedrez
-
-p Variantes con muy pocas piezas diferentes y un propósito simplificado.
--
-  var varlist = [
-    "Bishopawns",
-    "Discoduel",
-    "Dobutsu",
-    "Knightpawns",
-    "Pawns",
-    "Pawnsking",
-    "Queenpawns",
-    "Rookpawns"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Capturas obligatorias
-
-p.
-  En una posición dada, generalmente hay menos movimientos posibles que
-  en juego ortodoxo ya que tienes que capturar.
--
-  var varlist = [
-    "Arena",
-    "Capture",
-    "Losers",
-    "Monocolor",
-    "Suicide"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Transformaciones
-
-p En general, las piezas se transforman mediante la captura.
--
-  var varlist = [
-    "Absorption",
-    "Cannibal1",
-    "Cannibal2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Bordes modificados
-
-p Tableros de ajedrez con bordes comunicantes.
--
-  var varlist = [
-    "Circular",
-    "Cylinder"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Diferentes movimientos de peones
-
-p.
-  Todo va como en el juego ortodoxo,
-  pero los peones se mueven de una manera inusual.
--
-  var varlist = [
-    "Berolina",
-    "Diamond",
-    "Vchess"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Diferentes ejércitos
-
-p Piezas estándar contra un equipo de diferentes piezas.
--
-  var varlist = [
-    "Colorbound",
-    "Empire",
-    "Horde",
-    "Orda",
-    "Shinobi",
-    "Spartan",
-    "Synochess"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inspirado por juegos de pelota
-
-p.
-  Variantes que usan un globo, abstracto o no,
-  quien tiene que cruzar el tablero de ajedrez.
--
-  var varlist = [
-    "Ball",
-    "Football",
-    "Rugby"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Piezas nuevas
-
-p.
-  Se puede introducir una amplia gama de piezas mágicas.
-  Algunas son muy poderosos como la Amazona,
-  otros bastante débiles como el Saltamontes.
--
-  var varlist = [
-    "Capablanca",
-    "Eightpieces",
-    "Fullcavalry",
-    "Grand",
-    "Grasshopper",
-    "Hoppelpoppel",
-    "Newzealand",
-    "Omega",
-    "Ordamirror",
-    "Perfect",
-    "Shako",
-    "Tencubed",
-    "Wildebeest"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p.
-  En las siguientes variantes, piezas mágicas (o no)
-  puede aparecer más adelante en el juego:
--
-  var varlist = [
-    "Musketeer",
-    "Schess",
-    "Titan"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Varias piezas reales
-
-p En estos juegos debes mantener a salvo a dos o más "reyes".
--
-  var varlist = [
-    "Coregal",
-    "Twokings",
-    "Antiking1",
-    "Antiking2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Capturas no ortodoxas
-
-p Capturas no estándar, pero utilizando mecanismos conocidos.
--
-  var varlist = [
-    "Enpassant",
-    "Rifle",
-    "Zen"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p.
-  Las capturas generalmente se realizan sin reemplazo. Es decir
-  que no reemplaces las piezas opuestas en su espacio de captura.
--
-  var varlist = [
-    "Allmate1",
-    "Allmate2",
-    "Baroque",
-    "Dynamo",
-    "Fugue",
-    "Interweave",
-    "Rococo",
-    "Maxima"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Piezas que cambian de lado, piezas compartidas
-
-p Los propietarios (colores) de las piezas pueden cambiar durante el juego.
--
-  var varlist = [
-    "Benedict",
-    "Checkered1",
-    "Checkered2",
-    "Otage",
-    "Pacifist1",
-    "Pacifist2",
-    "Pacosako"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Información incompleta
-
-p.
-  Se requiere cierta especulación para estas variantes,
-  donde se oculta alguna información sobre el juego.
--
-  var varlist = [
-    "Apocalypse",
-    "Dark",
-    "Hidden",
-    "Hiddenqueen",
-    "Stealthbomb1",
-    "Stealthbomb2",
-    "Synchrone1",
-    "Synchrone2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Factores aleatorios
-
-p.
-  Estos juegos incluyen efectos aleatorios,
-  que puede ser divertido, frustrante o ambos :)
--
-  var varlist = [
-    "Chakart",
-    "Dice"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inspiradas por el movimiento del caballo
-
-p.
-  Variantes basadas en el movimiento del caballo,
-  que aumenta o transforma las capacidades de las piezas.
--
-  var varlist = [
-    "Balaklava",
-    "Knightmate1",
-    "Knightmate2",
-    "Knightrelay1",
-    "Knightrelay2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Disposición inicial inusual
-
-p Del diseño inicial resultado muchas capturas al principio del juego.
--
-  var varlist = [
-    "Doublearmy",
-    "Upsidedown",
-    "Pawnmassacre"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Variantes "fáciles": reglas simples
-
-p.
-  Las reglas ortodoxas solo cambian ligeramente,
-  lo que da un juego muy similar.
--
-  var varlist = [
-    "Coronation",
-    "Freecapture",
-    "Pocketknight"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p ...O conduce a estrategias muy diferentes:
--
-  var varlist = [
-    "Antimatter",
-    "Atomic1",
-    "Atomic2",
-    "Brotherhood",
-    "Checkless"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Tablero inicialmente vacío
-
-p.
-  Todas las piezas se agregan gradualmente desde un tablero vacío.
--
-  var varlist = [
-    "Parachute",
-    "Screen"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Reposicionamiento
-
-p.
-  Las piezas se pueden dejar caer en el tablero de ajedrez,
-  ya sea inmediatamente o más tarde en el juego.
--
-  var varlist = [
-    "Clorange",
-    "Crazyhouse",
-    "Madhouse",
-    "Rampage",
-    "Recycle",
-    "Shogun",
-    "Teleport1",
-    "Teleport2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inmovilización
-
-p Las piezas pueden paralizarse o controlarse en ciertas circunstancias.
--
-  var varlist = [
-    "Hypnotic",
-    "Isardam",
-    "Koopa",
-    "Madrasi",
-    "Mesmer"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Variantes regionales e históricas
-
-p Evolución (parcial) del juego en espacio y tiempo.
--
-  var varlist = [
-    "Janggi",
-    "Karouk",
-    "Makpong",
-    "Makruk",
-    "Minishogi",
-    "Minixiangqi",
-    "Shatranj",
-    "Shogi",
-    "Sittuyin",
-    "Xiangqi"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Carrera de reyes
-
-p El objetivo es cruzar el tablero de ajedrez con tu rey.
--
-  var varlist = [
-    "Racingkings",
-    "Royalrace"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Varias jugadas por turno
-
-p En estas variantes, puedes jugar dos o más movimientos por turno.
--
-  var varlist = [
-    "Avalanche",
-    "Doublemove1",
-    "Doublemove2",
-    "Monster",
-    "Progressive1",
-    "Progressive2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Una sola pieza poderosa contra un ejército
-
-p Muy pocas piezas pero muy poderosas contra todo un ejército.
--
-  var varlist = [
-    "Maharajah",
-    "Monster"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Intercambio de posiciones de piezas
-
-p Algunas o todas las piezas pueden intercambiarse.
--
-  var varlist = [
-    "Joker",
-    "Suction",
-    "Swap",
-    "Switching"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Objetivo diferente
-
-p Reglas ortodoxas, pero el objetivo no es jaque mate (o no solo).
--
-  var varlist = [
-    "Alapo",
-    "Castle",
-    "Crossing",
-    "Extinction",
-    "Threechecks",
-    "Kinglet",
-    "Koth",
-    "Squatter1",
-    "Squatter2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Aparte del Ajedrez
-
-p Algunos juegos no están relacionados con el ajedrez.
--
-  var varlist = [
-    "Atarigo",
-    "Avalam1",
-    "Avalam2",
-    "Emergo",
-    "Fanorona",
-    "Gomoku",
-    "Konane",
-    "Yote"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Varios
-
-p.
-  Estas variantes aún no están clasificadas, en general porque son
-  el único representante de su tipo en este sitio.
--
-  var varlist = [
-    "Alice",
-    "Align4",
-    "Ambiguous",
-    "Bario",
-    "Bicolour",
-    "Convert",
-    "Copycat",
-    "Evolution",
-    "Forward",
-    "Fusion",
-    "Gridolina",
-    "Hamilton",
-    "Iceage",
-    "Kingsmaker",
-    "Magnetic",
-    "Pandemonium1",
-    "Pandemonium2",
-    "Refusal1",
-    "Refusal2",
-    "Relayup",
-    "Rollerball",
-    "Selfabsorb",
-    "Takenmake",
-    "Wormhole1",
-    "Wormhole2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug
deleted file mode 100644
index ae68c665..00000000
--- a/client/src/translations/variants/fr.pug
+++ /dev/null
@@ -1,505 +0,0 @@
-p.text-center
-  a(href="https://www.chessvariants.com/what.html") Qu'est-ce qu'une variante ?
-  | &nbsp;&nbsp;&nbsp;&nbsp;
-  a(href="https://www.chessvariants.com/why.html") Pourquoi jouer aux variantes ?
-
-p
-  | La variante 
-  a(href="/#/variants/Chess960") Chess960
-  | &nbsp;permet de jouer avec les règles standard, depuis une position
-  | initiale aléatoire (ou non) symétrique (ou non).
-
-h3 Jeux simplifiés pour apprendre les échecs
-
-p Variantes avec très peu de pièces différentes, et un but simplifié.
--
-  var varlist = [
-    "Bishopawns",
-    "Discoduel",
-    "Dobutsu",
-    "Knightpawns",
-    "Pawns",
-    "Pawnsking",
-    "Queenpawns",
-    "Rookpawns"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Captures obligatoires
-
-p.
-  Dans une position donnée, il y a généralement moins de coups possibles que
-  dans le jeu orthodoxe puisque vous devez capturer.
--
-  var varlist = [
-    "Arena",
-    "Capture",
-    "Losers",
-    "Monocolor",
-    "Suicide"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Transformations
-
-p En général les pièces se transforment en capturant.
--
-  var varlist = [
-    "Absorption",
-    "Cannibal1",
-    "Cannibal2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Frontières modifiées
-
-p Échiquiers aux bords communiquants.
--
-  var varlist = [
-    "Circular",
-    "Cylinder"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Différents coups de pions
-
-p.
-  Tout se déroule comme au jeu orthodoxe,
-  mais les pions se déplacent d'une manière inhabituelle.
--
-  var varlist = [
-    "Berolina",
-    "Diamond",
-    "Vchess"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Armées différentes
-
-p Pièces standard contre une équipe de pièces différentes.
--
-  var varlist = [
-    "Colorbound",
-    "Empire",
-    "Horde",
-    "Orda",
-    "Shinobi",
-    "Spartan",
-    "Synochess"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inspirées par des jeux de ballon
-
-p.
-  Variantes utilisant un ballon, abstrait ou non,
-  qui doit traverser l'échiquier.
--
-  var varlist = [
-    "Ball",
-    "Football",
-    "Rugby"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Nouvelles pièces
-
-p.
-  Une large gamme de pièces féériques peuvent être introduites.
-  Certaines sont très puissantes comme l'Amazone,
-  d'autres plutôt faibles comme la Sauterelle.
--
-  var varlist = [
-    "Capablanca",
-    "Eightpieces",
-    "Fullcavalry",
-    "Grand",
-    "Grasshopper",
-    "Hoppelpoppel",
-    "Newzealand",
-    "Omega",
-    "Ordamirror",
-    "Perfect",
-    "Shako",
-    "Tencubed",
-    "Wildebeest"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p.
-  Dans les variantes suivantes, des pièces féériques (ou non)
-  peuvent apparaître plus tard dans le jeu :
--
-  var varlist = [
-    "Musketeer",
-    "Schess",
-    "Titan"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Plusieurs pièces royales
-
-p Dans ces jeux vous devez garder à l'abri deux "rois" ou plus.
--
-  var varlist = [
-    "Coregal",
-    "Twokings",
-    "Antiking1",
-    "Antiking2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Captures non orthodoxes
-
-p Captures non-standard, mais utilisant des mécanismes connus.
--
-  var varlist = [
-    "Enpassant",
-    "Rifle",
-    "Zen"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p.
-  Les captures s'effectuent généralement sans remplacement. C'est-à-dire
-  que vous ne remplacez pas les pièces adverses sur leur case de capture.
--
-  var varlist = [
-    "Allmate1",
-    "Allmate2",
-    "Baroque",
-    "Dynamo",
-    "Fugue",
-    "Interweave",
-    "Rococo",
-    "Maxima"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Pièces changeant de camp, pièces partagées
-
-p Les propriétaires (couleurs) des pièces peuvent changer pendant la partie.
--
-  var varlist = [
-    "Benedict",
-    "Checkered1",
-    "Checkered2",
-    "Otage",
-    "Pacifist1",
-    "Pacifist2",
-    "Pacosako"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Information incomplète
-
-p.
-  Une part de spéculation est requise pour ces variantes,
-  où certaines informations sur le jeu sont cachées.
--
-  var varlist = [
-    "Apocalypse",
-    "Dark",
-    "Hidden",
-    "Hiddenqueen",
-    "Stealthbomb1",
-    "Stealthbomb2",
-    "Synchrone1",
-    "Synchrone2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Facteurs aléatoires
-
-p.
-  Ces jeux incluent des effets aléatoires,
-  qui peuvent être amusants, frustrants ou bien les deux :)
--
-  var varlist = [
-    "Chakart",
-    "Dice"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Inspirées par le déplacement du cavalier
-
-p.
-  Variantes basées sur le coup de cavalier,
-  qui augmente ou transforme les capacités des pièces.
--
-  var varlist = [
-    "Balaklava",
-    "Knightmate1",
-    "Knightmate2",
-    "Knightrelay1",
-    "Knightrelay2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Arrangement initial inhabituel
-
-p L'agencement initial entraîne de nombreuses captures tôt dans la partie.
--
-  var varlist = [
-    "Doublearmy",
-    "Upsidedown",
-    "Pawnmassacre"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Variantes "faciles" : règles simples
-
-p.
-  Les règles orthodoxes ne sont que légèrement modifiées,
-  ce qui donne un jeu très similaire.
--
-  var varlist = [
-    "Coronation",
-    "Freecapture",
-    "Pocketknight"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-p ...Ou mène à des stratégies très différentes :
--
-  var varlist = [
-    "Antimatter",
-    "Atomic1",
-    "Atomic2",
-    "Brotherhood",
-    "Checkless"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Échiquier initialement vide
-
-p.
-  Toutes les pièces sont ajoutées progressivement depuis un échiquier vide.
--
-  var varlist = [
-    "Parachute",
-    "Screen"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Repositionnement
-
-p.
-  Les pièces peuvent être parachutées sur l'échiquier,
-  soit immédiatement soit plus tard dans la partie.
--
-  var varlist = [
-    "Clorange",
-    "Crazyhouse",
-    "Madhouse",
-    "Rampage",
-    "Recycle",
-    "Shogun",
-    "Teleport1",
-    "Teleport2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Immobilisation
-
-p Les pièces peuvent être paralysées ou contrôlées dans certaines circonstances.
--
-  var varlist = [
-    "Hypnotic",
-    "Isardam",
-    "Koopa",
-    "Madrasi",
-    "Mesmer"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Variantes régionales et historiques
-
-p Évolution (partielle) du jeu dans l'espace et le temps.
--
-  var varlist = [
-    "Janggi",
-    "Karouk",
-    "Makpong",
-    "Makruk",
-    "Minishogi",
-    "Minixiangqi",
-    "Shatranj",
-    "Shogi",
-    "Sittuyin",
-    "Xiangqi"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Course de rois
-
-p L'objectif est de traverser l'échiquier avec votre roi.
--
-  var varlist = [
-    "Racingkings",
-    "Royalrace"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Plusieurs coups par tour
-
-p Dans ces variantes, vous pouvez jouer deux coups ou plus par tour.
--
-  var varlist = [
-    "Avalanche",
-    "Doublemove1",
-    "Doublemove2",
-    "Progressive1",
-    "Progressive2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Une seule pièce puissante face à une armée
-
-p Très peu de pièces mais très puissantes face à une armée entière.
--
-  var varlist = [
-    "Maharajah",
-    "Monster"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Échange des positions des pièces
-
-p Certaines ou toutes les pièces peuvent être échangées.
--
-  var varlist = [
-    "Joker",
-    "Suction",
-    "Swap",
-    "Switching"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Objectif différent
-
-p Règles orthodoxes, mais le but n'est pas de mater (ou pas seulement).
--
-  var varlist = [
-    "Alapo",
-    "Castle",
-    "Crossing",
-    "Extinction",
-    "Threechecks",
-    "Kinglet",
-    "Koth",
-    "Squatter1",
-    "Squatter2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Hors Échecs
-
-p Quelques jeux a priori sans liens avec les échecs.
--
-  var varlist = [
-    "Atarigo",
-    "Avalam1",
-    "Avalam2",
-    "Emergo",
-    "Fanorona",
-    "Gomoku",
-    "Konane",
-    "Yote"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
-
-h3 Divers
-
-p.
-  Ces variantes ne sont pas encore classées, en général car elles sont
-  l'unique représentant de leur type sur ce site.
--
-  var varlist = [
-    "Alice",
-    "Align4",
-    "Ambiguous",
-    "Bario",
-    "Bicolour",
-    "Convert",
-    "Copycat",
-    "Evolution",
-    "Forward",
-    "Fusion",
-    "Gridolina",
-    "Hamilton",
-    "Iceage",
-    "Kingsmaker",
-    "Magnetic",
-    "Pandemonium1",
-    "Pandemonium2",
-    "Refusal1",
-    "Refusal2",
-    "Relayup",
-    "Rollerball",
-    "Selfabsorb",
-    "Takenmake",
-    "Wormhole1",
-    "Wormhole2"
-  ]
-ul
-  for v in varlist
-    li #[a(href="/#/variants/"+v) #{v}]
diff --git a/client/src/variants/Chess.js b/client/src/variants/Chess.js
new file mode 120000
index 00000000..8e6b27cd
--- /dev/null
+++ b/client/src/variants/Chess.js
@@ -0,0 +1 @@
+../base_rules.js
\ No newline at end of file
diff --git a/client/src/variants/Chess960.js b/client/src/variants/Chess960.js
deleted file mode 100644
index 15eb7ecc..00000000
--- a/client/src/variants/Chess960.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { ChessRules } from "@/base_rules";
-
-export class Chess960Rules extends ChessRules {
-  // Standard rules
-};
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index fc86e90d..3d73893e 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -7,7 +7,7 @@ main
   )
     .card
       label.modal-close(for="modalRules")
-      a#variantNameInGame(:href="'/#/variants/'+game.vname") {{ game.vname }}
+      a#variantNameInGame(:href="'/#/variants/'+game.vname") {{ game.vdisp }}
       div(v-html="rulesContent")
   input#modalScore.modal(type="checkbox")
   div#scoreDiv(
@@ -707,7 +707,7 @@ export default {
           const gameToSend = Object.keys(this.game)
             .filter(k =>
               [
-                "id","fen","players","vid","cadence","fenStart","vname",
+                "id","fen","players","vid","cadence","fenStart",
                 "moves","clocks","score","drawOffer","rematchOffer"
               ].includes(k))
             .reduce(
@@ -1024,7 +1024,6 @@ export default {
         {
           // (other) Game infos: constant
           fenStart: gameInfo.fen,
-          vname: this.game.vname,
           created: Date.now(),
           // Game state (including FEN): will be updated
           moves: [],
@@ -1282,18 +1281,33 @@ export default {
       if (!!callback) callback();
     },
     loadVariantThenGame: async function(game, callback) {
-      await import("@/variants/" + game.vname + ".js")
-      .then((vModule) => {
-        window.V = vModule[game.vname + "Rules"];
-        this.loadGame(game, callback);
-      });
-      this.rulesContent =
-        afterRawLoad(
-          require(
-            "raw-loader!@/translations/rules/" +
-            game.vname + "/" + this.st.lang + ".pug"
-          ).default
-        ).replace(/(fen:)([^:]*):/g, replaceByDiag);
+      const afterSetVname = async () => {
+        await import("@/variants/" + game.vname + ".js")
+        .then((vModule) => {
+          window.V = vModule[game.vname + "Rules"];
+          this.loadGame(game, callback);
+        });
+        this.rulesContent =
+          afterRawLoad(
+            require(
+              "raw-loader!@/translations/rules/" +
+              game.vname + "/" + this.st.lang + ".pug"
+            ).default
+          ).replace(/(fen:)([^:]*):/g, replaceByDiag);
+      };
+      let variant = undefined;
+      const trySetVname = setInterval(
+        () => {
+          // this.st.variants might be uninitialized (variant == null)
+          variant = this.st.variants.find(v => v.id == game.vid);
+          if (!!variant) {
+            clearInterval(trySetVname);
+            game.vname = variant.name;
+            game.vdisp = variant.display;
+            afterSetVname();
+          }
+        }, 500
+      );
     },
     // 3 cases for loading a game:
     //  - from indexedDB (running or completed live game I play)
diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue
index fe189413..7749dfad 100644
--- a/client/src/views/Hall.vue
+++ b/client/src/views/Hall.vue
@@ -48,7 +48,7 @@ main
               :value="v.id"
               :selected="newchallenge.vid==v.id"
             )
-              | {{ v.name }}
+              | {{ v.display }}
         fieldset
           label(for="cadence") {{ st.tr["Cadence"] }} *
           div#predefinedCadences
@@ -279,7 +279,7 @@ export default {
     "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) this.setVname(o);
       });
       if (!this.newchallenge.V && this.newchallenge.vid > 0)
         this.loadNewchallVariant();
@@ -315,7 +315,7 @@ export default {
         // Automatic challenge sending, for tournaments
         this.loadNewchallVariant(
           () => {
-            this.newchallenge = Object.assign(
+            Object.assign(
               this.newchallenge,
               {
                 fen: "",
@@ -419,7 +419,6 @@ export default {
                 const type = this.classifyObject(c);
                 const vname = this.getVname(c.vid);
                 return Object.assign(
-                  {},
                   {
                     type: type,
                     vname: vname,
@@ -554,10 +553,15 @@ export default {
         this.conn.send(JSON.stringify(Object.assign({ code: code }, obj)));
       }
     },
-    getVname: function(vid) {
-      const variant = this.st.variants.find(v => v.id == vid);
+    setVname: function(obj) {
+      const variant = this.st.variants.find(v => v.id == obj.vid);
       // this.st.variants might be uninitialized (variant == null)
-      return variant ? variant.name : "";
+      if (!!variant) {
+        obj.vname = variant.name;
+        obj.vdisp = variant.display;
+      }
+      // NOTE: Next line is used in loadNewchallVariant
+      return (!variant ? "" : variant.name);
     },
     filterChallenges: function(type) {
       return this.challenges.filter(c => c.type == type);
@@ -876,7 +880,7 @@ export default {
           ) {
             let newGame = game;
             newGame.type = this.classifyObject(game);
-            newGame.vname = this.getVname(game.vid);
+            this.setVname(game);
             if (!game.score)
               // New game from Hall
               newGame.score = "*";
@@ -944,15 +948,9 @@ export default {
               }
               this.cursor = res.games[L - 1].created;
               let moreGames = res.games.map(g => {
-                const vname = this.getVname(g.vid);
-                return Object.assign(
-                  {},
-                  g,
-                  {
-                    type: "corr",
-                    vname: vname
-                  }
-                );
+                this.setVname(g);
+                g.type = "corr";
+                return g;
               });
               this.games = this.games.concat(moreGames);
             }
@@ -976,7 +974,7 @@ export default {
         let fromValues = Object.assign({}, this.people[chall.from]);
         delete fromValues["pages"]; //irrelevant in this context
         newChall.from = Object.assign({ sid: chall.from }, fromValues);
-        newChall.vname = this.getVname(newChall.vid);
+        this.setVname(newChall);
         this.challenges.push(newChall);
         if (
           (newChall.type == "live" && this.cdisplay == "corr") ||
@@ -998,12 +996,11 @@ export default {
       }
     },
     loadNewchallVariant: async function(cb, vname) {
-      vname = vname || this.getVname(this.newchallenge.vid);
+      vname = vname || this.setVname(this.newchallenge);
       await import("@/variants/" + vname + ".js")
       .then((vModule) => {
         window.V = vModule[vname + "Rules"];
         this.newchallenge.V = window.V;
-        this.newchallenge.vname = vname;
         if (!!cb) cb();
       });
     },
@@ -1343,13 +1340,13 @@ export default {
     },
     // NOTE: for live games only (corr games start on the server)
     startNewGame: function(gameInfo) {
+      this.setVname(gameInfo);
       const game = Object.assign(
         {},
         gameInfo,
         {
           // (other) Game infos: constant
           fenStart: gameInfo.fen,
-          vname: this.getVname(gameInfo.vid),
           created: Date.now(),
           // Game state (including FEN): will be updated
           moves: [],
diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue
index 5c3522ec..3d86636f 100644
--- a/client/src/views/MyGames.vue
+++ b/client/src/views/MyGames.vue
@@ -84,6 +84,14 @@ export default {
   watch: {
     $route: function(to, from) {
       if (to.path != "/mygames") this.cleanBeforeDestroy();
+    },
+    // st.variants changes only once, at loading from [] to [...]
+    "st.variants": function() {
+      // Set potential games variant names + display:
+      this.livesGames.concat(this.corrGames).concat(this.importGames)
+      .forEach(o => {
+        if (!o.vname) this.setVname(o);
+      });
     }
   },
   created: function() {
@@ -191,6 +199,14 @@ export default {
           document.getElementById(t + "Games").classList.remove("active");
       }
     },
+    // TODO: duplicated from Hall.vue:
+    setVname: function(obj) {
+      const variant = this.st.variants.find(v => v.id == obj.vid);
+      if (!!variant) {
+        obj.vname = variant.name;
+        obj.vdisp = variant.display;
+      }
+    },
     addGameImport(game) {
       game.type = "import";
       ImportgameStorage.add(game, (err) => {
@@ -230,6 +246,7 @@ export default {
           if ((rem == 0 && g.myColor == 'w') || (rem == 1 && g.myColor == 'b'))
             g.myTurn = true;
         }
+        this.setVname(g);
       });
     },
     socketMessageListener: function(msg) {
@@ -260,15 +277,11 @@ export default {
           break;
         }
         case "notifynewgame": {
-          const gameInfo = data.data;
-          // st.variants might be uninitialized,
-          // if unlucky and newgame right after connect:
-          const v = this.st.variants.find(v => v.id == gameInfo.vid);
-          const vname = !!v ? v.name : "";
+          let gameInfo = data.data;
+          this.setVname(gameInfo);
           const type = (gameInfo.cadence.indexOf('d') >= 0 ? "corr": "live");
           let game = Object.assign(
             {
-              vname: vname,
               type: type,
               score: "*",
               created: Date.now()
@@ -385,7 +398,10 @@ export default {
           if (L > 0) {
             // Add "-1" because IDBKeyRange.upperBound includes boundary
             this.cursor["import"] = importGames[L - 1].created - 1;
-            importGames.forEach(g => g.type = "import");
+            importGames.forEach(g => {
+              g.type = "import";
+              this.setVname(g);
+            });
             this.importGames = this.importGames.concat(importGames);
           }
           else this.hasMore["import"] = false;
diff --git a/client/src/views/Problems.vue b/client/src/views/Problems.vue
index a55126fa..057035ac 100644
--- a/client/src/views/Problems.vue
+++ b/client/src/views/Problems.vue
@@ -8,7 +8,7 @@ main
     .card
       label.modal-close(for="modalRules")
       a#variantNameInProblems(:href="'/#/variants/'+game.vname")
-        | {{ game.vname }}
+        | {{ curproblem.vdisp }}
       div(v-html="rulesContent")
   input#modalNewprob.modal(
     type="checkbox"
@@ -63,7 +63,7 @@ main
         .button-group(v-if="canIedit(curproblem.uid)")
           button(@click="editProblem(curproblem)") {{ st.tr["Edit"] }}
           button(@click="deleteProblem(curproblem)") {{ st.tr["Delete"] }}
-        span.vname {{ curproblem.vname }}
+        span.vname {{ curproblem.vdisp }}
         span.uname ({{ curproblem.uname }})
         button.marginleft(@click="backToList()") {{ st.tr["Back to list"] }}
         button.nomargin(@click="gotoPrevNext(curproblem,1)")
@@ -108,7 +108,7 @@ main
           v-show="onlyMine || !selectedVar || p.vid == selectedVar"
           @click="setHrefPid(p)"
         )
-          td {{ p.vname }}
+          td {{ p.vdisp }}
           td {{ firstChars(p.instruction) }}
           td {{ p.id }}
       button#loadMoreBtn(
@@ -141,10 +141,7 @@ export default {
   data: function() {
     return {
       st: store.state,
-      emptyVar: {
-        vid: 0,
-        vname: ""
-      },
+      emptyVar: { vid: 0 },
       // Problem currently showed, or edited:
       curproblem: {
         id: 0, //used in case of edit
@@ -220,7 +217,9 @@ export default {
       t.style.height = (t.scrollHeight + 3) + "px";
     },
     setVname: function(prob) {
-      prob.vname = this.st.variants.find(v => v.id == prob.vid).name;
+      const variant = this.st.variants.find(v => v.id == prob.vid);
+      prob.vname = variant.name;
+      prob.vdisp = variant.display;
     },
     // Add vname and user names:
     decorate: function(problems, callback) {
@@ -250,7 +249,8 @@ export default {
             }
           }
         );
-      } else if (!!callback) callback();
+      }
+      else if (!!callback) callback();
     },
     firstChars: function(text) {
       let preparedText = text
@@ -282,6 +282,7 @@ export default {
       this.curproblem.uid = 0;
       this.curproblem.vid = "";
       this.curproblem.vname = "";
+      this.curproblem.vdisp = "";
       this.curproblem.fen = "";
       this.curproblem.diag = "";
       this.curproblem.instruction = "";
@@ -378,7 +379,8 @@ export default {
             }
           }
         );
-      } else processWhenWeHaveProb();
+      }
+      else processWhenWeHaveProb();
     },
     gotoPrevNext: function(prob, dir) {
       const mode = (this.onlyMine ? "mine" : "others");
@@ -495,7 +497,8 @@ export default {
                 // (Unless the user navigated several times by URL to show a
                 // single problem...)
                 .sort((p1, p2) => p2.added - p1.added);
-            } else this.hasMore[mode] = false;
+            }
+            else this.hasMore[mode] = false;
             if (!!cb) cb(L);
           }
         }
@@ -570,7 +573,7 @@ button#loadMoreBtn
   display: inline-block
 
 #topPage
-  span.vname
+  span.vdisp
     font-weight: bold
     padding-left: var(--universal-margin)
   span.uname
diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue
index c069eb29..09d5e06a 100644
--- a/client/src/views/Rules.vue
+++ b/client/src/views/Rules.vue
@@ -26,7 +26,7 @@ main
           | {{ st.tr["Analysis mode"] }}
   .row
     .col-sm-12.col-md-8.col-md-offset-2.col-lg-6.col-lg-offset-3
-      h4#variantName(v-show="display=='rules'") {{ gameInfo.vname }}
+      h4#variantName(v-show="display=='rules'") {{ getVariantDisplay }}
       div(
         v-show="display=='rules'"
         v-html="content"
@@ -76,6 +76,10 @@ export default {
     showAnalyzeBtn: function() {
       return !!this.V && this.V.CanAnalyze;
     },
+    getVariantDisplay: function() {
+      if (!this.gameInfo.vname) return ""; //variant not set yet
+      return this.st.variants.find(v => v.name == this.gameInfo.vname).display;
+    },
     content: function() {
       if (!this.gameInfo.vname) return ""; //variant not set yet
       return (
diff --git a/client/src/views/VariantList.vue b/client/src/views/VariantList.vue
index e5d02856..5f230107 100644
--- a/client/src/views/VariantList.vue
+++ b/client/src/views/VariantList.vue
@@ -12,7 +12,7 @@ main
       :class="getVclasses(filteredVariants, idx)"
     )
       router-link(:to="getLink(v.name)")
-        h4.boxtitle.text-center {{ v.name }}
+        h4.boxtitle.text-center {{ v.display }}
         p.description.text-center {{ st.tr[v.desc] }}
 </template>
 
@@ -41,7 +41,8 @@ export default {
         .map(v => {
           return {
             name: v.name,
-            desc: v.description
+            desc: v.description,
+            display: v.display
           };
         })
         .sort((a, b) => {
diff --git a/client/src/views/Variants.vue b/client/src/views/Variants.vue
index da6478b4..4374fd04 100644
--- a/client/src/views/Variants.vue
+++ b/client/src/views/Variants.vue
@@ -4,27 +4,57 @@ main
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
       a#mainLink(href="/#/variants/list")
         | {{ st.tr["View alphabetical variants list"] }}
-      div(v-html="content")
+      p.text-center
+        a.leftLink(href="https://www.chessvariants.com/what.html")
+          | {{ st.tr["What is a chess variant?"] }}
+        a(href="https://www.chessvariants.com/why.html")
+          | {{ st.tr["Why play chess variants?"] }}
+      p
+        a(href="/#/variants/Chess") Chess
+        | {{ st.tr["chess_v"] }}
+      div(v-for="g of sortedGroups")
+        h3 {{ st.tr["vt" + g] }}
+        p {{ st.tr["vg" + g] }}
+        ul
+          li(v-for="v of variantGroup.get(g)")
+            a(:href="getLink(v)") {{ v.display }}
+            | &nbsp&ndash;&nbsp;
+            | {{ v.description }}
 </template>
 
 <script>
 import { store } from "@/store";
-import afterRawLoad from "@/utils/afterRawLoad";
+import { ajax } from "@/utils/ajax";
 export default {
   name: "my-variants",
   data: function() {
     return {
-      st: store.state
+      st: store.state,
+      variantGroup: []
     };
   },
+  created: function() {
+    ajax(
+      "/variants",
+      "GET",
+      {
+        success: (res) => {
+          this.variantGroup = new Map();
+          res.variantArray.forEach((v) => {
+            if (v.groupe >= 0) {
+              let collection = this.variantGroup.get(v.groupe);
+              if (!collection) this.variantGroup.set(v.groupe, [v]);
+              else collection.push(v);
+            }
+          });
+        }
+      }
+    );
+  },
   computed: {
-    content: function() {
+    sortedGroups: function() {
       return (
-        afterRawLoad(
-          require(
-            "raw-loader!@/translations/variants/" + this.st.lang + ".pug"
-          ).default
-        )
+        Array.from(this.variantGroup.keys()).sort((a, b) => (a < b ? -1 : 1))
       );
     }
   },
@@ -33,8 +63,8 @@ export default {
     setCurPrefix: function(e) {
       this.curPrefix = e.target.value;
     },
-    getLink: function(vname) {
-      return "/variants/" + vname;
+    getLink: function(variant) {
+      return "/#/variants/" + variant.name;
     },
     getVclasses: function(varray, idx) {
       const idxMod2 = idx % 2;
@@ -49,6 +79,9 @@ export default {
 </script>
 
 <style lang="sass" scoped>
+a.leftLink
+  margin-right: 15px
+
 a#mainLink
   display: block
   margin: 10px auto
diff --git a/server/db/create.sql b/server/db/create.sql
index e4035a15..e26b03b9 100644
--- a/server/db/create.sql
+++ b/server/db/create.sql
@@ -3,6 +3,8 @@
 create table Variants (
   id integer primary key,
   name varchar unique,
+  display varchar,
+  groupe integer,
   description text,
   noProblems boolean
 );
diff --git a/server/db/populate.sql b/server/db/populate.sql
index e7e7b702..9af2bacb 100644
--- a/server/db/populate.sql
+++ b/server/db/populate.sql
@@ -1,181 +1,181 @@
 -- Re-run this script after variants are added
 
-insert or ignore into Variants (name, description, noProblems) values
-  ('Apocalypse', 'The end of the world', true),
-  ('Chakart', 'Capture the princess', true),
-  ('Dark', 'In the shadow', true),
-  ('Dice', 'Roll the dice', true),
-  ('Hidden', 'Unidentified pieces', true),
-  ('Hiddenqueen', 'Queen disguised as a pawn', true),
-  ('Synchrone1', 'Play at the same time (v1)', true),
-  ('Synchrone2', 'Play at the same time (v2)', true);
+insert or ignore into Variants (name, description, noProblems, groupe, display) values
+  ('Apocalypse', 'The end of the world', true, 13, 'Apocalypse'),
+  ('Chakart', 'Capture the princess', true, 14, 'Chakart'),
+  ('Dark', 'In the shadow', true, 13, 'Dark Chess'),
+  ('Dice', 'Roll the dice', true, 14, 'Dice Chess'),
+  ('Hidden', 'Unidentified pieces', true, 13, 'Strate-Go'),
+  ('Hiddenqueen', 'Queen disguised as a pawn', true, 13, 'Hidden Queen'),
+  ('Stealthbomb1', 'Beware the bomb (v1)', true, 13, 'Stealth Bomb 1'),
+  ('Stealthbomb2', 'Beware the bomb (v2)', true, 13, 'Stealth Bomb 2'),
+  ('Synchrone1', 'Play at the same time (v1)', true, 13, 'Synchrone 1'),
+  ('Synchrone2', 'Play at the same time (v2)', true, 13, 'Synchrone 2');
 
-insert or ignore into Variants (name, description) values
-  ('Absorption', 'Absorb powers'),
-  ('Alapo', 'Geometric Chess'),
-  ('Alice', 'Both sides of the mirror'),
-  ('Align4', 'Align four pawns'),
-  ('Allmate1', 'Mate any piece (v1)'),
-  ('Allmate2', 'Mate any piece (v2)'),
-  ('Ambiguous', 'Play opponent''s pieces'),
-  ('Antiking1', 'Keep antiking in check (v1)'),
-  ('Antiking2', 'Keep antiking in check (v2)'),
-  ('Antimatter', 'Dangerous collisions'),
-  ('Arena', 'Middle battle'),
-  ('Atarigo', 'First capture wins'),
-  ('Atomic1', 'Explosive captures (v1)'),
-  ('Atomic2', 'Explosive captures (v2)'),
-  ('Avalam1', 'Build towers (v1)'),
-  ('Avalam2', 'Build towers (v2)'),
-  ('Avalanche', 'Pawnfalls'),
-  ('Ball', 'Score a goal'),
-  ('Balaklava', 'Meet the Mammoth'),
-  ('Bario', 'A quantum story'),
-  ('Baroque', 'Exotic captures'),
-  ('Benedict', 'Change colors'),
-  ('Berolina', 'Pawns move diagonally'),
-  ('Bicolour', 'Harassed kings'),
-  ('Bishopawns', 'Bishop versus pawns'),
-  ('Brotherhood', 'Friendly pieces'),
-  ('Cannibal1', 'Capture powers (v1)'),
-  ('Cannibal2', 'Capture powers (v2)'),
-  ('Capablanca', 'Capablanca Chess'),
-  ('Capture', 'Mandatory captures'),
-  ('Castle', 'Win by castling long'),
-  ('Checkered1', 'Shared pieces (v1)'),
-  ('Checkered2', 'Shared pieces (v2)'),
-  ('Checkless', 'No-check mode'),
-  ('Chess960', 'Standard rules'),
-  ('Circular', 'Run forward'),
-  ('Clorange', 'A Clockwork Orange'),
-  ('Colorbound', 'The colorbound clobberers'),
-  ('Convert', 'Convert enemy pieces'),
-  ('Copycat', 'Borrow powers'),
-  ('Coregal', 'Two royal pieces'),
-  ('Coronation', 'Long live the Queen'),
-  ('Crazyhouse', 'Captures reborn'),
-  ('Crossing', 'Cross the river'),
-  ('Cylinder', 'Neverending rows'),
-  ('Diamond', 'Rotating board'),
-  ('Discoduel', 'Enter the disco'),
-  ('Dobutsu', 'Let''s catch the Lion!'),
-  ('Doublearmy', '64 pieces on the board'),
-  ('Doublemove1', 'Double moves (v1)'),
-  ('Doublemove2', 'Double moves (v2)'),
-  ('Dynamo', 'Push and pull'),
-  ('Eightpieces', 'Each piece is unique'),
-  ('Emergo', 'Stacking Checkers variant'),
-  ('Empire', 'Empire versus Kingdom'),
-  ('Enpassant', 'Capture en passant'),
-  ('Evolution', 'Faster development'),
-  ('Extinction', 'Capture all of a kind'),
-  ('Fanorona', 'Malagasy Draughts'),
-  ('Football', 'Score a goal'),
-  ('Forward', 'Moving forward'),
-  ('Freecapture', 'Capture both colors'),
-  ('Fugue', 'Baroque Music'),
-  ('Fullcavalry', 'Lancers everywhere'),
-  ('Fusion', 'Fusion pieces (v1)'),
-  ('Gomoku', 'Align five stones'),
-  ('Grand', 'Big board'),
-  ('Grasshopper', 'Long jumps over pieces'),
-  ('Gridolina', 'Jump the borders'),
-  ('Hamilton', 'Walk on a graph'),
-  ('Hoppelpoppel', 'Knibis and Bisknis'),
-  ('Horde', 'A pawns cloud'),
-  ('Hypnotic', 'Mind control (v1)'),
-  ('Iceage', 'Ice Age is coming!'),
-  ('Interweave', 'Interweaved colorbound teams'),
-  ('Isardam', 'No paralyzed pieces'),
-  ('Janggi', 'Korean Chess'),
-  ('Joker', 'Replace pieces'),
-  ('Karouk', 'Thai Chess (v3)'),
-  ('Kinglet', 'Protect your pawns'),
-  ('Kingsmaker', 'Promote into kings'),
-  ('Knightmate1', 'Mate the knight (v1)'),
-  ('Knightmate2', 'Mate the knight (v2)'),
-  ('Knightpawns', 'Knight versus pawns'),
-  ('Knightrelay1', 'Move like a knight (v1)'),
-  ('Knightrelay2', 'Move like a knight (v2)'),
-  ('Konane', 'Hawaiian Checkers'),
-  ('Koopa', 'Stun & kick pieces'),
-  ('Koth', 'King of the Hill'),
-  ('Losers', 'Get strong at self-mate'),
-  ('Madhouse', 'Rearrange enemy pieces'),
-  ('Madrasi', 'Paralyzed pieces'),
-  ('Magnetic', 'Laws of attraction'),
-  ('Maharajah', 'Augmented Queens'),
-  ('Makpong', 'Thai Chess (v2)'),
-  ('Makruk', 'Thai Chess (v1)'),
-  ('Maxima', 'Occupy the enemy palace'),
-  ('Mesmer', 'Mind control (v2)'),
-  ('Minishogi', 'Shogi 5 x 5'),
-  ('Minixiangqi', 'Xiangqi 7 x 7'),
-  ('Monocolor', 'All of the same color'),
-  ('Monster', 'White move twice'),
-  ('Musketeer', 'New fairy pieces'),
-  ('Newzealand', 'Kniros and Rosknis'),
-  ('Omega', 'A wizard in the corner'),
-  ('Orda', 'Mongolian Horde (v1)'),
-  ('Ordamirror', 'Mongolian Horde (v2)'),
-  ('Otage', 'Capture and release hostages'),
-  ('Pacifist1', 'Convert & support (v1)'),
-  ('Pacifist2', 'Convert & support (v2)'),
-  ('Pacosako', 'Dance with the King'),
-  ('Pandemonium1', 'Noise and confusion (v1)'),
-  ('Pandemonium2', 'Noise and confusion (v2)'),
-  ('Parachute', 'Landing on the board'),
-  ('Pawnmassacre', 'Pieces upside down'),
-  ('Pawns', 'Reach the last rank (v1)'),
-  ('Pawnsking', 'Reach the last rank (v2)'),
-  ('Perfect', 'Powerful pieces'),
-  ('Pocketknight', 'Knight in pocket'),
-  ('Progressive1', 'Play more and more moves (v1)'),
-  ('Progressive2', 'Play more and more moves (v2)'),
-  ('Queenpawns', 'Queen versus pawns'),
-  ('Racingkings', 'Kings cross the 8x8 board'),
-  ('Rampage', 'Move under cover'),
-  ('Relayup', 'Upgrade pieces'),
-  ('Rifle', 'Shoot pieces'),
-  ('Recycle', 'Reuse pieces'),
-  ('Refusal1', 'Do not play that! (v1)'),
-  ('Refusal2', 'Do not play that! (v2)'),
-  ('Rollerball', 'As in the movie'),
-  ('Rococo', 'Capture on the edge'),
-  ('Rookpawns', 'Rook versus pawns'),
-  ('Royalrace', 'Kings cross the 11x11 board'),
-  ('Rugby', 'Transform an essay'),
-  ('Schess', 'Seirawan-Harper Chess'),
-  ('Screen', 'Free initial setup'),
-  ('Selfabsorb', 'Fusion pieces (v2)'),
-  ('Shako', 'Non-conformism and utopia'),
-  ('Shatranj', 'Ancient rules'),
-  ('Shinobi', 'A story of invasion'),
-  ('Shogi', 'Japanese Chess'),
-  ('Shogun', 'General''s Chess'),
-  ('Sittuyin', 'Burmese Chess'),
-  ('Spartan', 'Spartan versus Persians'),
-  ('Squatter1', 'Squat last rank (v1)'),
-  ('Squatter2', 'Squat last rank (v2)'),
-  ('Stealthbomb1', 'Beware the bomb (v1)'),
-  ('Stealthbomb2', 'Beware the bomb (v2)'),
-  ('Suicide', 'Lose all pieces'),
-  ('Suction', 'Attract opposite king'),
-  ('Swap', 'Dangerous captures'),
-  ('Switching', 'Exchange pieces'' positions'),
-  ('Synochess', 'Dynasty versus Kingdom'),
-  ('Takenmake', 'Prolongated captures'),
-  ('Teleport1', 'Reposition pieces (v1)'),
-  ('Teleport2', 'Reposition pieces (v2)'),
-  ('Tencubed', 'Four new pieces'),
-  ('Threechecks', 'Give three checks'),
-  ('Titan', 'Extra bishops and knights'),
-  ('Twokings', 'Two kings'),
-  ('Upsidedown', 'Board upside down'),
-  ('Vchess', 'Pawns capture backward'),
-  ('Wildebeest', 'Balanced sliders & leapers'),
-  ('Wormhole1', 'Squares disappear (v1)'),
-  ('Wormhole2', 'Squares disappear (v2)'),
-  ('Xiangqi', 'Chinese Chess'),
-  ('Yote', 'African Draughts'),
-  ('Zen', 'Reverse captures');
+insert or ignore into Variants (name, description, groupe, display) values
+  ('Absorption', 'Absorb powers', 2, 'Absorption'),
+  ('Alapo', 'Geometric Chess', 27, 'Alapo'),
+  ('Alice', 'Both sides of the mirror', 31, 'Alice Chess'),
+  ('Align4', 'Align four pawns', 31, 'Align4'),
+  ('Allmate1', 'Mate any piece (v1)', 11, 'Allmate1'),
+  ('Allmate2', 'Mate any piece (v2)', 11, 'Allmate2'),
+  ('Ambiguous', 'Play opponent''s pieces', 29, 'Ambiguous'),
+  ('Antiking1', 'Keep antiking in check (v1)', 9, 'Anti-King 1'),
+  ('Antiking2', 'Keep antiking in check (v2)', 9, 'Anti-King 2'),
+  ('Antimatter', 'Dangerous collisions', 18, 'Antimatter'),
+  ('Arena', 'Middle battle', 1, 'Arena'),
+  ('Atarigo', 'First capture wins', 28, 'Atari-Go'),
+  ('Atomic1', 'Explosive captures (v1)', 18, 'Atomic 1'),
+  ('Atomic2', 'Explosive captures (v2)', 18, 'Atomic 2'),
+  ('Avalam1', 'Build towers (v1)', 28, 'Avalam 1'),
+  ('Avalam2', 'Build towers (v2)', 28, 'Avalam 2'),
+  ('Avalanche', 'Pawnfalls', 24, 'Avalanche'),
+  ('Ball', 'Score a goal', 6, 'Ball'),
+  ('Balaklava', 'Meet the Mammoth', 15, 'Balaklava'),
+  ('Bario', 'A quantum story', 31, 'Bario'),
+  ('Baroque', 'Exotic captures', 11, 'Baroque'),
+  ('Benedict', 'Change colors', 12, 'Benedict'),
+  ('Berolina', 'Pawns move diagonally', 4, 'Berolina'),
+  ('Bicolour', 'Harassed kings', 31, 'Bicolour'),
+  ('Bishopawns', 'Bishop versus pawns', 0, 'Bishop-Pawns'),
+  ('Brotherhood', 'Friendly pieces', 18, 'Brotherhood'),
+  ('Cannibal1', 'Capture powers (v1)', 2, 'Cannibal 1'),
+  ('Cannibal2', 'Capture powers (v2)', 2, 'Cannibal 2'),
+  ('Capablanca', 'Capablanca Chess', 7, 'Capablanca Chess'),
+  ('Capture', 'Mandatory captures', 1, 'Capture'),
+  ('Castle', 'Win by castling long', 27, 'Castle'),
+  ('Checkered1', 'Shared pieces (v1)', 12, 'Checkered 1'),
+  ('Checkered2', 'Shared pieces (v2)', 12, 'Checkered 2'),
+  ('Checkless', 'No-check mode', 18, 'Checkless'),
+  ('Chess', 'Standard rules', -1, 'Chess'),
+  ('Circular', 'Run forward', 3, 'Circular Chess'),
+  ('Clorange', 'A Clockwork Orange', 20, 'Clockwork Orange'),
+  ('Colorbound', 'The colorbound clobberers', 5, 'Colorbound Clobberers'),
+  ('Convert', 'Convert enemy pieces', 12, 'Convert'),
+  ('Copycat', 'Borrow powers', 30, 'Copycat'),
+  ('Coregal', 'Two royal pieces', 9, 'Coregal'),
+  ('Coronation', 'Long live the Queen', 17, 'Coronation'),
+  ('Crazyhouse', 'Captures reborn', 20, 'Crazyhouse'),
+  ('Crossing', 'Cross the river', 27, 'Crossing'),
+  ('Cylinder', 'Neverending rows', 3, 'Cylindrical Chess'),
+  ('Diamond', 'Rotating board', 4, 'Diamond'),
+  ('Discoduel', 'Enter the disco', 0, 'Disco Duel'),
+  ('Dobutsu', 'Let''s catch the Lion!', 0, 'Dobutsu'),
+  ('Doublearmy', '64 pieces on the board', 16, 'Double Army'),
+  ('Doublemove1', 'Double moves (v1)', 24, 'Doublemove 1'),
+  ('Doublemove2', 'Double moves (v2)', 24, 'Doublemove 2'),
+  ('Dynamo', 'Push and pull', 11, 'Dynamo'),
+  ('Eightpieces', 'Each piece is unique', 7, 'Eightpieces'),
+  ('Emergo', 'Stacking Checkers variant', 28, 'Emergo'),
+  ('Empire', 'Empire versus Kingdom', 5, 'Empire Chess'),
+  ('Enpassant', 'Capture en passant', 10, 'En-passant'),
+  ('Evolution', 'Faster development', 31, 'Evolution'),
+  ('Extinction', 'Capture all of a kind', 27, 'Extinction'),
+  ('Fanorona', 'Malagasy Draughts', 28, 'Fanorona'),
+  ('Football', 'Score a goal', 6, 'Football'),
+  ('Forward', 'Moving forward', 31, 'Forward'),
+  ('Freecapture', 'Capture both colors', 17, 'Free Capture'),
+  ('Fugue', 'Baroque Music', 11, 'Fugue'),
+  ('Fullcavalry', 'Lancers everywhere', 7, 'Full Cavalry'),
+  ('Fusion', 'Fusion pieces (v1)', 31, 'Fusion Chess'),
+  ('Gomoku', 'Align five stones', 28, 'Gomoku'),
+  ('Grand', 'Big board', 7, 'Grand Chess'),
+  ('Grasshopper', 'Long jumps over pieces', 7, 'Grasshopper'),
+  ('Gridolina', 'Jump the borders', 31, 'Gridolina'),
+  ('Hamilton', 'Walk on a graph', 31, 'Hamilton'),
+  ('Hoppelpoppel', 'Knibis and Bisknis', 7, 'Hoppel-Poppel'),
+  ('Horde', 'A pawns cloud', 5, 'Horde'),
+  ('Hypnotic', 'Mind control (v1)', 21, 'Hypnotic'),
+  ('Iceage', 'Ice Age is coming!', 31, 'Ice Age'),
+  ('Interweave', 'Interweaved colorbound teams', 11, 'Interweave'),
+  ('Isardam', 'No paralyzed pieces', 21, 'Isardam'),
+  ('Janggi', 'Korean Chess', 22, 'Janggi'),
+  ('Joker', 'Replace pieces', 26, 'Joker'),
+  ('Karouk', 'Thai Chess (v3)', 22, 'Karouk'),
+  ('Kinglet', 'Protect your pawns', 27, 'Kinglet'),
+  ('Kingsmaker', 'Promote into kings', 31, 'Kingsmaker'),
+  ('Knightmate1', 'Mate the knight (v1)', 15, 'Knightmate 1'),
+  ('Knightmate2', 'Mate the knight (v2)', 15, 'Knightmate 2'),
+  ('Knightpawns', 'Knight versus pawns', 0, 'Knight-Pawns'),
+  ('Knightrelay1', 'Move like a knight (v1)', 15, 'Knightrelay 1'),
+  ('Knightrelay2', 'Move like a knight (v2)', 15, 'Knightrelay 2'),
+  ('Konane', 'Hawaiian Checkers', 28, 'Konane'),
+  ('Koopa', 'Stun & kick pieces', 21, 'Koopa'),
+  ('Koth', 'King of the Hill', 27, 'King of the Hill'),
+  ('Losers', 'Get strong at self-mate', 1, 'Losers'),
+  ('Madhouse', 'Rearrange enemy pieces', 20, 'Madhouse'),
+  ('Madrasi', 'Paralyzed pieces', 21, 'Madrasi'),
+  ('Magnetic', 'Laws of attraction', 31, 'Magnetic'),
+  ('Maharajah', 'Augmented Queens', 25, 'Maharajah'),
+  ('Makpong', 'Thai Chess (v2)', 22, 'Makpong'),
+  ('Makruk', 'Thai Chess (v1)', 22, 'Makruk'),
+  ('Maxima', 'Occupy the enemy palace', 11, 'Maxima'),
+  ('Mesmer', 'Mind control (v2)', 21, 'Mesmer'),
+  ('Minishogi', 'Shogi 5 x 5', 22, 'Minishogi'),
+  ('Minixiangqi', 'Xiangqi 7 x 7', 22, 'Minixiangqi'),
+  ('Monocolor', 'All of the same color', 1, 'Monocolor'),
+  ('Monster', 'White move twice', 25, 'Monster'),
+  ('Musketeer', 'New fairy pieces', 8, 'Musketeer Chess'),
+  ('Newzealand', 'Kniros and Rosknis', 7, 'New-Zealand Chess'),
+  ('Omega', 'A wizard in the corner', 7, 'Omega'),
+  ('Orda', 'Mongolian Horde (v1)', 5, 'Orda'),
+  ('Ordamirror', 'Mongolian Horde (v2)', 7, 'Orda Mirror'),
+  ('Otage', 'Capture and release hostages', 12, 'Otage'),
+  ('Pacifist1', 'Convert & support (v1)', 12, 'Pacifist 1'),
+  ('Pacifist2', 'Convert & support (v2)', 12, 'Pacifist 2'),
+  ('Pacosako', 'Dance with the King', 12, 'Paco-Sako'),
+  ('Pandemonium1', 'Noise and confusion (v1)', 20, 'Pandemonium 1'),
+  ('Pandemonium2', 'Noise and confusion (v2)', 20, 'Pandemonium 2'),
+  ('Parachute', 'Landing on the board', 19, 'Parachute'),
+  ('Pawnmassacre', 'Pieces upside down', 16, 'Pawn Massacre'),
+  ('Pawns', 'Reach the last rank (v1)', 0, 'Pawns'),
+  ('Pawnsking', 'Reach the last rank (v2)', 0, 'Pawns King'),
+  ('Perfect', 'Powerful pieces', 7, 'Perfect Chess'),
+  ('Pocketknight', 'Knight in pocket', 17, 'Pocket Knight'),
+  ('Progressive1', 'Play more and more moves (v1)', 24, 'Progressive 1'),
+  ('Progressive2', 'Play more and more moves (v2)', 24, 'Progressive 2'),
+  ('Queenpawns', 'Queen versus pawns', 0, 'Queen-Pawns'),
+  ('Racingkings', 'Kings cross the 8x8 board', 23, 'Racing Kings'),
+  ('Rampage', 'Move under cover', 20, 'Rampage'),
+  ('Relayup', 'Upgrade pieces', 30, 'Relay-up'),
+  ('Rifle', 'Shoot pieces', 10, 'Rifle Chess'),
+  ('Recycle', 'Reuse pieces', 20, 'Recycle Chess'),
+  ('Refusal1', 'Do not play that! (v1)', 29, 'Refusal 1'),
+  ('Refusal2', 'Do not play that! (v2)', 29, 'Refusal 2'),
+  ('Rollerball', 'As in the movie', 31, 'Rollerball'),
+  ('Rococo', 'Capture on the edge', 11, 'Rococo'),
+  ('Rookpawns', 'Rook versus pawns', 0, 'Rook-Pawns'),
+  ('Royalrace', 'Kings cross the 11x11 board', 23, 'Royal Race'),
+  ('Rugby', 'Transform an essay', 6, 'Rugby'),
+  ('Schess', 'Seirawan-Harper Chess', 8, 'Seirawan-Harper Chess'),
+  ('Screen', 'Free initial setup', 19, 'Screen Chess'),
+  ('Selfabsorb', 'Fusion pieces (v2)', 31, 'Self-Absorption'),
+  ('Shako', 'Non-conformism and utopia', 7, 'Shako'),
+  ('Shatranj', 'Ancient rules', 22, 'Shatranj'),
+  ('Shinobi', 'A story of invasion', 5, 'Shinobi'),
+  ('Shogi', 'Japanese Chess', 22, 'Shogi'),
+  ('Shogun', 'General''s Chess', 20, 'Shogun'),
+  ('Sittuyin', 'Burmese Chess', 22, 'Sittuyin'),
+  ('Spartan', 'Spartan versus Persians', 5, 'Spartan Chess'),
+  ('Squatter1', 'Squat last rank (v1)', 27, 'Squatter 1'),
+  ('Squatter2', 'Squat last rank (v2)', 27, 'Squatter 2'),
+  ('Suicide', 'Lose all pieces', 1, 'Suicide'),
+  ('Suction', 'Attract opposite king', 26, 'Suction'),
+  ('Swap', 'Dangerous captures', 26, 'Swap'),
+  ('Switching', 'Exchange pieces'' positions', 26, 'Switching'),
+  ('Synochess', 'Dynasty versus Kingdom', 5, 'Synochess'),
+  ('Takenmake', 'Prolongated captures', 31, 'Take and make'),
+  ('Teleport1', 'Reposition pieces (v1)', 20, 'Teleport 1'),
+  ('Teleport2', 'Reposition pieces (v2)', 20, 'Teleport 2'),
+  ('Tencubed', 'Four new pieces', 7, 'Tencubed'),
+  ('Threechecks', 'Give three checks', 27, 'Three Checks'),
+  ('Titan', 'Extra bishops and knights', 8, 'Titan Chess'),
+  ('Twokings', 'Two kings', 9, 'Two Kings'),
+  ('Upsidedown', 'Board upside down', 16, 'Upside-down'),
+  ('Vchess', 'Pawns capture backward', 4, 'Victor Chess'),
+  ('Wildebeest', 'Balanced sliders & leapers', 7, 'Wildebeest'),
+  ('Wormhole1', 'Squares disappear (v1)', 31, 'Wormhole 1'),
+  ('Wormhole2', 'Squares disappear (v2)', 31, 'Wormhole 2'),
+  ('Xiangqi', 'Chinese Chess', 22, 'Xiangqi'),
+  ('Yote', 'African Draughts', 28, 'Yote'),
+  ('Zen', 'Reverse captures', 10, 'Zen Chess');
diff --git a/server/models/Game.js b/server/models/Game.js
index 5f9ab734..7c3c8081 100644
--- a/server/models/Game.js
+++ b/server/models/Game.js
@@ -84,15 +84,11 @@ const GameModel = {
     db.serialize(function() {
       let query =
         "SELECT " +
-          "g.id, g.fen, g.fenStart, g.cadence, g.created, " +
-          "g.white, g.black, g.randomness, g.score, g.scoreMsg, " +
-          "g.chatReadWhite, g.chatReadBlack, g.drawOffer, " +
-          // TODO: vid and vname are redundant
-          "g.rematchOffer, v.id as vid, v.name AS vname " +
-        "FROM Games g " +
-        "JOIN Variants v " +
-        "  ON g.vid = v.id " +
-        "WHERE g.id = " + id;
+          "id, vid, fen, fenStart, cadence, created, " +
+          "white, black, randomness, score, scoreMsg, " +
+          "chatReadWhite, chatReadBlack, drawOffer, rematchOffer " +
+        "FROM Games " +
+        "WHERE id = " + id;
       db.get(query, (err, gameInfo) => {
         if (!gameInfo) {
           cb(err || { errmsg: "Game not found" }, undefined);
@@ -118,13 +114,12 @@ const GameModel = {
               "WHERE gid = " + id;
             db.all(query, (err4, chats) => {
               const game = Object.assign(
-                {},
-                gameInfo,
                 {
                   players: players,
                   moves: moves,
                   chats: chats
-                }
+                },
+                gameInfo
               );
               cb(null, game);
             });
@@ -186,11 +181,8 @@ const GameModel = {
   getRunning: function(uid, cb) {
     db.serialize(function() {
       let query =
-        "SELECT g.id, g.cadence, g.created, " +
-          "g.white, g.black, v.name AS vname " +
-        "FROM Games g " +
-        "JOIN Variants v " +
-        "  ON g.vid = v.id " +
+        "SELECT id, vid, cadence, created, white, black " +
+        "FROM Games " +
         "WHERE score = '*' AND (white = " + uid + " OR black = " + uid + ")";
       db.all(query, (err, games) => {
         // Get movesCount (could be done in // with next query)
@@ -240,12 +232,9 @@ const GameModel = {
   getCompleted: function(uid, cursor, cb) {
     db.serialize(function() {
       let query =
-        "SELECT g.id, g.cadence, g.created, g.score, g.scoreMsg, " +
-          "g.white, g.black, g.deletedByWhite, g.deletedByBlack, " +
-          "v.name AS vname " +
-        "FROM Games g " +
-        "JOIN Variants v " +
-        "  ON g.vid = v.id " +
+        "SELECT id, vid, cadence, created, score, scoreMsg, " +
+          "white, black, deletedByWhite, deletedByBlack " +
+        "FROM Games " +
         "WHERE " +
         "  score <> '*' AND" +
         "  created < " + cursor + " AND" +
@@ -279,7 +268,7 @@ const GameModel = {
               g => {
                 return {
                   id: g.id,
-                  vname: g.vname,
+                  vid: g.vid,
                   cadence: g.cadence,
                   created: g.created,
                   score: g.score,
diff --git a/server/models/Variant.js b/server/models/Variant.js
index d01d2a27..f439e192 100644
--- a/server/models/Variant.js
+++ b/server/models/Variant.js
@@ -4,7 +4,10 @@ const db = require("../utils/database");
  * Structure:
  *   id: integer
  *   name: varchar
+ *   display: varchar
+ *   groupe: integer
  *   description: varchar
+ *   noProblems: boolean
  */
 
 const VariantModel = {
-- 
2.44.0