Fix Eightpieces, add some simple variants, add a basic variants classification instea...
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 28 May 2020 12:54:37 +0000 (14:54 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 28 May 2020 12:54:37 +0000 (14:54 +0200)
49 files changed:
client/src/components/MoveList.vue
client/src/router.js
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Bishopawns/en.pug [new file with mode: 0644]
client/src/translations/rules/Bishopawns/es.pug [new file with mode: 0644]
client/src/translations/rules/Bishopawns/fr.pug [new file with mode: 0644]
client/src/translations/rules/Capture/en.pug
client/src/translations/rules/Capture/es.pug
client/src/translations/rules/Capture/fr.pug
client/src/translations/rules/Discoduel/en.pug [new file with mode: 0644]
client/src/translations/rules/Discoduel/es.pug [new file with mode: 0644]
client/src/translations/rules/Discoduel/fr.pug [new file with mode: 0644]
client/src/translations/rules/Doublemove1/en.pug
client/src/translations/rules/Knightpawns/en.pug [new file with mode: 0644]
client/src/translations/rules/Knightpawns/es.pug [new file with mode: 0644]
client/src/translations/rules/Knightpawns/fr.pug [new file with mode: 0644]
client/src/translations/rules/Pawnmassacre/en.pug [new file with mode: 0644]
client/src/translations/rules/Pawnmassacre/es.pug [new file with mode: 0644]
client/src/translations/rules/Pawnmassacre/fr.pug [new file with mode: 0644]
client/src/translations/rules/Pawnsking/en.pug [new file with mode: 0644]
client/src/translations/rules/Pawnsking/es.pug [new file with mode: 0644]
client/src/translations/rules/Pawnsking/fr.pug [new file with mode: 0644]
client/src/translations/rules/Progressive/en.pug [new file with mode: 0644]
client/src/translations/rules/Progressive/es.pug [new file with mode: 0644]
client/src/translations/rules/Progressive/fr.pug [new file with mode: 0644]
client/src/translations/rules/Rookpawns/en.pug [new file with mode: 0644]
client/src/translations/rules/Rookpawns/es.pug [new file with mode: 0644]
client/src/translations/rules/Rookpawns/fr.pug [new file with mode: 0644]
client/src/translations/variants/en.pug [new file with mode: 0644]
client/src/translations/variants/es.pug [new file with mode: 0644]
client/src/translations/variants/fr.pug [new file with mode: 0644]
client/src/utils/notation.js
client/src/variants/Bishopawns.js [new file with mode: 0644]
client/src/variants/Dark.js
client/src/variants/Discoduel.js [new file with mode: 0644]
client/src/variants/Doublemove1.js
client/src/variants/Doublemove2.js
client/src/variants/Eightpieces.js
client/src/variants/Knightpawns.js [new file with mode: 0644]
client/src/variants/Pawnmassacre.js [new file with mode: 0644]
client/src/variants/Pawns.js
client/src/variants/Pawnsking.js [new file with mode: 0644]
client/src/variants/Progressive.js [new file with mode: 0644]
client/src/variants/Rookpawns.js [new file with mode: 0644]
client/src/views/VariantList.vue [new file with mode: 0644]
client/src/views/Variants.vue
server/db/populate.sql

index 2e011b4..e36c058 100644 (file)
@@ -53,14 +53,12 @@ div
       .td(
         :class="{'highlight-lm': cursor == moveIdx}"
         @click="() => gotoMove(moveIdx)"
       .td(
         :class="{'highlight-lm': cursor == moveIdx}"
         @click="() => gotoMove(moveIdx)"
-      )
-        | {{ notation(moveIdx) }}
+        v-html="notation(moveIdx)")
       .td(
         v-if="moveIdx < moves.length-1"
         :class="{'highlight-lm': cursor == moveIdx+1}"
         @click="() => gotoMove(moveIdx+1)"
       .td(
         v-if="moveIdx < moves.length-1"
         :class="{'highlight-lm': cursor == moveIdx+1}"
         @click="() => gotoMove(moveIdx+1)"
-      )
-        | {{ notation(moveIdx + 1) }}
+        v-html="notation(moveIdx + 1)")
 </template>
 
 <script>
 </template>
 
 <script>
index 04c8f06..8590c33 100644 (file)
@@ -20,6 +20,11 @@ const router = new Router({
       name: "variants",
       component: loadView("Variants")
     },
       name: "variants",
       component: loadView("Variants")
     },
+    {
+      path: "/variants/list",
+      name: "variantlist",
+      component: loadView("VariantList")
+    },
     {
       path: "/variants/:vname([a-zA-Z0-9]+)",
       name: "rules",
     {
       path: "/variants/:vname([a-zA-Z0-9]+)",
       name: "rules",
index 13869e9..41c007a 100644 (file)
@@ -149,6 +149,7 @@ export const translations = {
   Variant: "Variant",
   Variants: "Variants",
   Versus: "Versus",
   Variant: "Variant",
   Variants: "Variants",
   Versus: "Versus",
+  "View alphabetical variants list": "View alphabetical variants list",
   White: "White",
   "White to move": "White to move",
   "White surrender": "White surrender",
   White: "White",
   "White to move": "White to move",
   "White surrender": "White surrender",
@@ -172,6 +173,7 @@ export const translations = {
   "Attract opposite king": "Attract opposite king",
   "Balanced sliders & leapers": "Balanced sliders & leapers",
   "Big board": "Big board",
   "Attract opposite king": "Attract opposite king",
   "Balanced sliders & leapers": "Balanced sliders & leapers",
   "Big board": "Big board",
+  "Bishop versus pawns": "Bishop versus pawns",
   "Board upside down": "Board upside down",
   "Both sides of the mirror": "Both sides of the mirror",
   "Burmese chess": "Burmese chess",
   "Board upside down": "Board upside down",
   "Both sides of the mirror": "Both sides of the mirror",
   "Burmese chess": "Burmese chess",
@@ -190,6 +192,7 @@ export const translations = {
   "Double moves (v1)": "Double moves (v1)",
   "Double moves (v2)": "Double moves (v2)",
   "Each piece is unique": "Each piece is unique",
   "Double moves (v1)": "Double moves (v1)",
   "Double moves (v2)": "Double moves (v2)",
   "Each piece is unique": "Each piece is unique",
+  "Enter the disco": "Enter the disco",
   "Exchange pieces' positions": "Exchange pieces' positions",
   "Exotic captures": "Exotic captures",
   "Explosive captures": "Explosive captures",
   "Exchange pieces' positions": "Exchange pieces' positions",
   "Exotic captures": "Exotic captures",
   "Explosive captures": "Explosive captures",
@@ -207,6 +210,7 @@ export const translations = {
   "Kings cross the 8x8 board": "Kings cross the 8x8 board",
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
   "Knight in pocket": "Knight in pocket",
   "Kings cross the 8x8 board": "Kings cross the 8x8 board",
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
   "Knight in pocket": "Knight in pocket",
+  "Knight versus pawns": "Knight versus pawns",
   "Landing on the board": "Landing on the board",
   "Laws of attraction": "Laws of attraction",
   "Long jumps over pieces": "Long jumps over pieces",
   "Landing on the board": "Landing on the board",
   "Laws of attraction": "Laws of attraction",
   "Long jumps over pieces": "Long jumps over pieces",
@@ -231,18 +235,22 @@ export const translations = {
   "Occupy the enemy palace": "Occupy the enemy palace",
   "Paralyzed pieces": "Paralyzed pieces",
   "Pawns move diagonally": "Pawns move diagonally",
   "Occupy the enemy palace": "Occupy the enemy palace",
   "Paralyzed pieces": "Paralyzed pieces",
   "Pawns move diagonally": "Pawns move diagonally",
+  "Pieces upside down": "Pieces upside down",
   "Play at the same time": "Play at the same time",
   "Play at the same time": "Play at the same time",
+  "Play more and more moves": "Play more and more moves",
   "Play opponent's pieces": "Play opponent's pieces",
   "Powerful pieces": "Powerful pieces",
   "Prolongated captures": "Prolongated captures",
   "Protect your pawns": "Protect your pawns",
   "Push and pull": "Push and pull",
   "Queen disguised as a pawn": "Queen disguised as a pawn",
   "Play opponent's pieces": "Play opponent's pieces",
   "Powerful pieces": "Powerful pieces",
   "Prolongated captures": "Prolongated captures",
   "Protect your pawns": "Protect your pawns",
   "Push and pull": "Push and pull",
   "Queen disguised as a pawn": "Queen disguised as a pawn",
-  "Reach the last rank": "Reach the last rank",
+  "Reach the last rank (v1)": "Reach the last rank (v1)",
+  "Reach the last rank (v2)": "Reach the last rank (v2)",
   "Reposition pieces": "Reposition pieces",
   "Reuse pieces": "Reuse pieces",
   "Reverse captures": "Reverse captures",
   "Roll the dice": "Roll the dice",
   "Reposition pieces": "Reposition pieces",
   "Reuse pieces": "Reuse pieces",
   "Reverse captures": "Reverse captures",
   "Roll the dice": "Roll the dice",
+  "Rook versus pawns": "Rook versus pawns",
   "Rotating board": "Rotating board",
   "Run forward": "Run forward",
   "Score a goal": "Score a goal",
   "Rotating board": "Rotating board",
   "Run forward": "Run forward",
   "Score a goal": "Score a goal",
index 2036ad5..bc25339 100644 (file)
@@ -149,6 +149,7 @@ export const translations = {
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contra",
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contra",
+  "View alphabetical variants list": "Ver la lista alfabética de variantes",
   White: "Blancas",
   "White to move": "Juegan las blancas",
   "White surrender": "Las blancas abandonan",
   White: "Blancas",
   "White to move": "Juegan las blancas",
   "White surrender": "Las blancas abandonan",
@@ -172,6 +173,7 @@ export const translations = {
   "Attract opposite king": "Atraer al rey contrario",
   "Balanced sliders & leapers": "Modos de desplazamiento equilibrados",
   "Big board": "Gran tablero",
   "Attract opposite king": "Atraer al rey contrario",
   "Balanced sliders & leapers": "Modos de desplazamiento equilibrados",
   "Big board": "Gran tablero",
+  "Bishop versus pawns": "Alfil contra peones",
   "Board upside down": "Tablero al revés",
   "Both sides of the mirror": "Ambos lados del espejo",
   "Burmese chess": "Ajedrez birmano",
   "Board upside down": "Tablero al revés",
   "Both sides of the mirror": "Ambos lados del espejo",
   "Burmese chess": "Ajedrez birmano",
@@ -190,6 +192,7 @@ export const translations = {
   "Double moves (v1)": "Jugadas doble (v1)",
   "Double moves (v2)": "Jugadas doble (v2)",
   "Each piece is unique": "Cada pieza es Ãºnica",
   "Double moves (v1)": "Jugadas doble (v1)",
   "Double moves (v2)": "Jugadas doble (v2)",
   "Each piece is unique": "Cada pieza es Ãºnica",
+  "Enter the disco": "Entrar en la discoteca",
   "Exchange pieces' positions": "Intercambiar posiciones de piezas",
   "Exotic captures": "Capturas exóticas",
   "Explosive captures": "Capturas explosivas",
   "Exchange pieces' positions": "Intercambiar posiciones de piezas",
   "Exotic captures": "Capturas exóticas",
   "Explosive captures": "Capturas explosivas",
@@ -207,6 +210,7 @@ export const translations = {
   "Kings cross the 8x8 board": "Los reyes cruzan el 8x8 tablero",
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
   "Knight in pocket": "Caballo en bolsillo",
   "Kings cross the 8x8 board": "Los reyes cruzan el 8x8 tablero",
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
   "Knight in pocket": "Caballo en bolsillo",
+  "Knight versus pawns": "Caballo contra peones",
   "Landing on the board": "Aterrizando en el tablero",
   "Laws of attraction": "Las leyes de las atracciones",
   "Long jumps over pieces": "Saltos largos sobre las piezas",
   "Landing on the board": "Aterrizando en el tablero",
   "Laws of attraction": "Las leyes de las atracciones",
   "Long jumps over pieces": "Saltos largos sobre las piezas",
@@ -231,18 +235,22 @@ export const translations = {
   "Occupy the enemy palace": "Ocupar el palacio enemigo",
   "Paralyzed pieces": "Piezas paralizadas",
   "Pawns move diagonally": "Peones se mueven en diagonal",
   "Occupy the enemy palace": "Ocupar el palacio enemigo",
   "Paralyzed pieces": "Piezas paralizadas",
   "Pawns move diagonally": "Peones se mueven en diagonal",
+  "Pieces upside down": "Piezas al revés",
   "Play at the same time": "Jugar al mismo tiempo",
   "Play at the same time": "Jugar al mismo tiempo",
+  "Play more and more moves": "Jugar más y más movimientos",
   "Play opponent's pieces": "Jugar piezas opuestas",
   "Powerful pieces": "Piezas poderosas",
   "Prolongated captures": "Capturas extendidas",
   "Protect your pawns": "Protege tus peones",
   "Push and pull": "Empujar y tirar",
   "Queen disguised as a pawn": "Reina disfrazada de peón",
   "Play opponent's pieces": "Jugar piezas opuestas",
   "Powerful pieces": "Piezas poderosas",
   "Prolongated captures": "Capturas extendidas",
   "Protect your pawns": "Protege tus peones",
   "Push and pull": "Empujar y tirar",
   "Queen disguised as a pawn": "Reina disfrazada de peón",
-  "Reach the last rank": "Llegar a la Ãºltima fila",
+  "Reach the last rank (v1)": "Llegar a la Ãºltima fila (v1)",
+  "Reach the last rank (v2)": "Llegar a la Ãºltima fila (v2)",
   "Reposition pieces": "Reposicionar las piezas",
   "Reuse pieces": "Reutilizar piezas",
   "Reverse captures": "Capturas invertidas",
   "Roll the dice": "Tirar los dados",
   "Reposition pieces": "Reposicionar las piezas",
   "Reuse pieces": "Reutilizar piezas",
   "Reverse captures": "Capturas invertidas",
   "Roll the dice": "Tirar los dados",
+  "Rook versus pawns": "Torre contra peones",
   "Rotating board": "Tablero giratorio",
   "Run forward": "Correr hacia adelante",
   "Score a goal": "Marcar una meta",
   "Rotating board": "Tablero giratorio",
   "Run forward": "Correr hacia adelante",
   "Score a goal": "Marcar una meta",
index 2e34016..869a68d 100644 (file)
@@ -149,6 +149,7 @@ export const translations = {
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contre",
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contre",
+  "View alphabetical variants list": "Voir la liste alphabétique des variantes",
   White: "Blancs",
   "White to move": "Trait aux blancs",
   "White surrender": "Les blancs abandonnent",
   White: "Blancs",
   "White to move": "Trait aux blancs",
   "White surrender": "Les blancs abandonnent",
@@ -172,6 +173,7 @@ export const translations = {
   "Attract opposite king": "Attirer le roi adverse",
   "Balanced sliders & leapers": "Modes de déplacement Ã©quilibrés",
   "Big board": "Grand Ã©chiquier",
   "Attract opposite king": "Attirer le roi adverse",
   "Balanced sliders & leapers": "Modes de déplacement Ã©quilibrés",
   "Big board": "Grand Ã©chiquier",
+  "Bishop versus pawns": "Fou contre pions",
   "Board upside down": "Échiquier Ã  l'envers",
   "Both sides of the mirror": "Les deux côté du miroir",
   "Burmese chess": "Échecs birmans",
   "Board upside down": "Échiquier Ã  l'envers",
   "Both sides of the mirror": "Les deux côté du miroir",
   "Burmese chess": "Échecs birmans",
@@ -190,6 +192,7 @@ export const translations = {
   "Double moves (v1)": "Coups doubles (v1)",
   "Double moves (v2)": "Coups doubles (v2)",
   "Each piece is unique": "Chaque pièce est unique",
   "Double moves (v1)": "Coups doubles (v1)",
   "Double moves (v2)": "Coups doubles (v2)",
   "Each piece is unique": "Chaque pièce est unique",
+  "Enter the disco": "Entrez dans la boîte",
   "Exchange pieces' positions": "Échangez les positions des pièces",
   "Exotic captures": "Captures exotiques",
   "Explosive captures": "Captures explosives",
   "Exchange pieces' positions": "Échangez les positions des pièces",
   "Exotic captures": "Captures exotiques",
   "Explosive captures": "Captures explosives",
@@ -207,6 +210,7 @@ export const translations = {
   "Kings cross the 8x8 board": "Les rois traversent l'échiquier 8x8",
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
   "Knight in pocket": "Cavalier en poche",
   "Kings cross the 8x8 board": "Les rois traversent l'échiquier 8x8",
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
   "Knight in pocket": "Cavalier en poche",
+  "Knight versus pawns": "Cavalier contre pions",
   "Landing on the board": "Débarquement sur l'échiquier",
   "Laws of attraction": "Les lois de l'attraction",
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
   "Landing on the board": "Débarquement sur l'échiquier",
   "Laws of attraction": "Les lois de l'attraction",
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
@@ -231,18 +235,22 @@ export const translations = {
   "Occupy the enemy palace": "Occuper le palais ennemi",
   "Paralyzed pieces": "Pièces paralysées",
   "Pawns move diagonally": "Les pions vont en diagonale",
   "Occupy the enemy palace": "Occuper le palais ennemi",
   "Paralyzed pieces": "Pièces paralysées",
   "Pawns move diagonally": "Les pions vont en diagonale",
+  "Pieces upside down": "Pièces Ã  l'envers",
   "Play at the same time": "Jouer en même temps",
   "Play at the same time": "Jouer en même temps",
+  "Play more and more moves": "Jouez de plus en plus de coups",
   "Play opponent's pieces": "Jouez les pièces adverses",
   "Powerful pieces": "Pièces puissantes",
   "Prolongated captures": "Captures prolongées",
   "Protect your pawns": "Protégez vos pions",
   "Push and pull": "Pousser et tirer",
   "Queen disguised as a pawn": "Reine déguisée en pion",
   "Play opponent's pieces": "Jouez les pièces adverses",
   "Powerful pieces": "Pièces puissantes",
   "Prolongated captures": "Captures prolongées",
   "Protect your pawns": "Protégez vos pions",
   "Push and pull": "Pousser et tirer",
   "Queen disguised as a pawn": "Reine déguisée en pion",
-  "Reach the last rank": "Atteignez la dernière rangée",
+  "Reach the last rank (v1)": "Atteignez la dernière rangée (v1)",
+  "Reach the last rank (v2)": "Atteignez la dernière rangée (v2)",
   "Reposition pieces": "Replacer les pièces",
   "Reuse pieces": "Réutiliser les pièces",
   "Reverse captures": "Captures inversées",
   "Roll the dice": "Lancez le dé",
   "Reposition pieces": "Replacer les pièces",
   "Reuse pieces": "Réutiliser les pièces",
   "Reverse captures": "Captures inversées",
   "Roll the dice": "Lancez le dé",
+  "Rook versus pawns": "Tour contre pions",
   "Rotating board": "Échiquier tournant",
   "Run forward": "Courir vers l'avant",
   "Score a goal": "Marquer un but",
   "Rotating board": "Échiquier tournant",
   "Run forward": "Courir vers l'avant",
   "Score a goal": "Marquer un but",
diff --git a/client/src/translations/rules/Bishopawns/en.pug b/client/src/translations/rules/Bishopawns/en.pug
new file mode 100644 (file)
index 0000000..e08e8f5
--- /dev/null
@@ -0,0 +1,19 @@
+p.boxed
+  | A bishop versus three pawns.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/5B2:
+  figcaption Initial position
+
+p.
+  The pawns win if one reaches the other end
+  without being immediately captured.
+  The bishop wins if it captures all the pawns.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Bishop vs 3 Pawns
+  | &nbsp;on chessplus.net.
diff --git a/client/src/translations/rules/Bishopawns/es.pug b/client/src/translations/rules/Bishopawns/es.pug
new file mode 100644 (file)
index 0000000..8c46e06
--- /dev/null
@@ -0,0 +1,19 @@
+p.boxed
+  | Un alfil contra tres peones.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/5B2:
+  figcaption Posición inicial
+
+p.
+  Los peones ganan si uno de ellos llega al otro lado sin ser
+  inmediatamente capturado.
+  El alfil gana si se come todos los peones.
+
+h3 Fuente
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Bishop vs 3 Pawns
+  | &nbsp;en chessplus.net.
diff --git a/client/src/translations/rules/Bishopawns/fr.pug b/client/src/translations/rules/Bishopawns/fr.pug
new file mode 100644 (file)
index 0000000..8d06d2f
--- /dev/null
@@ -0,0 +1,19 @@
+p.boxed
+  | Un fou contre trois pions.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/5B2:
+  figcaption Position initiale
+
+p.
+  Les pions gagnent si l'un d'eux arrive de l'autre côté sans Ãªtre
+  immédiatement capturé.
+  Le fou gagne s'il mange tous les pions.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Bishop vs 3 Pawns
+  | &nbsp;sur chessplus.net.
index 5ed5541..0007569 100644 (file)
@@ -17,6 +17,11 @@ figure.diagram-container
 
 h3 More information
 
 
 h3 More information
 
-p.
-  This idea was suggested recently (2020) by Vincent Rothuis.
-  It's simple so probably not new, but I never saw it before.
+p
+  | The variant idea was suggested recently (2020) by Vincent Rothuis.
+  | It is mentioned on 
+  a(href="https://www.chessvariants.com/other.dir/modest-various.html")
+    | this page
+  | .
+
+p Inventor: Roger Cooper (2000)
index 505a04e..7cd1882 100644 (file)
@@ -17,7 +17,11 @@ figure.diagram-container
 
 h3 Más información
 
 
 h3 Más información
 
-p.
-  Esta idea fue sugerida recientemente (2020) por Vincent Rothuis.
-  Es simple, así que probablemente no sea nuevo, pero nunca
-  previamente encontrado.
+p
+  | Esta idea fue sugerida recientemente (2020) por Vincent Rothuis.
+  | La variante es mencionada en 
+  a(href="https://www.chessvariants.com/other.dir/modest-various.html")
+    | esta página
+  | .
+
+p Inventor: Roger Cooper (2000)
index 21a0353..e956d8a 100644 (file)
@@ -17,7 +17,11 @@ figure.diagram-container
 
 h3 Plus d'information
 
 
 h3 Plus d'information
 
-p.
-  Cette idée a Ã©té suggérée récemment (2020) par Vincent Rothuis.
-  Elle est simple donc probablement pas nouvelle, mais je ne l'ai jamais
-  rencontrée auparavant.
+p
+  | Cette idée a Ã©té suggérée récemment (2020) par Vincent Rothuis.
+  | La variante est mentionnée sur 
+  a(href="https://www.chessvariants.com/other.dir/modest-various.html")
+    | cette page
+  | .
+
+p Inventeur : Roger Cooper (2000)
diff --git a/client/src/translations/rules/Discoduel/en.pug b/client/src/translations/rules/Discoduel/en.pug
new file mode 100644 (file)
index 0000000..130e176
--- /dev/null
@@ -0,0 +1,22 @@
+p.boxed
+  | Cross the board with as many pawns as possible.
+
+figure.diagram-container
+  .diagram
+    | fen:1n4n1/8/8/8/8/8/PPPPPPPP/8:
+  figcaption Initial position
+
+p.
+  The story is that 8 teenagers (pawns) want to go to a disco but they have to
+  pass 2 security guards (knights). The objective is to get as many pawns to
+  the other side as possible.
+  Once a pawn has reached the end it cannot be captured.
+
+p No winning condition: play two games and sum pawns on eighth rank.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Disco Duel
+  | &nbsp;on chessplus.net.
diff --git a/client/src/translations/rules/Discoduel/es.pug b/client/src/translations/rules/Discoduel/es.pug
new file mode 100644 (file)
index 0000000..0aafec5
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Cruza el tablero con tantos peones como sea posible.
+
+figure.diagram-container
+  .diagram
+    | fen:1n4n1/8/8/8/8/8/PPPPPPPP/8:
+  figcaption Posición inicial
+
+p.
+  Según la historia, 8 adolescentes (peones) quieren ir al fiesta, pero ellos
+  debe evitar a los dos guardias (caballos). El objetivo es traer un
+  número máximo de peones en la Ãºltima fila.
+
+p.
+  Sin condiciones de victoria:
+  juega dos partidas y agrega los peones en la octava fila.
+
+h3 Fuente
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Disco Duel
+  | &nbsp;en chessplus.net.
diff --git a/client/src/translations/rules/Discoduel/fr.pug b/client/src/translations/rules/Discoduel/fr.pug
new file mode 100644 (file)
index 0000000..dc45913
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Traversez l'échiquier avec autant de pions que possible.
+
+figure.diagram-container
+  .diagram
+    | fen:1n4n1/8/8/8/8/8/PPPPPPPP/8:
+  figcaption Position initiale
+
+p.
+  Selon l'histoire, 8 adolescents (les pions) veulent aller en boîte, mais ils
+  doivent Ã©viter les deux vigiles (cavaliers). L'objectif est d'amener un
+  maximum de pions sur la dernière rangée.
+
+p.
+  Pas de conditions de victoire :
+  jouez deux parties et sommez les pions sur la huitième rangée.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Disco Duel
+  | &nbsp;sur chessplus.net.
index 6d1982e..9b7595b 100644 (file)
@@ -50,7 +50,7 @@ h3 More information
 p
   | See for example the 
   a(href="https://www.chessvariants.com/multimove.dir/marseill.html")
 p
   | See for example the 
   a(href="https://www.chessvariants.com/multimove.dir/marseill.html")
-    |  Marseillais Chess
+    | Marseillais Chess
   | &nbsp;page on chessvariants.com.
 
 p
   | &nbsp;page on chessvariants.com.
 
 p
diff --git a/client/src/translations/rules/Knightpawns/en.pug b/client/src/translations/rules/Knightpawns/en.pug
new file mode 100644 (file)
index 0000000..5a78390
--- /dev/null
@@ -0,0 +1,20 @@
+p.boxed
+  | A knight versus three pawns.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/6N2:
+  figcaption Initial position
+
+p.
+  The pawns win if one reaches the other end
+  without being immediately captured.
+  The knight wins if it captures all the pawns.
+
+h3 Source
+
+p
+  | Inspired by 
+  a(href="https://chessplus.net/interactive-games/")
+    | Bishop vs 3 Pawns
+  | &nbsp;on chessplus.net.
diff --git a/client/src/translations/rules/Knightpawns/es.pug b/client/src/translations/rules/Knightpawns/es.pug
new file mode 100644 (file)
index 0000000..e295638
--- /dev/null
@@ -0,0 +1,20 @@
+p.boxed
+  | Un caballo contra tres peones.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/6N1:
+  figcaption Posición inicial
+
+p.
+  Los peones ganan si uno de ellos llega al otro lado sin ser
+  inmediatamente capturado.
+  El caballo gana si se come todos los peones.
+
+h3 Fuente
+
+p
+  | Inspirado por 
+  a(href="https://chessplus.net/interactive-games/")
+    | Bishop vs 3 Pawns
+  | &nbsp;en chessplus.net.
diff --git a/client/src/translations/rules/Knightpawns/fr.pug b/client/src/translations/rules/Knightpawns/fr.pug
new file mode 100644 (file)
index 0000000..2c53b22
--- /dev/null
@@ -0,0 +1,20 @@
+p.boxed
+  | Un cavalier contre trois pions.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/6N1:
+  figcaption Position initiale
+
+p.
+  Les pions gagnent si l'un d'eux arrive de l'autre côté sans Ãªtre
+  immédiatement capturé.
+  Le cavalier gagne s'il mange tous les pions.
+
+h3 Source
+
+p
+  | Inspiré par 
+  a(href="https://chessplus.net/interactive-games/")
+    | Bishop vs 3 Pawns
+  | &nbsp;sur chessplus.net.
diff --git a/client/src/translations/rules/Pawnmassacre/en.pug b/client/src/translations/rules/Pawnmassacre/en.pug
new file mode 100644 (file)
index 0000000..9055914
--- /dev/null
@@ -0,0 +1,21 @@
+p.boxed
+  | White pieces start on the eigth rank, black pieces start on the first rank.
+
+figure.diagram-container
+  .diagram
+    | fen:RNBQKBNR/pppppppp/8/8/8/8/PPPPPPPP/rnbqkbnr:
+  figcaption Initial deterministic position
+
+p.
+  All orthodox rules apply, except for castling.
+  Pawns are very vulnerable, their role seems mostly to limit pieces movements.
+
+h3 Source
+
+p
+  | The variant is mentioned on 
+  a(href="https://www.chessvariants.com/other.dir/modest-various.html")
+    | this page
+  | .
+
+p Inventor: Jeff "Cavebear" Stroud (1998)
diff --git a/client/src/translations/rules/Pawnmassacre/es.pug b/client/src/translations/rules/Pawnmassacre/es.pug
new file mode 100644 (file)
index 0000000..6695062
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Las piezas blancas comienzan en la octava fila,
+  | las piezas negras comienzan en la primera fila.
+
+figure.diagram-container
+  .diagram
+    | fen:RNBQKBNR/pppppppp/8/8/8/8/PPPPPPPP/rnbqkbnr:
+  figcaption Posición inicial determinista
+
+p.
+  Se aplican todas las reglas ortodoxos, excepto el enroque.
+  Los peones son muy vulnerables, su papel parece esencialmente
+  limite el movimiento de las piezas.
+
+h3 Fuente
+
+p
+  | La variante está mencionada en 
+  a(href="https://www.chessvariants.com/other.dir/modest-various.html")
+    | esta página
+  | .
+
+p Inventor: Jeff "Cavebear" Stroud (1998)
diff --git a/client/src/translations/rules/Pawnmassacre/fr.pug b/client/src/translations/rules/Pawnmassacre/fr.pug
new file mode 100644 (file)
index 0000000..adfb203
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Les pièces blanches démarrent sur la huitième rangée,
+  | les pièces noires démarrent sur la première rangée.
+
+figure.diagram-container
+  .diagram
+    | fen:RNBQKBNR/pppppppp/8/8/8/8/PPPPPPPP/rnbqkbnr:
+  figcaption Position initiale déterministe
+
+p.
+  Toutes les règles orthodoxes s'appliquent, sauf le roque.
+  Les pions sont très vulnérables, leur rôle semble essentiellement de
+  limiter les mouvements des pièces.
+
+h3 Source
+
+p
+  | La variante est mentionnée sur 
+  a(href="https://www.chessvariants.com/other.dir/modest-various.html")
+    | cette page
+  | .
+
+p Inventeur : Jeff "Cavebear" Stroud (1998)
diff --git a/client/src/translations/rules/Pawnsking/en.pug b/client/src/translations/rules/Pawnsking/en.pug
new file mode 100644 (file)
index 0000000..1c23951
--- /dev/null
@@ -0,0 +1,21 @@
+p.boxed
+  | Win by crossing the board.
+
+figure.diagram-container
+  .diagram
+    | fen:4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3:
+  figcaption Initial position
+
+p
+  | This is an extension of the 
+  a(href="/#/variants/Pawns") Pawns
+  | &nbsp;Game. A king is added to its starting square. He moves as usual.
+  | The first person to reach the other side with a pawn or king is the winner.
+  | You can also win if you capture the enemy king.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | The Pawns Game with a King
+  | &nbsp;on chessplus.net.
diff --git a/client/src/translations/rules/Pawnsking/es.pug b/client/src/translations/rules/Pawnsking/es.pug
new file mode 100644 (file)
index 0000000..0117895
--- /dev/null
@@ -0,0 +1,22 @@
+p.boxed
+  | Gana cruzando el tablero de ajedrez.
+
+figure.diagram-container
+  .diagrama
+    | fen:4k3/pppppppp/8/8/8/8/PPPPPPPPP/4K3:
+  figcaption Posición inicial
+
+p
+  | Es una extensión de la variante.
+  a(href="/#/variants/Pawns") Pawns
+  | . Se agrega un rey a su casilla inicial. Se mueve como siempre.
+  | La primera persona que llega al otro lado del tablero con un peón
+  | o el rey es declarado la ganadora.
+  | También puedes ganar capturando al rey contrario.
+
+h3 Fuente
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | The Pawns Game with a King
+  | &nbsp;en chessplus.net.
diff --git a/client/src/translations/rules/Pawnsking/fr.pug b/client/src/translations/rules/Pawnsking/fr.pug
new file mode 100644 (file)
index 0000000..4f1a9f8
--- /dev/null
@@ -0,0 +1,22 @@
+p.boxed
+  | Gagnez en traversant l'échiquier.
+
+figure.diagram-container
+  .diagram
+    | fen:4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3:
+  figcaption Position initiale
+
+p
+  | C'est une extension de la variante 
+  a(href="/#/variants/Pawns") Pawns
+  | . Un roi est ajouté sur sa case initiale. Il se déplace comme d'habitude.
+  | La première personne Ã  atteindre l'autre côté de l'échiquier avec un pion
+  | ou le roi est déclarée gagnante.
+  | Vous pouvez aussi gagner en capturant le roi adverse.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | The Pawns Game with a King
+  | &nbsp;sur chessplus.net.
diff --git a/client/src/translations/rules/Progressive/en.pug b/client/src/translations/rules/Progressive/en.pug
new file mode 100644 (file)
index 0000000..325b734
--- /dev/null
@@ -0,0 +1,35 @@
+p.boxed
+  | White plays one move, then black play twice,
+  | then white plays three moves...
+
+p.
+  At the very first move of the game, white make only one move - as usual.
+  However, after that and for all the game, each side must play as many moves
+  as the opponent just played plus one.
+
+p.
+  If a move gives check, then the sequence stops here, but the opponent can
+  still play as many moves as would be theoretically possible.
+  Also, if a checkmate or stalemate situation occurs before the maximal number
+  of moves are played, the game ends.
+  There are no en-passant captures.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqk2r/pppp2pp/4p3/8/1b1P4/4pN2/PPP1PPPP/RN1QKB1R:
+  figcaption "Mate in 1"
+
+p.
+  On the diagram white mates with the sequence of 5 moves c3 Ne5 Qd3 Qf5 Qf7.
+  Moves' notation is replaced by &#8734; (infinity) when there are four moves
+  or more to print.
+
+h3 More information
+
+p
+  | You can start from the 
+  a(href="https://en.wikipedia.org/wiki/Progressive_chess")
+    | Wikipedia page
+  | , which points to 
+  a(href="http://users.ics.aalto.fi/tho/chess.html") this page
+  | &nbsp;with several commented games.
diff --git a/client/src/translations/rules/Progressive/es.pug b/client/src/translations/rules/Progressive/es.pug
new file mode 100644 (file)
index 0000000..68d25f9
--- /dev/null
@@ -0,0 +1,35 @@
+p.boxed
+  | Las blancas juegan un movimiento, luego las negras juegan dos veces,
+  | entonces las blancas juegan tres movimientos...
+
+p.
+  En la primera jugada de la partida, las blancas solo juegan una vez -
+  como de costumbre. Sin embargo, después de eso y por el resto del juego,
+  cada lado debe jugar tantos movimientos como el oponente haya jugado más uno.
+
+p.
+  Si una jugaga da jaque, la secuencia termina aquí, pero el oponente
+  todavía puede jugar tantos movimientos como hubiera sido teóricamente
+  posible. Además, si un lado es mat o empate antes del número máximo de
+  movimientos se juega, el juego termina.
+  No hay capturas en-passant.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqk2r/pppp2pp/4p3/8/1b1P4/4pN2/PPP1PPPP/RN1QKB1R:
+  figcaption "Mate en 1"
+
+p.
+  En el diagrama, las blancas matan con la secuencia de 5 jugadas
+  c3 Ne5 Qd3 Qf5 Qf7. La notación de movimiento se reemplaza por &#8734;
+  (infinito) cuando hay cuatro o más jugadas para mostrar.
+
+h3 Más información
+
+p
+  | Puedes comenzar con la 
+  a(href="https://en.wikipedia.org/wiki/Progressive_chess")
+    | página Wikipedia
+  | , que apunta a 
+  a(href="http://users.ics.aalto.fi/tho/chess.html") esta página
+  | &nbsp;con varias partidas comentadas.
diff --git a/client/src/translations/rules/Progressive/fr.pug b/client/src/translations/rules/Progressive/fr.pug
new file mode 100644 (file)
index 0000000..ca102de
--- /dev/null
@@ -0,0 +1,35 @@
+p.boxed
+  | Les blancs jouent un coup, puis les noirs jouent deux fois,
+  | ensuite les blancs jouent trois coups...
+
+p.
+  Au tout premier coup de la partie, les blancs ne jouent qu'une fois -
+  comme d'habitude. Cependant, après après cela et pour le reste du jeu, chaque
+  camp doit jouer autant de coups que l'adversaire vient de jouer plus un.
+
+p.
+  Si un coup donne Ã©chec, alors la séquence s'arrête ici, mais l'adversaire
+  peut toujours jouer autant de coups qu'il aurait Ã©té théoriquement possible.
+  De plus, si un camp est mat ou pat avant que le nombre maximal de coups
+  soit joué, la partie s'arrête.
+  Il n'y a pas de prises en passant.
+
+figure.diagram-container
+  .diagram
+    | fen:rnbqk2r/pppp2pp/4p3/8/1b1P4/4pN2/PPP1PPPP/RN1QKB1R:
+  figcaption "Mat en 1"
+
+p.
+  Sur le diagramme les blancs matent avec la séquence de 5 coups
+  c3 Ne5 Qd3 Qf5 Qf7. La notation des coups est remplacée par &#8734; (infini)
+  quand il y a quatre coups ou plus Ã  afficher.
+
+h3 Plus d'informations
+
+p
+  | Vous pouvez commencer par la 
+  a(href="https://en.wikipedia.org/wiki/Progressive_chess")
+    | page Wikipedia
+  | , qui pointe vers 
+  a(href="http://users.ics.aalto.fi/tho/chess.html") cette page
+  | &nbsp;avec plusieurs parties commentées.
diff --git a/client/src/translations/rules/Rookpawns/en.pug b/client/src/translations/rules/Rookpawns/en.pug
new file mode 100644 (file)
index 0000000..e489d60
--- /dev/null
@@ -0,0 +1,19 @@
+p.boxed
+  | A rook versus five pawns.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppppp3/8/8/8/8/8/7R:
+  figcaption Initial position
+
+p.
+  The pawns win if one reaches the other end
+  without being immediately captured.
+  The rook wins if it captures all the pawns.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Rook vs 5 Pawns
+  | &nbsp;on chessplus.net.
diff --git a/client/src/translations/rules/Rookpawns/es.pug b/client/src/translations/rules/Rookpawns/es.pug
new file mode 100644 (file)
index 0000000..3f75338
--- /dev/null
@@ -0,0 +1,19 @@
+p.boxed
+  | Una torre contra cinco peones.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/5B2:
+  figcaption Posición inicial
+
+p.
+  Los peones ganan si uno de ellos llega al otro lado sin ser
+  inmediatamente capturado.
+  La torre gana si se come todos los peones.
+
+h3 Fuente
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Rook vs 5 Pawns
+  | &nbsp;en chessplus.net.
diff --git a/client/src/translations/rules/Rookpawns/fr.pug b/client/src/translations/rules/Rookpawns/fr.pug
new file mode 100644 (file)
index 0000000..a4f26b3
--- /dev/null
@@ -0,0 +1,19 @@
+p.boxed
+  | Une tour contre cinq pions.
+
+figure.diagram-container
+  .diagram
+    | fen:8/ppp5/8/8/8/8/8/7R:
+  figcaption Position initiale
+
+p.
+  Les pions gagnent si l'un d'eux arrive de l'autre côté sans Ãªtre
+  immédiatement capturé.
+  La tour gagne si elle mange tous les pions.
+
+h3 Source
+
+p
+  a(href="https://chessplus.net/interactive-games/")
+    | Rook vs 5 Pawns
+  | &nbsp;sur chessplus.net.
diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug
new file mode 100644 (file)
index 0000000..d41e2df
--- /dev/null
@@ -0,0 +1,395 @@
+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 For beginners, to learn the game
+
+p Variants with very few different pieces, and a simplified goal.
+-
+  var varlist = [
+    "Bishopawns",
+    "Discoduel",
+    "Knightpawns",
+    "Pawns",
+    "Pawnsking",
+    "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",
+    "Interweave",
+    "Losers",
+    "Suicide"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Transformations
+
+p Pieces generally transform when capturing.
+-
+  var varlist = [
+    "Absorption",
+    "Cannibal"
+  ]
+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"
+  ]
+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",
+    "Horde",
+    "Orda"
+  ]
+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 = [
+    "Eightpieces",
+    "Grand",
+    "Grasshopper",
+    "Omega",
+    "Perfect",
+    "Schess",
+    "Shako",
+    "Tencubed",
+    "Wildebeest"
+  ]
+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",
+    "Rococo",
+    "Maxima"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Pieces changing side
+
+p Pieces' owners (color) may change during the game
+-
+  var varlist = [
+    "Benedict",
+    "Checkered1",
+    "Checkered2",
+    "Pacifist1",
+    "Pacifist2"
+  ]
+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",
+    "Synchrone"
+  ]
+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",
+    "Knightmate",
+    "Knightrelay1",
+    "Knightrelay2"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Unusual initial setup
+
+p Pawns and / or pieces are switched, which result in a very different game.
+-
+  var varlist = [
+    "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",
+    "Pocketknight"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+p ...Or leading to a very different strategy:
+-
+  var varlist = [
+    "Antimatter",
+    "Atomic",
+    "Checkless"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+//h3 Initially empty board
+//
+//Parachute
+//"Crown chess" (place all units at move 1)
+
+h3 Repositioning
+
+p Pieces can be drop on the board, either immediately or later in the game.
+-
+  var varlist = [
+    "Clorange",
+    "Crazyhouse",
+    "Madhouse",
+    "Rampage",
+    "Recycle",
+    "Teleport"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Immobilization
+
+p Pieces can be paralyzed under certain circumstances.
+-
+  var varlist = [
+    "Koopa",
+    "Madrasi"
+  ]
+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 = [
+    "Makruk",
+    "Minishogi",
+    "Shatranj",
+    "Shogi",
+    "Sittuyin"
+  ]
+  //Chinese chess (TODO)
+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 = [
+    "Doublemove1",
+    "Doublemove2",
+    "Monster",
+    "Progressive"
+  ]
+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 = [
+    "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 = [
+    "Extinction",
+    "Threechecks",
+    "Kinglet",
+    "Koth"
+  ]
+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",
+    "Ambiguous",
+    "Bicolour",
+    "Doublearmy",
+    "Forward",
+    "Freecapture",
+    "Gridolina",
+    "Hamilton",
+    "Magnetic",
+    "Monochrome",
+    "Parachute",
+    "Takenmake",
+    "Wormhole"
+  ]
+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
new file mode 100644 (file)
index 0000000..5d25763
--- /dev/null
@@ -0,0 +1,406 @@
+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 Para principiantes, aprender el juego.
+
+p Variantes con muy pocas piezas diferentes y un propósito simplificado.
+-
+  var varlist = [
+    "Bishopawns",
+    "Discoduel",
+    "Knightpawns",
+    "Pawns",
+    "Pawnsking",
+    "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",
+    "Interweave",
+    "Losers",
+    "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",
+    "Cannibal"
+  ]
+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"
+  ]
+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",
+    "Horde",
+    "Orda"
+  ]
+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 = [
+    "Eightpieces",
+    "Grand",
+    "Grasshopper",
+    "Omega",
+    "Perfect",
+    "Schess",
+    "Shako",
+    "Tencubed",
+    "Wildebeest"
+  ]
+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",
+    "Rococo",
+    "Maxima"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Piezas que cambian de lado
+
+p Los propietarios (colores) de las piezas pueden cambiar durante el juego.
+-
+  var varlist = [
+    "Benedict",
+    "Checkered1",
+    "Checkered2",
+    "Pacifist1",
+    "Pacifist2"
+  ]
+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",
+    "Synchrone"
+  ]
+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",
+    "Knightmate",
+    "Knightrelay1",
+    "Knightrelay2"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Disposición inicial inusual
+
+p.
+  Los peones y / o piezas se intercambian,
+  resultando en un juego muy diferente.
+-
+  var varlist = [
+    "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",
+    "Pocketknight"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+p ...O conduce a estrategias muy diferentes:
+-
+  var varlist = [
+    "Antimatter",
+    "Atomic",
+    "Checkless"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+//h3 Initially empty board
+//
+//Parachute
+//"Crown chess" (place all units at move 1)
+
+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",
+    "Teleport"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Inmovilización
+
+p Las piezas pueden paralizarse en ciertas circunstancias.
+-
+  var varlist = [
+    "Koopa",
+    "Madrasi"
+  ]
+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 = [
+    "Makruk",
+    "Minishogi",
+    "Shatranj",
+    "Shogi",
+    "Sittuyin"
+  ]
+  //Chinese chess (TODO)
+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 = [
+    "Doublemove1",
+    "Doublemove2",
+    "Monster",
+    "Progressive"
+  ]
+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 = [
+    "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 = [
+    "Extinction",
+    "Threechecks",
+    "Kinglet",
+    "Koth"
+  ]
+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",
+    "Ambiguous",
+    "Bicolour",
+    "Doublearmy",
+    "Forward",
+    "Freecapture",
+    "Gridolina",
+    "Hamilton",
+    "Magnetic",
+    "Monochrome",
+    "Parachute",
+    "Takenmake",
+    "Wormhole"
+  ]
+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
new file mode 100644 (file)
index 0000000..3962fb6
--- /dev/null
@@ -0,0 +1,405 @@
+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 Pour les débutants, pour apprendre le jeu.
+
+p Variantes avec très peu de pièces différentes, et un but simplifié.
+-
+  var varlist = [
+    "Bishopawns",
+    "Discoduel",
+    "Knightpawns",
+    "Pawns",
+    "Pawnsking",
+    "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",
+    "Interweave",
+    "Losers",
+    "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",
+    "Cannibal"
+  ]
+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"
+  ]
+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",
+    "Horde",
+    "Orda"
+  ]
+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 = [
+    "Eightpieces",
+    "Grand",
+    "Grasshopper",
+    "Omega",
+    "Perfect",
+    "Schess",
+    "Shako",
+    "Tencubed",
+    "Wildebeest"
+  ]
+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",
+    "Rococo",
+    "Maxima"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Pièces changeant de camp
+
+p Les propriétaires (couleurs) des pièces peuvent changer pendant la partie.
+-
+  var varlist = [
+    "Benedict",
+    "Checkered1",
+    "Checkered2",
+    "Pacifist1",
+    "Pacifist2"
+  ]
+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",
+    "Synchrone"
+  ]
+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",
+    "Knightmate",
+    "Knightrelay1",
+    "Knightrelay2"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Arrangement initial inhabituel
+
+p.
+  Les pions et / ou les pièces sont Ã©changés,
+  résultant en un jeu très différent.
+-
+  var varlist = [
+    "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",
+    "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",
+    "Atomic",
+    "Checkless"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+//h3 Initially empty board
+//
+//Parachute
+//"Crown chess" (place all units at move 1)
+
+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",
+    "Teleport"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
+h3 Immobilisation
+
+p Les pièces peuvent Ãªtre paralysées dans certaines circonstances.
+-
+  var varlist = [
+    "Koopa",
+    "Madrasi"
+  ]
+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 = [
+    "Makruk",
+    "Minishogi",
+    "Shatranj",
+    "Shogi",
+    "Sittuyin"
+  ]
+  //Chinese chess (TODO)
+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 = [
+    "Doublemove1",
+    "Doublemove2",
+    "Monster",
+    "Progressive"
+  ]
+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 = [
+    "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 = [
+    "Extinction",
+    "Threechecks",
+    "Kinglet",
+    "Koth"
+  ]
+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",
+    "Ambiguous",
+    "Bicolour",
+    "Doublearmy",
+    "Forward",
+    "Freecapture",
+    "Gridolina",
+    "Hamilton",
+    "Magnetic",
+    "Monochrome",
+    "Parachute",
+    "Takenmake",
+    "Wormhole"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
index 4402e26..3d566c8 100644 (file)
@@ -2,11 +2,15 @@
 export function getFullNotation(move, type) {
   if (!type) type = "notation";
   if (Array.isArray(move)) {
 export function getFullNotation(move, type) {
   if (!type) type = "notation";
   if (Array.isArray(move)) {
-    let notation = "";
-    for (let i=0; i<move.length; i++)
-      notation += move[i][type] + ",";
-    // Remove last comma:
-    return notation.slice(0,-1);
+    if (move.length <= 3) {
+      let notation = "";
+      for (let i=0; i<move.length; i++)
+        notation += move[i][type] + ",";
+      // Remove last comma:
+      return notation.slice(0,-1);
+    }
+    // Four sub-moves or more:
+    return "&#8734;";
   }
   // Simple (usual) case
   return move[type];
   }
   // Simple (usual) case
   return move[type];
diff --git a/client/src/variants/Bishopawns.js b/client/src/variants/Bishopawns.js
new file mode 100644 (file)
index 0000000..1ee9b97
--- /dev/null
@@ -0,0 +1,51 @@
+import { ChessRules } from "@/base_rules";
+
+export class BishopawnsRules extends ChessRules {
+  static get PawnSpecs() {
+    return Object.assign(
+      {},
+      ChessRules.PawnSpecs,
+      // The promotion piece doesn't matter, the game is won
+      { promotions: [V.QUEEN] }
+    );
+  }
+
+  static get HasFlags() {
+    return false;
+  }
+
+  scanKings() {}
+
+  static GenRandInitFen() {
+    return "8/ppp5/8/8/8/8/8/5B2 w 0 -";
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    // If all pieces of some color vanished, the opponent wins:
+    for (let c of ['w', 'b']) {
+      if (this.board.every(b => b.every(cell => !cell || cell[0] != c)))
+        return (c == 'w' ? "0-1" : "1-0");
+    }
+    // Did a black pawn promote? Can the bishop take it?
+    const qIdx = this.board[7].findIndex(cell => cell[1] == V.QUEEN);
+    if (qIdx >= 0 && !super.isAttackedByBishop([7, qIdx], 'w'))
+      return "0-1";
+    if (!this.atLeastOneMove()) return "1/2";
+    return "*";
+  }
+
+  postPlay() {}
+  postUndo() {}
+
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
+};
index cb073cf..65934e6 100644 (file)
@@ -109,7 +109,7 @@ export class DarkRules extends ChessRules {
 
   postPlay(move) {
     super.postPlay(move);
 
   postPlay(move) {
     super.postPlay(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];
 
       // We took opponent king (because if castle vanish[1] is a rook)
       this.kingPos[this.turn] = [-1, -1];
 
@@ -119,11 +119,9 @@ export class DarkRules extends ChessRules {
 
   postUndo(move) {
     super.postUndo(move);
 
   postUndo(move) {
     super.postUndo(move);
-    const c = move.vanish[0].c;
-    const oppCol = V.GetOppCol(c);
-    if (this.kingPos[oppCol][0] < 0)
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
       // Last move took opponent's king:
       // Last move took opponent's king:
-      this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y];
+      this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
 
     // Update lights for both colors:
     this.updateEnlightened();
 
     // Update lights for both colors:
     this.updateEnlightened();
diff --git a/client/src/variants/Discoduel.js b/client/src/variants/Discoduel.js
new file mode 100644 (file)
index 0000000..ea522df
--- /dev/null
@@ -0,0 +1,50 @@
+import { ChessRules } from "@/base_rules";
+
+export class DiscoduelRules extends ChessRules {
+  static get PawnSpecs() {
+    return Object.assign(
+      {},
+      ChessRules.PawnSpecs,
+      { promotions: [V.PAWN] }
+    );
+  }
+
+  static get HasFlags() {
+    return false;
+  }
+
+  scanKings() {}
+
+  static GenRandInitFen() {
+    return "1n4n1/8/8/8/8/8/PPPPPPPP/8 w 0 -";
+  }
+
+  getPotentialMovesFrom(sq) {
+    const moves = super.getPotentialMovesFrom(sq);
+    if (this.turn == 'b')
+      // Prevent pawn captures on last rank:
+      return moves.filter(m => m.vanish.length == 1 || m.vanish[1].x != 0);
+    return moves;
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    // No real winning condition (promotions count...)
+    if (!this.atLeastOneMove()) return "1/2";
+    return "*";
+  }
+
+  postPlay() {}
+  postUndo() {}
+
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
+};
index f51dd7a..6999eb4 100644 (file)
@@ -1,5 +1,4 @@
 import { ChessRules } from "@/base_rules";
 import { ChessRules } from "@/base_rules";
-import { randInt } from "@/utils/alea";
 
 export class Doublemove1Rules extends ChessRules {
   static IsGoodEnpassant(enpassant) {
 
 export class Doublemove1Rules extends ChessRules {
   static IsGoodEnpassant(enpassant) {
@@ -88,8 +87,15 @@ export class Doublemove1Rules extends ChessRules {
       this.epSquares.push([epSq]);
       this.movesCount = 1;
     }
       this.epSquares.push([epSq]);
       this.movesCount = 1;
     }
-    // Does this move give check on subturn 1? If yes, skip subturn 2
-    else if (this.subTurn == 1 && this.underCheck(V.GetOppCol(this.turn))) {
+    // Does this move give check on subturn 1 or reach stalemate?
+    // If yes, skip subturn 2
+    else if (
+      this.subTurn == 1 &&
+      (
+        this.underCheck(V.GetOppCol(this.turn)) ||
+        !this.atLeastOneMove()
+      )
+    ) {
       this.turn = V.GetOppCol(this.turn);
       this.epSquares.push([epSq]);
       move.checkOnSubturn1 = true;
       this.turn = V.GetOppCol(this.turn);
       this.epSquares.push([epSq]);
       move.checkOnSubturn1 = true;
@@ -115,7 +121,7 @@ export class Doublemove1Rules extends ChessRules {
     const piece = move.vanish[0].p;
     const firstRank = c == "w" ? V.size.x - 1 : 0;
 
     const piece = move.vanish[0].p;
     const firstRank = c == "w" ? V.size.x - 1 : 0;
 
-    if (piece == V.KING && move.appear.length > 0) {
+    if (piece == V.KING) {
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
       this.castleFlags[c] = [V.size.y, V.size.y];
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
       this.castleFlags[c] = [V.size.y, V.size.y];
@@ -130,7 +136,7 @@ export class Doublemove1Rules extends ChessRules {
       const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
       this.castleFlags[c][flagIdx] = V.size.y;
     }
       const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
       this.castleFlags[c][flagIdx] = V.size.y;
     }
-    else if (
+    if (
       move.end.x == oppFirstRank && //we took opponent rook?
       this.castleFlags[oppCol].includes(move.end.y)
     ) {
       move.end.x == oppFirstRank && //we took opponent rook?
       this.castleFlags[oppCol].includes(move.end.y)
     ) {
@@ -206,43 +212,39 @@ export class Doublemove1Rules extends ChessRules {
     };
 
     const moves11 = this.getAllValidMoves();
     };
 
     const moves11 = this.getAllValidMoves();
-    let doubleMoves = [];
+    let doubleMove = null;
+    let bestEval = Number.POSITIVE_INFINITY * (color == 'w' ? -1 : 1);
     // Rank moves using a min-max at depth 2
     for (let i = 0; i < moves11.length; i++) {
       this.play(moves11[i]);
       if (this.turn != color) {
         // We gave check with last move: search the best opponent move
     // Rank moves using a min-max at depth 2
     for (let i = 0; i < moves11.length; i++) {
       this.play(moves11[i]);
       if (this.turn != color) {
         // We gave check with last move: search the best opponent move
-        doubleMoves.push({ moves: [moves11[i]], eval: getBestMoveEval() });
+        const evalM = getBestMoveEval() + 0.05 - Math.random() / 10;
+        if (
+          (color == 'w' && evalM > bestEval) ||
+          (color == 'b' && evalM < bestEval)
+        ) {
+          doubleMove = moves11[i];
+          bestEval = evalM;
+        }
       }
       else {
         let moves12 = this.getAllValidMoves();
         for (let j = 0; j < moves12.length; j++) {
           this.play(moves12[j]);
       }
       else {
         let moves12 = this.getAllValidMoves();
         for (let j = 0; j < moves12.length; j++) {
           this.play(moves12[j]);
-          doubleMoves.push({
-            moves: [moves11[i], moves12[j]],
-            eval: getBestMoveEval() + 0.05 - Math.random() / 10
-          });
+          const evalM  = getBestMoveEval() + 0.05 - Math.random() / 10
+          if (
+            (color == 'w' && evalM > bestEval) ||
+            (color == 'b' && evalM < bestEval)
+          ) {
+            doubleMove = [moves11[i], moves12[j]];
+            bestEval = evalM;
+          }
           this.undo(moves12[j]);
         }
       }
       this.undo(moves11[i]);
     }
           this.undo(moves12[j]);
         }
       }
       this.undo(moves11[i]);
     }
-
-    // TODO: array + sort + candidates logic not required when adding small
-    // fluctuations to the eval function (could also be generalized).
-    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++
-    ) {
-      candidates.push(i);
-    }
-    const selected = doubleMoves[randInt(candidates.length)].moves;
-    if (selected.length == 1) return selected[0];
-    return selected;
+    return doubleMove;
   }
 };
   }
 };
index 4801d36..f9d5e4f 100644 (file)
@@ -127,7 +127,7 @@ export class Doublemove2Rules extends ChessRules {
     const piece = move.vanish[0].p;
     const firstRank = c == "w" ? V.size.x - 1 : 0;
 
     const piece = move.vanish[0].p;
     const firstRank = c == "w" ? V.size.x - 1 : 0;
 
-    if (piece == V.KING && move.appear.length > 0) {
+    if (piece == V.KING) {
       this.kingPos[c] = [move.appear[0].x, move.appear[0].y];
       this.castleFlags[c] = [V.size.y, V.size.y];
       return;
       this.kingPos[c] = [move.appear[0].x, move.appear[0].y];
       this.castleFlags[c] = [V.size.y, V.size.y];
       return;
@@ -146,7 +146,7 @@ export class Doublemove2Rules extends ChessRules {
       const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
       this.castleFlags[c][flagIdx] = V.size.y;
     }
       const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
       this.castleFlags[c][flagIdx] = V.size.y;
     }
-    else if (
+    if (
       move.end.x == oppFirstRank && //we took opponent rook?
       this.castleFlags[oppCol].includes(move.end.y)
     ) {
       move.end.x == oppFirstRank && //we took opponent rook?
       this.castleFlags[oppCol].includes(move.end.y)
     ) {
@@ -210,34 +210,28 @@ export class Doublemove2Rules extends ChessRules {
     if (this.movesCount == 0)
       // First white move at random:
       return moves11[randInt(moves11.length)];
     if (this.movesCount == 0)
       // First white move at random:
       return moves11[randInt(moves11.length)];
-    let doubleMoves = [];
+    let doubleMove = null;
+    let bestEval = Number.POSITIVE_INFINITY * (color == 'w' ? -1 : 1);
     // Rank moves using a min-max at depth 2
     for (let i = 0; i < moves11.length; i++) {
       this.play(moves11[i]);
       const moves12 = this.getAllValidMoves();
       for (let j = 0; j < moves12.length; j++) {
         this.play(moves12[j]);
     // Rank moves using a min-max at depth 2
     for (let i = 0; i < moves11.length; i++) {
       this.play(moves11[i]);
       const moves12 = this.getAllValidMoves();
       for (let j = 0; j < moves12.length; j++) {
         this.play(moves12[j]);
-        doubleMoves.push({
-          moves: [moves11[i], moves12[j]],
-          // Small fluctuations to uniformize play a little
-          eval: getBestMoveEval() + 0.05 - Math.random() / 10
-        });
+        // Small fluctuations to uniformize play a little
+        const evalM = getBestMoveEval() + 0.05 - Math.random() / 10
+        if (
+          (color == 'w' && evalM > bestEval) ||
+          (color == 'b' && evalM < bestEval)
+        ) {
+          doubleMove = [moves11[i],  moves12[j]];
+          bestEval = evalM;
+        }
         this.undo(moves12[j]);
       }
       this.undo(moves11[i]);
     }
         this.undo(moves12[j]);
       }
       this.undo(moves11[i]);
     }
-
-    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++
-    ) {
-      candidates.push(i);
-    }
-    return doubleMoves[randInt(candidates.length)].moves;
+    // TODO: not always the best move played (why ???)
+    return doubleMove;
   }
 };
   }
 };
index 6efb182..a232890 100644 (file)
@@ -557,6 +557,7 @@ export class EightpiecesRules extends ChessRules {
     const L = this.sentryPush.length;
     const color = this.getColor(x, y);
     const dirCode = this.board[x][y][1];
     const L = this.sentryPush.length;
     const color = this.getColor(x, y);
     const dirCode = this.board[x][y][1];
+    const curDir = V.LANCER_DIRS[dirCode];
     if (!!this.sentryPush[L-1]) {
       // Maybe I was pushed
       const pl = this.sentryPush[L-1].length;
     if (!!this.sentryPush[L-1]) {
       // Maybe I was pushed
       const pl = this.sentryPush[L-1].length;
@@ -567,7 +568,6 @@ export class EightpiecesRules extends ChessRules {
         // I was pushed: allow all directions (for this move only), but
         // do not change direction after moving, *except* if I keep the
         // same orientation in which I was pushed.
         // I was pushed: allow all directions (for this move only), but
         // do not change direction after moving, *except* if I keep the
         // same orientation in which I was pushed.
-        const curDir = V.LANCER_DIRS[dirCode];
         // Also allow simple reorientation ("capturing king"):
         if (!V.OnBoard(x + curDir[0], y + curDir[1])) {
           const kp = this.kingPos[color];
         // Also allow simple reorientation ("capturing king"):
         if (!V.OnBoard(x + curDir[0], y + curDir[1])) {
           const kp = this.kingPos[color];
@@ -647,10 +647,11 @@ export class EightpiecesRules extends ChessRules {
       });
       return moves;
     } else {
       });
       return moves;
     } else {
-      // I'm pushed: add potential nudges
+      // I'm pushed: add potential nudges, except for current orientation
       let potentialNudges = [];
       for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
         if (
       let potentialNudges = [];
       for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
         if (
+          (step[0] != curDir[0] || step[1] != curDir[1]) &&
           V.OnBoard(x + step[0], y + step[1]) &&
           this.board[x + step[0]][y + step[1]] == V.EMPTY
         ) {
           V.OnBoard(x + step[0], y + step[1]) &&
           this.board[x + step[0]][y + step[1]] == V.EMPTY
         ) {
@@ -842,9 +843,21 @@ export class EightpiecesRules extends ChessRules {
         coord.x += step[0];
         coord.y += step[1];
       }
         coord.x += step[0];
         coord.y += step[1];
       }
+      const L = this.sentryPush.length;
+      const pl = (!!this.sentryPush[L-1] ? this.sentryPush[L-1].length : 0);
       for (let xy of lancerPos) {
         const dir = V.LANCER_DIRS[this.board[xy.x][xy.y].charAt(1)];
       for (let xy of lancerPos) {
         const dir = V.LANCER_DIRS[this.board[xy.x][xy.y].charAt(1)];
-        if (dir[0] == -step[0] && dir[1] == -step[1]) return true;
+        if (
+          (dir[0] == -step[0] && dir[1] == -step[1]) ||
+          // If the lancer was just pushed, this is an attack too:
+          (
+            !!this.sentryPush[L-1] &&
+            this.sentryPush[L-1][pl-1].x == xy.x &&
+            this.sentryPush[L-1][pl-1].y == xy.y
+          )
+        ) {
+          return true;
+        }
       }
     }
     return false;
       }
     }
     return false;
diff --git a/client/src/variants/Knightpawns.js b/client/src/variants/Knightpawns.js
new file mode 100644 (file)
index 0000000..6b7415c
--- /dev/null
@@ -0,0 +1,47 @@
+import { ChessRules } from "@/base_rules";
+
+export class KnightpawnsRules extends ChessRules {
+  static get PawnSpecs() {
+    return Object.assign(
+      {},
+      ChessRules.PawnSpecs,
+      // The promotion piece doesn't matter, the game is won
+      { promotions: [V.QUEEN] }
+    );
+  }
+
+  static get HasFlags() {
+    return false;
+  }
+
+  scanKings() {}
+
+  static GenRandInitFen() {
+    return "8/ppp5/8/8/8/8/8/6N1 w 0 -";
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    // If all pieces of some color vanished, the opponent wins:
+    for (let c of ['w', 'b']) {
+      if (this.board.every(b => b.every(cell => !cell || cell[0] != c)))
+        return (c == 'w' ? "0-1" : "1-0");
+    }
+    // Did a black pawn promote? Can the rook take it?
+    const qIdx = this.board[7].findIndex(cell => cell[1] == V.QUEEN);
+    if (qIdx >= 0 && !super.isAttackedByKnight([7, qIdx], 'w'))
+      return "0-1";
+    if (!this.atLeastOneMove()) return "1/2";
+    return "*";
+  }
+
+  postPlay() {}
+  postUndo() {}
+};
diff --git a/client/src/variants/Pawnmassacre.js b/client/src/variants/Pawnmassacre.js
new file mode 100644 (file)
index 0000000..563fd00
--- /dev/null
@@ -0,0 +1,18 @@
+import { ChessRules } from "@/base_rules";
+
+export class PawnmassacreRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
+
+  static GenRandInitFen(randomness) {
+    return (
+      ChessRules.GenRandIntFen(randomness)
+      // Remove castle flags
+      .slice(0, -6).concat("-")
+      .replace("PPPPPPPP", "pppppppp")
+      // Next replacement is OK because only acts on first occurrence
+      .replace("pppppppp", "PPPPPPPP")
+    );
+  }
+};
index 83ccca7..1dca994 100644 (file)
@@ -14,6 +14,8 @@ export class PawnsRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  scanKings() {}
+
   static GenRandInitFen() {
     return "8/pppppppp/8/8/8/8/PPPPPPPP/8 w 0 -";
   }
   static GenRandInitFen() {
     return "8/pppppppp/8/8/8/8/PPPPPPPP/8 w 0 -";
   }
@@ -36,4 +38,11 @@ export class PawnsRules extends ChessRules {
     if (!this.atLeastOneMove()) return "1/2";
     return "*";
   }
     if (!this.atLeastOneMove()) return "1/2";
     return "*";
   }
+
+  postPlay() {}
+  postUndo() {}
+
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
 };
 };
diff --git a/client/src/variants/Pawnsking.js b/client/src/variants/Pawnsking.js
new file mode 100644 (file)
index 0000000..76d03ef
--- /dev/null
@@ -0,0 +1,56 @@
+import { ChessRules } from "@/base_rules";
+
+export class PawnskingRules extends ChessRules {
+  static get PawnSpecs() {
+    return Object.assign(
+      {},
+      ChessRules.PawnSpecs,
+      // The promotion piece doesn't matter, the game is won
+      { promotions: [V.QUEEN] }
+    );
+  }
+
+  static get HasFlags() {
+    return false;
+  }
+
+  static GenRandInitFen() {
+    return "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w 0 -";
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    if (this.kingPos[color][0] < 0) return (color == "w" ? "0-1" : "1-0");
+    const oppCol = V.GetOppCol(color);
+    const lastRank = (oppCol == 'w' ? 0 : 7);
+    if (this.board[lastRank].some(cell => cell[0] == oppCol))
+      // The opposing edge is reached!
+      return (oppCol == "w" ? "1-0" : "0-1");
+    if (this.atLeastOneMove()) return "*";
+    return "1/2";
+  }
+
+  postPlay(move) {
+    super.postPlay(move);
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      this.kingPos[this.turn] = [-1, -1];
+  }
+
+  postUndo(move) {
+    super.postUndo(move);
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
+  }
+
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
+};
diff --git a/client/src/variants/Progressive.js b/client/src/variants/Progressive.js
new file mode 100644 (file)
index 0000000..6765428
--- /dev/null
@@ -0,0 +1,111 @@
+import { ChessRules } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+
+export class ProgressiveRules extends ChessRules {
+  static get HasEnpassant() {
+    return false;
+  }
+
+  setOtherVariables(fen) {
+    super.setOtherVariables(fen);
+    this.subTurn = 1;
+  }
+
+  filterValid(moves) {
+    if (moves.length == 0) return [];
+    const color = this.turn;
+    return moves.filter(m => {
+      // Not using this.play() (would result ininfinite recursive calls)
+      V.PlayOnBoard(this.board, m);
+      if (m.appear[0].p == V.KING)
+        this.kingPos[color] = [m.appear[0].x, m.appear[0].y];
+      const res = !this.underCheck(color);
+      V.UndoOnBoard(this.board, m);
+      if (m.appear[0].p == V.KING)
+        this.kingPos[color] = [m.vanish[0].x, m.vanish[0].y];
+      return res;
+    });
+  }
+
+  play(move) {
+    move.flags = JSON.stringify(this.aggregateFlags());
+    const color = this.turn;
+    const oppCol = V.GetOppCol(color);
+    move.turn = [color, this.subTurn];
+    V.PlayOnBoard(this.board, move);
+    if (
+      this.subTurn > this.movesCount ||
+      this.underCheck(oppCol) ||
+      !this.atLeastOneMove()
+    ) {
+      this.turn = oppCol;
+      this.subTurn = 1;
+      this.movesCount++;
+    }
+    else this.subTurn++;
+    this.postPlay(move);
+  }
+
+  postPlay(move) {
+    const c = move.turn[0];
+    const piece = move.vanish[0].p;
+    const firstRank = c == "w" ? V.size.x - 1 : 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;
+      this.castleFlags[c] = [V.size.y, V.size.y];
+      return;
+    }
+    const oppCol = V.GetOppCol(c);
+    const oppFirstRank = V.size.x - 1 - firstRank;
+    if (
+      move.start.x == firstRank && //our rook moves?
+      this.castleFlags[c].includes(move.start.y)
+    ) {
+      const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
+      this.castleFlags[c][flagIdx] = V.size.y;
+    }
+    if (
+      move.end.x == oppFirstRank && //we took opponent rook?
+      this.castleFlags[oppCol].includes(move.end.y)
+    ) {
+      const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1);
+      this.castleFlags[oppCol][flagIdx] = V.size.y;
+    }
+  }
+
+  undo(move) {
+    this.disaggregateFlags(JSON.parse(move.flags));
+    V.UndoOnBoard(this.board, move);
+    if (this.turn != move.turn[0]) this.movesCount--;
+    this.turn = move.turn[0];
+    this.subTurn = move.turn[1];
+    super.postUndo(move);
+  }
+
+  static get VALUES() {
+    return {
+      p: 1,
+      r: 5,
+      n: 3,
+      b: 3,
+      q: 7, //slightly less than in orthodox game
+      k: 1000
+    };
+  }
+
+  // Random moves (too high branching factor otherwise). TODO
+  getComputerMove() {
+    let res = [];
+    const color = this.turn;
+    while (this.turn == color) {
+      const moves = this.getAllValidMoves();
+      const m = moves[randInt(moves.length)];
+      res.push(m);
+      this.play(m);
+    }
+    for (let i=res.length - 1; i>= 0; i--) this.undo(res[i]);
+    return res;
+  }
+};
diff --git a/client/src/variants/Rookpawns.js b/client/src/variants/Rookpawns.js
new file mode 100644 (file)
index 0000000..abbd785
--- /dev/null
@@ -0,0 +1,51 @@
+import { ChessRules } from "@/base_rules";
+
+export class RookpawnsRules extends ChessRules {
+  static get PawnSpecs() {
+    return Object.assign(
+      {},
+      ChessRules.PawnSpecs,
+      // The promotion piece doesn't matter, the game is won
+      { promotions: [V.QUEEN] }
+    );
+  }
+
+  static get HasFlags() {
+    return false;
+  }
+
+  scanKings() {}
+
+  static GenRandInitFen() {
+    return "8/ppppp3/8/8/8/8/8/7R w 0 -";
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    // If all pieces of some color vanished, the opponent wins:
+    for (let c of ['w', 'b']) {
+      if (this.board.every(b => b.every(cell => !cell || cell[0] != c)))
+        return (c == 'w' ? "0-1" : "1-0");
+    }
+    // Did a black pawn promote? Can the rook take it?
+    const qIdx = this.board[7].findIndex(cell => cell[1] == V.QUEEN);
+    if (qIdx >= 0 && !super.isAttackedByRook([7, qIdx], 'w'))
+      return "0-1";
+    if (!this.atLeastOneMove()) return "1/2";
+    return "*";
+  }
+
+  postPlay() {}
+  postUndo() {}
+
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
+};
diff --git a/client/src/views/VariantList.vue b/client/src/views/VariantList.vue
new file mode 100644 (file)
index 0000000..e5d0285
--- /dev/null
@@ -0,0 +1,96 @@
+<template lang="pug">
+main
+  .row
+    .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+      input#prefixFilter(
+        v-model="curPrefix"
+        @input="setCurPrefix($event)"
+        :placeholder="st.tr['Prefix?']"
+      )
+    .variant.col-sm-12.col-md-5.col-lg-4(
+      v-for="(v,idx) in filteredVariants"
+      :class="getVclasses(filteredVariants, idx)"
+    )
+      router-link(:to="getLink(v.name)")
+        h4.boxtitle.text-center {{ v.name }}
+        p.description.text-center {{ st.tr[v.desc] }}
+</template>
+
+<script>
+import { store } from "@/store";
+export default {
+  name: "my-variants",
+  data: function() {
+    return {
+      curPrefix: "",
+      st: store.state
+    };
+  },
+  mounted: function() {
+    document.getElementById("prefixFilter").focus();
+  },
+  computed: {
+    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);
+        });
+      return variants;
+    }
+  },
+  methods: {
+    // oninput listener, required for smartphones:
+    setCurPrefix: function(e) {
+      this.curPrefix = e.target.value;
+    },
+    getLink: function(vname) {
+      return "/variants/" + vname;
+    },
+    getVclasses: function(varray, idx) {
+      const idxMod2 = idx % 2;
+      return {
+        'col-md-offset-1': idxMod2 == 0,
+        'col-lg-offset-2': idxMod2 == 0,
+        'last-noneighb': idxMod2 == 0 && idx == varray.length - 1
+      };
+    },
+  }
+};
+</script>
+
+<style lang="sass" scoped>
+input#prefixFilter
+  display: block
+  margin: 0 auto
+
+.variant
+  box-sizing: border-box
+  border: 1px solid brown
+  background-color: lightyellow
+  &:hover
+    background-color: yellow
+  a
+    color: #663300
+    text-decoration: none
+  .boxtitle
+    font-weight: bold
+    margin-bottom: 0
+  .description
+    @media screen and (max-width: 767px)
+      margin-top: 0
+
+.last-noneighb
+  margin: 0 auto
+</style>
index e5d0285..d638b07 100644 (file)
@@ -2,18 +2,9 @@
 main
   .row
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 main
   .row
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
-      input#prefixFilter(
-        v-model="curPrefix"
-        @input="setCurPrefix($event)"
-        :placeholder="st.tr['Prefix?']"
-      )
-    .variant.col-sm-12.col-md-5.col-lg-4(
-      v-for="(v,idx) in filteredVariants"
-      :class="getVclasses(filteredVariants, idx)"
-    )
-      router-link(:to="getLink(v.name)")
-        h4.boxtitle.text-center {{ v.name }}
-        p.description.text-center {{ st.tr[v.desc] }}
+      a#mainLink(href="/#/variants/list")
+        | {{ st.tr["View alphabetical variants list"] }}
+      div(v-html="content")
 </template>
 
 <script>
 </template>
 
 <script>
@@ -22,32 +13,20 @@ export default {
   name: "my-variants",
   data: function() {
     return {
   name: "my-variants",
   data: function() {
     return {
-      curPrefix: "",
       st: store.state
     };
   },
       st: store.state
     };
   },
-  mounted: function() {
-    document.getElementById("prefixFilter").focus();
-  },
   computed: {
   computed: {
-    filteredVariants: function() {
-      const capitalizedPrefix = this.curPrefix.replace(/^\w/, c =>
-        c.toUpperCase()
+    content: function() {
+      // (AJAX) Request to get rules content (plain text, HTML)
+      return (
+        require("raw-loader!@/translations/variants/" + 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(/"$/, "")
       );
       );
-      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);
-        });
-      return variants;
     }
   },
   methods: {
     }
   },
   methods: {
@@ -71,26 +50,9 @@ export default {
 </script>
 
 <style lang="sass" scoped>
 </script>
 
 <style lang="sass" scoped>
-input#prefixFilter
+a#mainLink
   display: block
   display: block
-  margin: 0 auto
-
-.variant
-  box-sizing: border-box
-  border: 1px solid brown
-  background-color: lightyellow
-  &:hover
-    background-color: yellow
-  a
-    color: #663300
-    text-decoration: none
-  .boxtitle
-    font-weight: bold
-    margin-bottom: 0
-  .description
-    @media screen and (max-width: 767px)
-      margin-top: 0
-
-.last-noneighb
-  margin: 0 auto
+  margin: 10px auto
+  text-align: center
+  font-size: 1.3em
 </style>
 </style>
index a44a3ab..6b664a5 100644 (file)
@@ -26,6 +26,7 @@ insert or ignore into Variants (name, description) values
   ('Benedict', 'Change colors'),
   ('Berolina', 'Pawns move diagonally'),
   ('Bicolour', 'Harassed kings'),
   ('Benedict', 'Change colors'),
   ('Berolina', 'Pawns move diagonally'),
   ('Bicolour', 'Harassed kings'),
+  ('Bishopawns', 'Bishop versus pawns'),
   ('Cannibal', 'Capture powers'),
   ('Capture', 'Mandatory captures'),
   ('Checkered1', 'Shared pieces (v1)'),
   ('Cannibal', 'Capture powers'),
   ('Capture', 'Mandatory captures'),
   ('Checkered1', 'Shared pieces (v1)'),
@@ -40,6 +41,7 @@ insert or ignore into Variants (name, description) values
   ('Crazyhouse', 'Captures reborn'),
   ('Cylinder', 'Neverending rows'),
   ('Diamond', 'Rotating board'),
   ('Crazyhouse', 'Captures reborn'),
   ('Cylinder', 'Neverending rows'),
   ('Diamond', 'Rotating board'),
+  ('Discoduel', 'Enter the disco'),
   ('Doublearmy', '64 pieces on the board'),
   ('Doublemove1', 'Double moves (v1)'),
   ('Doublemove2', 'Double moves (v2)'),
   ('Doublearmy', '64 pieces on the board'),
   ('Doublemove1', 'Double moves (v1)'),
   ('Doublemove2', 'Double moves (v2)'),
@@ -58,6 +60,7 @@ insert or ignore into Variants (name, description) values
   ('Interweave', 'Interweaved colorbound teams'),
   ('Kinglet', 'Protect your pawns'),
   ('Knightmate', 'Mate the knight'),
   ('Interweave', 'Interweaved colorbound teams'),
   ('Kinglet', 'Protect your pawns'),
   ('Knightmate', 'Mate the knight'),
+  ('Knightpawns', 'Knight versus pawns'),
   ('Knightrelay1', 'Move like a knight (v1)'),
   ('Knightrelay2', 'Move like a knight (v2)'),
   ('Koopa', 'Stun & kick pieces'),
   ('Knightrelay1', 'Move like a knight (v1)'),
   ('Knightrelay2', 'Move like a knight (v2)'),
   ('Koopa', 'Stun & kick pieces'),
@@ -77,14 +80,18 @@ insert or ignore into Variants (name, description) values
   ('Pacifist1', 'Convert & support (v1)'),
   ('Pacifist2', 'Convert & support (v2)'),
   ('Parachute', 'Landing on the board'),
   ('Pacifist1', 'Convert & support (v1)'),
   ('Pacifist2', 'Convert & support (v2)'),
   ('Parachute', 'Landing on the board'),
-  ('Pawns', 'Reach the last rank'),
+  ('Pawnmassacre', 'Pieces upside down'),
+  ('Pawns', 'Reach the last rank (v1)'),
+  ('Pawnsking', 'Reach the last rank (v2)'),
   ('Perfect', 'Powerful pieces'),
   ('Pocketknight', 'Knight in pocket'),
   ('Perfect', 'Powerful pieces'),
   ('Pocketknight', 'Knight in pocket'),
+  ('Progressive', 'Play more and more moves'),
   ('Racingkings', 'Kings cross the 8x8 board'),
   ('Rampage', 'Move under cover'),
   ('Rifle', 'Shoot pieces'),
   ('Recycle', 'Reuse pieces'),
   ('Rococo', 'Capture on the edge'),
   ('Racingkings', 'Kings cross the 8x8 board'),
   ('Rampage', 'Move under cover'),
   ('Rifle', 'Shoot pieces'),
   ('Recycle', 'Reuse pieces'),
   ('Rococo', 'Capture on the edge'),
+  ('Rookpawns', 'Rook versus pawns'),
   ('Royalrace', 'Kings cross the 11x11 board'),
   ('Rugby', 'Transform an essay'),
   ('Schess', 'Seirawan-Harper Chess'),
   ('Royalrace', 'Kings cross the 11x11 board'),
   ('Rugby', 'Transform an essay'),
   ('Schess', 'Seirawan-Harper Chess'),