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)"
-      )
-        | {{ notation(moveIdx) }}
+        v-html="notation(moveIdx)")
       .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>
index 04c8f06..8590c33 100644 (file)
@@ -20,6 +20,11 @@ const router = new Router({
       name: "variants",
       component: loadView("Variants")
     },
+    {
+      path: "/variants/list",
+      name: "variantlist",
+      component: loadView("VariantList")
+    },
     {
       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",
+  "View alphabetical variants list": "View alphabetical variants list",
   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",
+  "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",
@@ -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",
+  "Enter the disco": "Enter the disco",
   "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",
+  "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",
@@ -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",
+  "Pieces upside down": "Pieces upside down",
   "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",
-  "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",
+  "Rook versus pawns": "Rook versus pawns",
   "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",
+  "View alphabetical variants list": "Ver la lista alfabética de variantes",
   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",
+  "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",
@@ -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",
+  "Enter the disco": "Entrar en la discoteca",
   "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",
+  "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",
@@ -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",
+  "Pieces upside down": "Piezas al revés",
   "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",
-  "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",
+  "Rook versus pawns": "Torre contra peones",
   "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",
+  "View alphabetical variants list": "Voir la liste alphabétique des variantes",
   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",
+  "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",
@@ -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",
+  "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",
@@ -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",
+  "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",
@@ -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",
+  "Pieces upside down": "Pièces à l'envers",
   "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",
-  "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é",
+  "Rook versus pawns": "Tour contre pions",
   "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
 
-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
 
-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
 
-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")
-    |  Marseillais Chess
+    | Marseillais Chess
   | &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)) {
-    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];
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);
-    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];
 
@@ -119,11 +119,9 @@ export class DarkRules extends ChessRules {
 
   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:
-      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();
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 { randInt } from "@/utils/alea";
 
 export class Doublemove1Rules extends ChessRules {
   static IsGoodEnpassant(enpassant) {
@@ -88,8 +87,15 @@ export class Doublemove1Rules extends ChessRules {
       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;
@@ -115,7 +121,7 @@ export class Doublemove1Rules extends ChessRules {
     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];
@@ -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;
     }
-    else if (
+    if (
       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();
-    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
-        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]);
-          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]);
     }
-
-    // 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;
 
-    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;
@@ -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;
     }
-    else if (
+    if (
       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)];
-    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]);
-        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]);
     }
-
-    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 curDir = V.LANCER_DIRS[dirCode];
     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.
-        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];
@@ -647,10 +647,11 @@ export class EightpiecesRules extends ChessRules {
       });
       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 (
+          (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
         ) {
@@ -842,9 +843,21 @@ export class EightpiecesRules extends ChessRules {
         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)];
-        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;
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;
   }
 
+  scanKings() {}
+
   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 "*";
   }
+
+  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
-      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>
@@ -22,32 +13,20 @@ 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()
+    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: {
@@ -71,26 +50,9 @@ export default {
 </script>
 
 <style lang="sass" scoped>
-input#prefixFilter
+a#mainLink
   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>
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'),
+  ('Bishopawns', 'Bishop versus pawns'),
   ('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'),
+  ('Discoduel', 'Enter the disco'),
   ('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'),
+  ('Knightpawns', 'Knight versus pawns'),
   ('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'),
-  ('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'),
+  ('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'),
+  ('Rookpawns', 'Rook versus pawns'),
   ('Royalrace', 'Kings cross the 11x11 board'),
   ('Rugby', 'Transform an essay'),
   ('Schess', 'Seirawan-Harper Chess'),