Add Konane, (very) early drafts of Emergo/Fanorona/Yote/Gomoku, fix repetitions detec...
authorBenjamin Auder <benjamin.auder@somewhere>
Sun, 17 Jan 2021 17:17:51 +0000 (18:17 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Sun, 17 Jan 2021 17:17:51 +0000 (18:17 +0100)
36 files changed:
client/public/images/pieces/Konane/SOURCE [new file with mode: 0644]
client/public/images/pieces/Konane/bp.svg [new file with mode: 0644]
client/public/images/pieces/Konane/wp.svg [new file with mode: 0644]
client/src/base_rules.js
client/src/components/Board.vue
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Emergo/en.pug [new file with mode: 0644]
client/src/translations/rules/Emergo/es.pug [new file with mode: 0644]
client/src/translations/rules/Emergo/fr.pug [new file with mode: 0644]
client/src/translations/rules/Fanorona/en.pug [new file with mode: 0644]
client/src/translations/rules/Fanorona/es.pug [new file with mode: 0644]
client/src/translations/rules/Fanorona/fr.pug [new file with mode: 0644]
client/src/translations/rules/Gomoku/en.pug [new file with mode: 0644]
client/src/translations/rules/Gomoku/es.pug [new file with mode: 0644]
client/src/translations/rules/Gomoku/fr.pug [new file with mode: 0644]
client/src/translations/rules/Konane/en.pug [new file with mode: 0644]
client/src/translations/rules/Konane/es.pug [new file with mode: 0644]
client/src/translations/rules/Konane/fr.pug [new file with mode: 0644]
client/src/translations/rules/Yote/en.pug [new file with mode: 0644]
client/src/translations/rules/Yote/es.pug [new file with mode: 0644]
client/src/translations/rules/Yote/fr.pug [new file with mode: 0644]
client/src/translations/variants/en.pug
client/src/translations/variants/es.pug
client/src/translations/variants/fr.pug
client/src/variants/Bario.js
client/src/variants/Emergo.js [new file with mode: 0644]
client/src/variants/Fanorona.js [new file with mode: 0644]
client/src/variants/Gomoku.js [new file with mode: 0644]
client/src/variants/Konane.js [new file with mode: 0644]
client/src/variants/Otage.js
client/src/variants/Pacosako.js
client/src/variants/Selfabsorption.js
client/src/variants/Yote.js [new file with mode: 0644]
server/db/populate.sql

diff --git a/client/public/images/pieces/Konane/SOURCE b/client/public/images/pieces/Konane/SOURCE
new file mode 100644 (file)
index 0000000..df8ca5f
--- /dev/null
@@ -0,0 +1,3 @@
+https://commons.wikimedia.org/wiki/File:Go_w.svg
+https://commons.wikimedia.org/wiki/File:Go_b.svg
+(EDITED: remove yellow background)
diff --git a/client/public/images/pieces/Konane/bp.svg b/client/public/images/pieces/Konane/bp.svg
new file mode 100644 (file)
index 0000000..75e907e
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<defs><radialGradient id="rg" cx=".47" cy=".49" r=".48">
+<stop offset=".7" stop-color="#FFF"/>
+<stop offset=".9" stop-color="#DDD"/>
+<stop offset="1" stop-color="#777"/>
+</radialGradient></defs>
+<circle cx="250" cy="250" r="235" fill="url(#rg)"/>
+</svg>
diff --git a/client/public/images/pieces/Konane/wp.svg b/client/public/images/pieces/Konane/wp.svg
new file mode 100644 (file)
index 0000000..357079e
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<defs><radialGradient id="rg" cx=".3" cy=".3" r=".8">
+<stop offset="0" stop-color="#777"/>
+<stop offset=".3" stop-color="#222"/>
+<stop offset="1" stop-color="#000"/>
+</radialGradient></defs>
+<circle cx="250" cy="250" r="235" fill="url(#rg)"/>
+</svg>
index a9b6a6a..0549139 100644 (file)
@@ -136,6 +136,11 @@ export const ChessRules = class ChessRules {
     return false;
   }
 
     return false;
   }
 
+  // At some stages, some games could wait clicks only:
+  onlyClick() {
+    return false;
+  }
+
   // Some variants use click infos:
   doClick() {
     return null;
   // Some variants use click infos:
   doClick() {
     return null;
index b8d0fad..cc3f6fd 100644 (file)
@@ -659,8 +659,9 @@ export default {
             // Emit the click event which could be used by some variants
             const targetId =
               (withPiece ? e.target.parentNode.id : e.target.id);
             // Emit the click event which could be used by some variants
             const targetId =
               (withPiece ? e.target.parentNode.id : e.target.id);
-            this.$emit("click-square", getSquareFromId(targetId));
-            if (withPiece) {
+            const sq = getSquareFromId(targetId);
+            this.$emit("click-square", sq);
+            if (withPiece && !this.vr.onlyClick(sq)) {
               this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
               // For potential drag'n drop, remember start coordinates
               // (to center the piece on mouse cursor)
               this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
               // For potential drag'n drop, remember start coordinates
               // (to center the piece on mouse cursor)
index 90a4fa1..06fadc9 100644 (file)
@@ -170,6 +170,8 @@ export const translations = {
   "A quantum story": "A quantum story",
   "A wizard in the corner": "A wizard in the corner",
   "Absorb powers": "Absorb powers",
   "A quantum story": "A quantum story",
   "A wizard in the corner": "A wizard in the corner",
   "Absorb powers": "Absorb powers",
+  "African Draughts": "African Draughts",
+  "Align five stones": "Align five stones",
   "All of the same color": "All of the same color",
   "Ancient rules": "Ancient rules",
   "As in the movie": "As in the movie",
   "All of the same color": "All of the same color",
   "Ancient rules": "Ancient rules",
   "As in the movie": "As in the movie",
@@ -224,6 +226,7 @@ export const translations = {
   "Get strong at self-mate": "Get strong at self-mate",
   "Give three checks": "Give three checks",
   "Harassed kings": "Harassed kings",
   "Get strong at self-mate": "Get strong at self-mate",
   "Give three checks": "Give three checks",
   "Harassed kings": "Harassed kings",
+  "Hawaiian Checkers": "Hawaiian Checkers",
   "Japanese Chess": "Japanese Chess",
   "Jump the borders": "Jump the borders",
   "Keep antiking in check (v1)": "Keep antiking in check (v1)",
   "Japanese Chess": "Japanese Chess",
   "Jump the borders": "Jump the borders",
   "Keep antiking in check (v1)": "Keep antiking in check (v1)",
@@ -242,7 +245,7 @@ export const translations = {
   "Long jumps over pieces": "Long jumps over pieces",
   "Long live the Queen": "Long live the Queen",
   "Lose all pieces": "Lose all pieces",
   "Long jumps over pieces": "Long jumps over pieces",
   "Long live the Queen": "Long live the Queen",
   "Lose all pieces": "Lose all pieces",
-  "Rearrange enemy pieces": "Rearrange enemy pieces",
+  "Malagasy Draughts": "Malagasy Draughts",
   "Mandatory captures": "Mandatory captures",
   "Mate any piece (v1)": "Mate any piece (v1)",
   "Mate any piece (v2)": "Mate any piece (v2)",
   "Mandatory captures": "Mandatory captures",
   "Mate any piece (v1)": "Mate any piece (v1)",
   "Mate any piece (v2)": "Mate any piece (v2)",
@@ -282,6 +285,7 @@ export const translations = {
   "Queen versus pawns": "Queen versus pawns",
   "Reach the last rank (v1)": "Reach the last rank (v1)",
   "Reach the last rank (v2)": "Reach the last rank (v2)",
   "Queen versus pawns": "Queen versus pawns",
   "Reach the last rank (v1)": "Reach the last rank (v1)",
   "Reach the last rank (v2)": "Reach the last rank (v2)",
+  "Rearrange enemy pieces": "Rearrange enemy pieces",
   "Replace pieces": "Replace pieces",
   "Reposition pieces": "Reposition pieces",
   "Reuse pieces": "Reuse pieces",
   "Replace pieces": "Replace pieces",
   "Reposition pieces": "Reposition pieces",
   "Reuse pieces": "Reuse pieces",
@@ -300,6 +304,7 @@ export const translations = {
   "Squares disappear": "Squares disappear",
   "Squat last rank (v1)": "Squat last rank (v1)",
   "Squat last rank (v2)": "Squat last rank (v2)",
   "Squares disappear": "Squares disappear",
   "Squat last rank (v1)": "Squat last rank (v1)",
   "Squat last rank (v2)": "Squat last rank (v2)",
+  "Stacking Checkers variant": "Stacking Checkers variant",
   "Standard rules": "Standard rules",
   "Stun & kick pieces": "Stun & kick pieces",
   "Thai Chess (v1)": "Thai Chess (v1)",
   "Standard rules": "Standard rules",
   "Stun & kick pieces": "Stun & kick pieces",
   "Thai Chess (v1)": "Thai Chess (v1)",
index 650aea9..cab69cf 100644 (file)
@@ -170,6 +170,8 @@ export const translations = {
   "A quantum story": "Una historia cuántica",
   "A wizard in the corner": "Un mago en la esquina",
   "Absorb powers": "Absorber poderes",
   "A quantum story": "Una historia cuántica",
   "A wizard in the corner": "Un mago en la esquina",
   "Absorb powers": "Absorber poderes",
+  "African Draughts": "Damas africanas",
+  "Align five stones": "Alinea cinco piedras",
   "All of the same color": "Todo el mismo color",
   "Ancient rules": "Viejas reglas",
   "As in the movie": "Como en la pelicula",
   "All of the same color": "Todo el mismo color",
   "Ancient rules": "Viejas reglas",
   "As in the movie": "Como en la pelicula",
@@ -224,6 +226,7 @@ export const translations = {
   "Get strong at self-mate": "Progreso en mates asistidos",
   "Give three checks": "Dar tres jaques",
   "Harassed kings": "Reyes acosados",
   "Get strong at self-mate": "Progreso en mates asistidos",
   "Give three checks": "Dar tres jaques",
   "Harassed kings": "Reyes acosados",
+  "Hawaiian Checkers": "Damas hawaianas",
   "Japanese Chess": "Ajedrez japonés",
   "Jump the borders": "Saltar las fronteras",
   "Keep antiking in check (v1)": "Mantener el antirey en jaque (v1)",
   "Japanese Chess": "Ajedrez japonés",
   "Jump the borders": "Saltar las fronteras",
   "Keep antiking in check (v1)": "Mantener el antirey en jaque (v1)",
@@ -242,7 +245,7 @@ export const translations = {
   "Long jumps over pieces": "Saltos largos sobre las piezas",
   "Long live the Queen": "Larga vida a la reina",
   "Lose all pieces": "Perder todas las piezas",
   "Long jumps over pieces": "Saltos largos sobre las piezas",
   "Long live the Queen": "Larga vida a la reina",
   "Lose all pieces": "Perder todas las piezas",
-  "Rearrange enemy pieces": "Reorganizar piezas opuestas",
+  "Malagasy Draughts": "Damas malgaches",
   "Mandatory captures": "Capturas obligatorias",
   "Mate any piece (v1)": "Matar cualquier pieza (v1)",
   "Mate any piece (v2)": "Matar cualquier pieza (v2)",
   "Mandatory captures": "Capturas obligatorias",
   "Mate any piece (v1)": "Matar cualquier pieza (v1)",
   "Mate any piece (v2)": "Matar cualquier pieza (v2)",
@@ -282,6 +285,7 @@ export const translations = {
   "Queen versus pawns": "Dama contra peones",
   "Reach the last rank (v1)": "Llegar a la última fila (v1)",
   "Reach the last rank (v2)": "Llegar a la última fila (v2)",
   "Queen versus pawns": "Dama contra peones",
   "Reach the last rank (v1)": "Llegar a la última fila (v1)",
   "Reach the last rank (v2)": "Llegar a la última fila (v2)",
+  "Rearrange enemy pieces": "Reorganizar piezas opuestas",
   "Replace pieces": "Reemplazar piezas",
   "Reposition pieces": "Reposicionar las piezas",
   "Reuse pieces": "Reutilizar piezas",
   "Replace pieces": "Reemplazar piezas",
   "Reposition pieces": "Reposicionar las piezas",
   "Reuse pieces": "Reutilizar piezas",
@@ -300,6 +304,7 @@ export const translations = {
   "Squares disappear": "Las casillas desaparecen",
   "Squat last rank (v1)": "Ocupa la última fila (v1)",
   "Squat last rank (v2)": "Ocupa la última fila (v2)",
   "Squares disappear": "Las casillas desaparecen",
   "Squat last rank (v1)": "Ocupa la última fila (v1)",
   "Squat last rank (v2)": "Ocupa la última fila (v2)",
+  "Stacking Checkers variant": "Variante de damas con pilas",
   "Standard rules": "Reglas estandar",
   "Stun & kick pieces": "Aturdir & patear piezas",
   "Thai Chess (v1)": "Ajedrez tailandés (v1)",
   "Standard rules": "Reglas estandar",
   "Stun & kick pieces": "Aturdir & patear piezas",
   "Thai Chess (v1)": "Ajedrez tailandés (v1)",
index 5a04fab..ba812a4 100644 (file)
@@ -170,6 +170,8 @@ export const translations = {
   "A quantum story": "Une histoire quantique",
   "A wizard in the corner": "Un sorcier dans le coin",
   "Absorb powers": "Absorber les pouvoirs",
   "A quantum story": "Une histoire quantique",
   "A wizard in the corner": "Un sorcier dans le coin",
   "Absorb powers": "Absorber les pouvoirs",
+  "African Draughts": "Dames africaines",
+  "Align five stones": "Alignez cinq pierres",
   "All of the same color": "Tout de la même couleur",
   "Ancient rules": "Règles anciennes",
   "As in the movie": "Comme dans le film",
   "All of the same color": "Tout de la même couleur",
   "Ancient rules": "Règles anciennes",
   "As in the movie": "Comme dans le film",
@@ -224,6 +226,7 @@ export const translations = {
   "Get strong at self-mate": "Progressez en mats aidés",
   "Give three checks": "Donnez trois échecs",
   "Harassed kings": "Rois harcelés",
   "Get strong at self-mate": "Progressez en mats aidés",
   "Give three checks": "Donnez trois échecs",
   "Harassed kings": "Rois harcelés",
+  "Hawaiian Checkers": "Dames hawaïennes",
   "Japanese Chess": "Échecs japonais",
   "Jump the borders": "Sauter les frontières",
   "Keep antiking in check (v1)": "Gardez l'antiroi en échec (v1)",
   "Japanese Chess": "Échecs japonais",
   "Jump the borders": "Sauter les frontières",
   "Keep antiking in check (v1)": "Gardez l'antiroi en échec (v1)",
@@ -242,7 +245,7 @@ export const translations = {
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
   "Long live the Queen": "Long vie à la Reine",
   "Lose all pieces": "Perdez toutes les pièces",
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
   "Long live the Queen": "Long vie à la Reine",
   "Lose all pieces": "Perdez toutes les pièces",
-  "Rearrange enemy pieces": "Réorganisez les pièces adverses",
+  "Malagasy Draughts": "Dames malgaches",
   "Mandatory captures": "Captures obligatoires",
   "Mate any piece (v1)": "Matez n'importe quelle pièce (v1)",
   "Mate any piece (v2)": "Matez n'importe quelle pièce (v2)",
   "Mandatory captures": "Captures obligatoires",
   "Mate any piece (v1)": "Matez n'importe quelle pièce (v1)",
   "Mate any piece (v2)": "Matez n'importe quelle pièce (v2)",
@@ -282,6 +285,7 @@ export const translations = {
   "Queen versus pawns": "Dame contre pions",
   "Reach the last rank (v1)": "Atteignez la dernière rangée (v1)",
   "Reach the last rank (v2)": "Atteignez la dernière rangée (v2)",
   "Queen versus pawns": "Dame contre pions",
   "Reach the last rank (v1)": "Atteignez la dernière rangée (v1)",
   "Reach the last rank (v2)": "Atteignez la dernière rangée (v2)",
+  "Rearrange enemy pieces": "Réorganisez les pièces adverses",
   "Replace pieces": "Remplacer les pièces",
   "Reposition pieces": "Replacer les pièces",
   "Reuse pieces": "Réutiliser les pièces",
   "Replace pieces": "Remplacer les pièces",
   "Reposition pieces": "Replacer les pièces",
   "Reuse pieces": "Réutiliser les pièces",
@@ -300,6 +304,7 @@ export const translations = {
   "Squares disappear": "Les cases disparaissent",
   "Squat last rank (v1)": "Occupez la dernière rangée (v1)",
   "Squat last rank (v2)": "Occupez la dernière rangée (v2)",
   "Squares disappear": "Les cases disparaissent",
   "Squat last rank (v1)": "Occupez la dernière rangée (v1)",
   "Squat last rank (v2)": "Occupez la dernière rangée (v2)",
+  "Stacking Checkers variant": "Variante des Dames avec empilements",
   "Standard rules": "Règles usuelles",
   "Stun & kick pieces": "Étourdissez & frappez les pièces",
   "Thai Chess (v1)": "Échecs thai (v1)",
   "Standard rules": "Règles usuelles",
   "Stun & kick pieces": "Étourdissez & frappez les pièces",
   "Thai Chess (v1)": "Échecs thai (v1)",
diff --git a/client/src/translations/rules/Emergo/en.pug b/client/src/translations/rules/Emergo/en.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Emergo/es.pug b/client/src/translations/rules/Emergo/es.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Emergo/fr.pug b/client/src/translations/rules/Emergo/fr.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Fanorona/en.pug b/client/src/translations/rules/Fanorona/en.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Fanorona/es.pug b/client/src/translations/rules/Fanorona/es.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Fanorona/fr.pug b/client/src/translations/rules/Fanorona/fr.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Gomoku/en.pug b/client/src/translations/rules/Gomoku/en.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Gomoku/es.pug b/client/src/translations/rules/Gomoku/es.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Gomoku/fr.pug b/client/src/translations/rules/Gomoku/fr.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Konane/en.pug b/client/src/translations/rules/Konane/en.pug
new file mode 100644 (file)
index 0000000..9a15f67
--- /dev/null
@@ -0,0 +1,49 @@
+p.boxed
+  | Capture orthogonally at each turn, "as in Draughts".
+  | If you cannot capture, you lose.
+
+p.
+  To initiate the game, the first player (black) must remove one of his stones
+  either in the upper left or lower right corner, or in the center,
+  as marked on the illustration.
+  The second player (white) then removes a stone orthogonally adjacent to
+  the first removed one, and the game starts.
+  To remove a stone at this stage, click it.
+
+figure.diagram-container
+  .diagram
+    | fen:PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP a8,d5,e4,h1:
+  figcaption Allowed first "moves" (stone removal).
+
+p.
+  Every move then is necessary a capture of at least one enemy piece.
+  Capture by jumping orthogonally over an adjacent stone to land right after
+  on an empty square. The intermediate piece is thus removed.
+  You may continue capturing, but only in the same direction,
+  with the same stone.
+  To stop a chain of captures (while more are available), play a move from the
+  current capturer until the location of the last captured stone:
+  g4 on this example.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:PpPpPpPp/pPpPpPpP/PpPpP2p/pPpPp1pP/PpP1Pp1p/pPpPpP1P/PpPpPpPp/pPpPpPpP:
+  .diagram.diag22
+    | fen:PpPpPp1p/pPpPpP1P/PpPpP1Pp/pPpPp1pP/PpP1Pp1p/pPpPpP1P/PpPpPpPp/pPpPpPpP g4,g7:
+  figcaption.
+    Before and after a first capture g8xg7.
+    The available continuations are indicated.
+
+p If a player has no possible capture, he loses.
+
+h3 More information
+
+p
+  | See for example 
+  a(href="https://hawaiiancheckers.com/") hawaiiancheckers.com
+  | , and an 
+  a(href="https://www.youtube.com/watch?app=desktop&v=-_y-B2wAwyw")
+    | example game
+  | . Konane is also playable on 
+  a(href="https://brainking.com/en/GameRules?tp=94") brainking.com
+  | .
diff --git a/client/src/translations/rules/Konane/es.pug b/client/src/translations/rules/Konane/es.pug
new file mode 100644 (file)
index 0000000..7cb7f73
--- /dev/null
@@ -0,0 +1,49 @@
+p.boxed
+  | Captura ortogonalmente en cada turno, "como a las Damas".
+  | Si no es posible la captura, ha perdido.
+
+p.
+  Para iniciar el juego, el primer jugador (negras) debe eliminar uno de
+  sus piedras en la esquina superior izquierda o inferior derecha,
+  ya sea en el centro, como se ilustra.
+  El segundo jugador (blancas) luego quita una piedra ortogonalmente
+  adyacente al primero eliminado, y comienza el juego.
+  Para quitar una piedra, haga clic en ella.
+
+figure.diagram-container
+  .diagram
+    | fen:PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP a8,d5,e4,h1:
+  figcaption Los primeros "movimientos" autorizado (eliminación de piedras).
+
+p.
+  Cada movimiento implica necesariamente la captura de al menos una pieza
+  enemiga. Captura saltando ortogonalmente sobre un obstáculo para aterrizar
+  en un cuadrado vacío justo detrás. Se quita así la pieza intermedia.
+  Tienes derecho a seguir capturando, pero solo en el mismo
+  dirección, con la misma piedra.
+  Para detener una cadena de capturas (mientras que otras son posibles),
+  hacer un movimiento de la captura actual a la ubicación de la última
+  pieza capturada: g4 en el ejemplo.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:PpPpPpPp/pPpPpPpP/PpPpP2p/pPpPp1pP/PpP1Pp1p/pPpPpP1P/PpPpPpPp/pPpPpPpP:
+  .diagram.diag22
+    | fen:PpPpPp1p/pPpPpP1P/PpPpP1Pp/pPpPp1pP/PpP1Pp1p/pPpPpP1P/PpPpPpPp/pPpPpPpP g4,g7:
+  figcaption.
+    Antes y después de una primera captura g8xg7.
+    Se indican las continuaciones disponibles.
+
+p Si un jugador no tiene más capturas disponibles, pierde.
+
+h3 Más información
+
+p
+  | Ver por ejemplo 
+  a(href="https://hawaiiancheckers.com/") hawaiiancheckers.com
+  | , y una 
+  a(href="https://www.youtube.com/watch?app=desktop&v=-_y-B2wAwyw")
+    | partida ejemplo
+  | . Konane también se puede jugar en 
+  a(href="https://brainking.com/en/GameRules?tp=94") brainking.com
+  | .
diff --git a/client/src/translations/rules/Konane/fr.pug b/client/src/translations/rules/Konane/fr.pug
new file mode 100644 (file)
index 0000000..2329737
--- /dev/null
@@ -0,0 +1,49 @@
+p.boxed
+  | Capturez orthogonalement à chaque tour, "comme aux Dames".
+  | Si aucune capture n'est possible, vous avez perdu.
+
+p.
+  Pour initialiser la partie, le premier joueur (noirs) doit retirer une de
+  ses pierres soit dans le coin supérieur gauche ou inférieur droit,
+  soit au centre, comme illustré.
+  Le second joueur (blancs) retire ensuite une pierre orthogonalement
+  adjacente à la première supprimée, et la partie commence.
+  Pour enlever une pierre, cliquez dessus.
+
+figure.diagram-container
+  .diagram
+    | fen:PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP a8,d5,e4,h1:
+  figcaption Premiers "coups" autorisés (suppression de pierre).
+
+p.
+  Chaque coup comporte nécessairement au moins une capture de pièce ennemie.
+  Capturez en sautant orthogonalement par dessus un obstacle pour atterrir
+  sur une case vide juste derrière. La pièce intermédiaire est ainsi retirée.
+  Vous avez le droit de continuer de capturer, mais seulement dans la même
+  direction, avec la même pierre.
+  Pour arrêter une chaîne de captures (alors que d'autres sont possibles),
+  jouez un coup depuis le capturant actuel vers l'emplacement de la dernière
+  pièce capturée : g4 sur l'exemple.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:PpPpPpPp/pPpPpPpP/PpPpP2p/pPpPp1pP/PpP1Pp1p/pPpPpP1P/PpPpPpPp/pPpPpPpP:
+  .diagram.diag22
+    | fen:PpPpPp1p/pPpPpP1P/PpPpP1Pp/pPpPp1pP/PpP1Pp1p/pPpPpP1P/PpPpPpPp/pPpPpPpP g4,g7:
+  figcaption.
+    Avant et après une première capture g8xg7.
+    Les continuations disponibles sont indiquées.
+
+p Si un joueur n'a plus de captures à disposition, il perd.
+
+h3 Plus d'informations
+
+p
+  | Voir par exemple 
+  a(href="https://hawaiiancheckers.com/") hawaiiancheckers.com
+  | , et une 
+  a(href="https://www.youtube.com/watch?app=desktop&v=-_y-B2wAwyw")
+    | partie exemple
+  | . Konane est aussi jouable sur 
+  a(href="https://brainking.com/en/GameRules?tp=94") brainking.com
+  | .
diff --git a/client/src/translations/rules/Yote/en.pug b/client/src/translations/rules/Yote/en.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Yote/es.pug b/client/src/translations/rules/Yote/es.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Yote/fr.pug b/client/src/translations/rules/Yote/fr.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
index 542c2d4..552652f 100644 (file)
@@ -433,6 +433,21 @@ ul
   for v in varlist
     li #[a(href="/#/variants/"+v) #{v}]
 
   for v in varlist
     li #[a(href="/#/variants/"+v) #{v}]
 
+h3 Non-chess
+
+p Some games not chess related.
+-
+  var varlist = [
+    "Emergo",
+    "Fanorona",
+    "Gomoku",
+    "Konane",
+    "Yote"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
 h3 Miscelleanous
 
 p.
 h3 Miscelleanous
 
 p.
index 09fd116..57ba50e 100644 (file)
@@ -443,6 +443,21 @@ ul
   for v in varlist
     li #[a(href="/#/variants/"+v) #{v}]
 
   for v in varlist
     li #[a(href="/#/variants/"+v) #{v}]
 
+h3 Aparte del Ajedrez
+
+p Algunos juegos no están relacionados con el ajedrez.
+-
+  var varlist = [
+    "Emergo",
+    "Fanorona",
+    "Gomoku",
+    "Konane",
+    "Yote"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
 h3 Varios
 
 p.
 h3 Varios
 
 p.
index 144ad2d..cebc622 100644 (file)
@@ -441,6 +441,21 @@ ul
   for v in varlist
     li #[a(href="/#/variants/"+v) #{v}]
 
   for v in varlist
     li #[a(href="/#/variants/"+v) #{v}]
 
+h3 Hors Échecs
+
+p Quelques jeux non connectés aux échecs.
+-
+  var varlist = [
+    "Emergo",
+    "Fanorona",
+    "Gomoku",
+    "Konane",
+    "Yote"
+  ]
+ul
+  for v in varlist
+    li #[a(href="/#/variants/"+v) #{v}]
+
 h3 Divers
 
 p.
 h3 Divers
 
 p.
index e895aee..21965e1 100644 (file)
@@ -46,6 +46,14 @@ export class BarioRules extends ChessRules {
     );
   }
 
     );
   }
 
+  onlyClick([x, y]) {
+    return (
+      this.movesCount <= 1 ||
+      // TODO: next line theoretically shouldn't be required...
+      (this.movesCount == 2 && this.getColor(x, y) != this.turn)
+    );
+  }
+
   // Initiate the game by choosing a square for the king:
   doClick(square) {
     const c = this.turn;
   // Initiate the game by choosing a square for the king:
   doClick(square) {
     const c = this.turn;
@@ -62,7 +70,9 @@ export class BarioRules extends ChessRules {
       appear: [
         new PiPo({ x: square[0], y: square[1], c: c, p: V.KING })
       ],
       appear: [
         new PiPo({ x: square[0], y: square[1], c: c, p: V.KING })
       ],
-      vanish: [],
+      vanish: [
+        new PiPo({ x: square[0], y: square[1], c: c, p: V.UNDEFINED })
+      ],
       start: { x: -1, y: -1 },
     });
   }
       start: { x: -1, y: -1 },
     });
   }
@@ -138,7 +148,7 @@ export class BarioRules extends ChessRules {
   }
 
   static GenRandInitFen() {
   }
 
   static GenRandInitFen() {
-    return "8/pppppppp/8/8/8/8/PPPPPPPP/8 w 0 - 22212221 -";
+    return "uuuuuuuu/pppppppp/8/8/8/8/PPPPPPPP/UUUUUUUU w 0 - 22212221 -";
   }
 
   setOtherVariables(fen) {
   }
 
   setOtherVariables(fen) {
@@ -259,7 +269,9 @@ export class BarioRules extends ChessRules {
           appear: [
             new PiPo({ x: firstRank, y: j, c: color, p: V.KING })
           ],
           appear: [
             new PiPo({ x: firstRank, y: j, c: color, p: V.KING })
           ],
-          vanish: [],
+          vanish: [
+            new PiPo({ x: firstRank, y: j, c: color, p: V.UNDEFINED })
+          ],
           start: { x: -1, y: -1 }
         });
       });
           start: { x: -1, y: -1 }
         });
       });
@@ -379,6 +391,11 @@ export class BarioRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
+  getCheckSquares() {
+    if (this.movesCount <= 2) return [];
+    return super.getCheckSquares();
+  }
+
   play(move) {
     move.turn = [this.turn, this.subTurn]; //easier undo (TODO?)
     const toNextPlayer = () => {
   play(move) {
     move.turn = [this.turn, this.subTurn]; //easier undo (TODO?)
     const toNextPlayer = () => {
@@ -389,40 +406,33 @@ export class BarioRules extends ChessRules {
       this.movesCount++;
       this.postPlay(move);
     };
       this.movesCount++;
       this.postPlay(move);
     };
-    if (move.vanish.length == 0) {
-      if (move.appear.length == 1) toNextPlayer();
-      else {
-        // Removal (subTurn == 0 --> 1)
-        this.reserve[this.turn][move.start.p]--;
-        this.subTurn++;
-      }
-      return;
-    }
-    const start = { x: move.vanish[0].x, y: move.vanish[0].y };
-    const end = { x: move.appear[0].x, y: move.appear[0].y };
-    if (start.x == end.x && start.y == end.y) {
-      // Specialisation (subTurn == 1 before 2)
-      this.reserve[this.turn][move.appear[0].p]--;
-      V.PlayOnBoard(this.board, move);
-      this.definitions.push(move.end);
+    if (this.movesCount <= 1) toNextPlayer();
+    else if (move.vanish.length == 0) {
+      // Removal (subTurn == 0 --> 1)
+      this.reserve[this.turn][move.start.p]--;
       this.subTurn++;
     }
     else {
       this.subTurn++;
     }
     else {
-      // Normal move (subTurn 1 or 2: change turn)
-      this.epSquares.push(this.getEpSquare(move));
-      toNextPlayer();
+      const start = { x: move.vanish[0].x, y: move.vanish[0].y };
+      const end = { x: move.appear[0].x, y: move.appear[0].y };
+      if (start.x == end.x && start.y == end.y) {
+        // Specialisation (subTurn == 1 before 2)
+        this.reserve[this.turn][move.appear[0].p]--;
+        V.PlayOnBoard(this.board, move);
+        this.definitions.push(move.end);
+        this.subTurn++;
+      }
+      else {
+        // Normal move (subTurn 1 or 2: change turn)
+        this.epSquares.push(this.getEpSquare(move));
+        toNextPlayer();
+      }
     }
   }
 
   postPlay(move) {
     const color = V.GetOppCol(this.turn);
     }
   }
 
   postPlay(move) {
     const color = V.GetOppCol(this.turn);
-    if (move.vanish.length == 0) {
-      this.kingPos[color] = [move.end.x, move.end.y];
-      const firstRank = (color == 'w' ? 7 : 0);
-      for (let j = 0; j < 8; j++) {
-        if (j != move.end.y) this.board[firstRank][j] = color + V.UNDEFINED;
-      }
-    }
+    if (this.movesCount <= 2) this.kingPos[color] = [move.end.x, move.end.y];
     else {
       if (move.vanish.length == 2 && move.vanish[1].p == V.UNDEFINED)
         this.captureUndefined.push(move.end);
     else {
       if (move.vanish.length == 2 && move.vanish[1].p == V.UNDEFINED)
         this.captureUndefined.push(move.end);
@@ -509,35 +519,30 @@ export class BarioRules extends ChessRules {
       this.movesCount--;
       this.postUndo(move);
     };
       this.movesCount--;
       this.postUndo(move);
     };
-    if (move.vanish.length == 0) {
-      if (move.appear.length == 1) toPrevPlayer();
-      else {
-        this.reserve[this.turn][move.start.p]++;
-        this.subTurn = move.turn[1];
-      }
-      return;
-    }
-    const start = { x: move.vanish[0].x, y: move.vanish[0].y };
-    const end = { x: move.appear[0].x, y: move.appear[0].y };
-    if (start.x == end.x && start.y == end.y) {
-      this.reserve[this.turn][move.appear[0].p]++;
-      V.UndoOnBoard(this.board, move);
-      this.definitions.pop();
+    if (this.movesCount <= 2) toPrevPlayer();
+    else if (move.vanish.length == 0) {
+      this.reserve[this.turn][move.start.p]++;
       this.subTurn = move.turn[1];
     }
     else {
       this.subTurn = move.turn[1];
     }
     else {
-      this.epSquares.pop();
-      toPrevPlayer();
+      const start = { x: move.vanish[0].x, y: move.vanish[0].y };
+      const end = { x: move.appear[0].x, y: move.appear[0].y };
+      if (start.x == end.x && start.y == end.y) {
+        this.reserve[this.turn][move.appear[0].p]++;
+        V.UndoOnBoard(this.board, move);
+        this.definitions.pop();
+        this.subTurn = move.turn[1];
+      }
+      else {
+        this.epSquares.pop();
+        toPrevPlayer();
+      }
     }
   }
 
   postUndo(move) {
     const color = this.turn;
     }
   }
 
   postUndo(move) {
     const color = this.turn;
-    if (move.vanish.length == 0) {
-      this.kingPos[color] = [-1, -1];
-      const firstRank = (color == 'w' ? 7 : 0);
-      for (let j = 0; j < 8; j++) this.board[firstRank][j] = "";
-    }
+    if (this.movesCount <= 1) this.kingPos[color] = [-1, -1];
     else {
       this.captureUndefined.pop();
       if (move.appear[0].p == V.KING) super.postUndo(move);
     else {
       this.captureUndefined.pop();
       if (move.appear[0].p == V.KING) super.postUndo(move);
diff --git a/client/src/variants/Emergo.js b/client/src/variants/Emergo.js
new file mode 100644 (file)
index 0000000..0d81759
--- /dev/null
@@ -0,0 +1,7 @@
+import { ChessRules } from "@/base_rules";
+
+export class YoteRules extends ChessRules {
+
+  // TODO
+
+};
diff --git a/client/src/variants/Fanorona.js b/client/src/variants/Fanorona.js
new file mode 100644 (file)
index 0000000..9f1db3a
--- /dev/null
@@ -0,0 +1,7 @@
+import { ChessRules } from "@/base_rules";
+
+export class FanoronaRules extends ChessRules {
+
+  // TODO
+
+};
diff --git a/client/src/variants/Gomoku.js b/client/src/variants/Gomoku.js
new file mode 100644 (file)
index 0000000..5bf971a
--- /dev/null
@@ -0,0 +1,7 @@
+import { ChessRules } from "@/base_rules";
+
+export class GomokuRules extends ChessRules {
+
+  // TODO
+
+};
diff --git a/client/src/variants/Konane.js b/client/src/variants/Konane.js
new file mode 100644 (file)
index 0000000..8f285ef
--- /dev/null
@@ -0,0 +1,206 @@
+import { ChessRules, Move, PiPo } from "@/base_rules";
+
+// TODO: Maybe more flexible end of game messages (V.ColorsReversed ?!)
+
+export class KonaneRules extends ChessRules {
+
+  static get HasFlags() {
+    return false;
+  }
+
+  static get HasEnpassant() {
+    return false;
+  }
+
+  static get PIECES() {
+    return V.PAWN;
+  }
+
+  getPpath(b) {
+    return "Konane/" + b;
+  }
+
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
+    const rows = position.split("/");
+    if (rows.length != V.size.x) return false;
+    for (let row of rows) {
+      let sumElts = 0;
+      for (let i = 0; i < row.length; i++) {
+        if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
+        else {
+          const num = parseInt(row[i], 10);
+          if (isNaN(num) || num <= 0) return false;
+          sumElts += num;
+        }
+      }
+      if (sumElts != V.size.y) return false;
+    }
+    return true;
+  }
+
+  static GenRandInitFen() {
+    return (
+      "PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/" +
+      "PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP w 0"
+    );
+  }
+
+  setOtherVariables(fen) {
+    this.captures = []; //reinit for each move
+  }
+
+  hoverHighlight(x, y) {
+    if (this.movesCount >= 2) return false;
+    const c = this.turn;
+    if (c == 'w') return (x == y && [0, 3, 4, 7].includes(x));
+    // "Black": search for empty square and allow nearby
+    for (let i of [0, 3, 4, 7]) {
+      if (this.board[i][i] == V.EMPTY)
+        return (Math.abs(x - i) + Math.abs(y - i) == 1)
+    }
+  }
+
+  onlyClick([x, y]) {
+    return (
+      this.movesCount <= 1 ||
+      // TODO: next line theoretically shouldn't be required...
+      (this.movesCount == 2 && this.getColor(x, y) != this.turn)
+    );
+  }
+
+  doClick([x, y]) {
+    if (this.movesCount >= 2) return null;
+    const color = this.turn;
+    if (color == 'w') {
+      if (x != y || ![0, 3, 4, 7].includes(x)) return null;
+      return new Move({
+        appear: [],
+        vanish: [ new PiPo({ x: x, y: y, c: color, p: V.PAWN }) ],
+        end: { x: x, y: y }
+      });
+    }
+    // "Black": search for empty square and allow nearby
+    for (let i of [0, 3, 4, 7]) {
+      if (this.board[i][i] == V.EMPTY) {
+        if (Math.abs(x - i) + Math.abs(y - i) != 1) return null;
+        return new Move({
+          appear: [],
+          vanish: [ new PiPo({ x: x, y: y, c: color, p: V.PAWN }) ],
+          end: { x: x, y: y }
+        });
+      }
+    }
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    if (this.movesCount <= 1) {
+      const mv = this.doClick([x, y]);
+      return (!!mv ? [mv] : []);
+    }
+    const L = this.captures.length;
+    const c = (L > 0 ? this.captures[L-1] : null);
+    const color = this.turn;
+    const oppCol = V.GetOppCol(color);
+    let step = null;
+    let moves = [];
+    if (!!c) {
+      if (x != c.end.x || y != c.end.y) return [];
+      step = [(c.end.x - c.start.x) / 2, (c.end.y - c.start.y) / 2];
+      // Add move to adjacent empty square to mark "end of capture"
+      moves.push(
+        new Move({
+          appear: [],
+          vanish: [],
+          start: { x: x, y: y },
+          end: { x: x - step[0], y: y - step[1] }
+        })
+      );
+    }
+    // Examine captures from here
+    for (let s of (!!step ? [step] : V.steps[V.ROOK])) {
+      let [i, j] = [x + 2*s[0], y + 2*s[1]];
+      if (
+        !!c || //avoid redundant checks if continuation
+        (
+          V.OnBoard(i, j) &&
+          this.board[i][j] == V.EMPTY &&
+          this.board[i - s[0]][j - s[1]] != V.EMPTY &&
+          this.getColor(i - s[0], j - s[1]) == oppCol
+        )
+      ) {
+        let mv = new Move({
+          appear: [
+            new PiPo({ x: i, y: j, c: color, p: V.PAWN })
+          ],
+          vanish: [
+            new PiPo({ x: x, y: y, c: color, p: V.PAWN }),
+            new PiPo({ x: i - s[0], y: j - s[1], c: oppCol, p: V.PAWN })
+          ]
+        });
+        // Is there another capture possible then?
+        [i, j] = [i + 2*s[0], j + 2*s[1]];
+        if (
+          V.OnBoard(i, j) &&
+          this.board[i][j] == V.EMPTY &&
+          this.board[i - s[0]][j - s[1]] != V.EMPTY &&
+          this.getColor(i - s[0], j - s[1]) == oppCol
+        ) {
+          mv.end.moreCapture = true;
+        }
+        moves.push(mv);
+      }
+    }
+    return moves;
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    if (this.atLeastOneMove()) return "*";
+    return (this.turn == "w" ? "0-1" : "1-0");
+  }
+
+  play(move) {
+    V.PlayOnBoard(this.board, move);
+    if (!move.end.moreCapture) {
+      this.turn = V.GetOppCol(this.turn);
+      this.movesCount++;
+      this.captures = [];
+    }
+    else {
+      this.captures.push(
+        {
+          start: move.start,
+          end: { x: move.end.x, y: move.end.y }
+        }
+      );
+    }
+  }
+
+  undo(move) {
+    V.UndoOnBoard(this.board, move);
+    if (!move.end.moreCapture) {
+      this.turn = V.GetOppCol(this.turn);
+      this.movesCount--;
+    }
+    else this.captures.pop();
+  }
+
+  static get SEARCH_DEPTH() {
+    return 4;
+  }
+
+  getNotation(move) {
+    if (this.movesCount <= 1) return V.CoordsToSquare(move.start) + "X";
+    if (move.vanish.length == 0) return "end";
+    return V.CoordsToSquare(move.start) + "x" + V.CoordsToSquare(move.end);
+  }
+
+};
index 6f408bb..742c6ee 100644 (file)
@@ -646,11 +646,19 @@ export class OtageRules extends ChessRules {
       if (!m.end.released) return true;
       // Check for repetitions:
       V.PlayOnBoard(this.board, m);
       if (!m.end.released) return true;
       // Check for repetitions:
       V.PlayOnBoard(this.board, m);
-      const newState = { piece: m.end.released, position: this.getBaseFen() };
+      const newState = {
+        piece: m.end.released,
+        square: { x: m.end.x, y: m.end.y },
+        position: this.getBaseFen()
+      };
       const repet =
         this.repetitions.some(r => {
           return (
             r.piece == newState.piece &&
       const repet =
         this.repetitions.some(r => {
           return (
             r.piece == newState.piece &&
+            (
+              r.square.x == newState.square.x &&
+              r.square.y == newState.square.y &&
+            ) &&
             r.position == newState.position
           );
         });
             r.position == newState.position
           );
         });
@@ -724,6 +732,7 @@ export class OtageRules extends ChessRules {
       this.repetitions.push(
         {
           piece: move.end.released,
       this.repetitions.push(
         {
           piece: move.end.released,
+          square: { x: move.end.x, y: move.end.y },
           position: this.getBaseFen()
         }
       );
           position: this.getBaseFen()
         }
       );
index d4908a5..d219916 100644 (file)
@@ -716,11 +716,19 @@ export class PacosakoRules extends ChessRules {
       if (!m.end.released) return true;
       // Check for repetitions:
       V.PlayOnBoard(this.board, m);
       if (!m.end.released) return true;
       // Check for repetitions:
       V.PlayOnBoard(this.board, m);
-      const newState = { piece: m.end.released, position: this.getBaseFen() };
+      const newState = {
+        piece: m.end.released,
+        square: { x: m.end.x, y: m.end.y },
+        position: this.getBaseFen()
+      };
       const repet =
         this.repetitions.some(r => {
           return (
             r.piece == newState.piece &&
       const repet =
         this.repetitions.some(r => {
           return (
             r.piece == newState.piece &&
+            (
+              r.square.x == newState.square.x &&
+              r.square.y == newState.square.y &&
+            ) &&
             r.position == newState.position
           );
         });
             r.position == newState.position
           );
         });
@@ -801,6 +809,7 @@ export class PacosakoRules extends ChessRules {
       this.repetitions.push(
         {
           piece: move.end.released,
       this.repetitions.push(
         {
           piece: move.end.released,
+          square: { x: move.end.x, y: move.end.y },
           position: this.getBaseFen()
         }
       );
           position: this.getBaseFen()
         }
       );
index e7d3c07..6f15d48 100644 (file)
@@ -9,10 +9,14 @@ export class SelfabsorptionRules extends AbsorptionRules {
     const p2 = this.getPiece(x2, y2);
     return (
       p1 != p2 &&
     const p2 = this.getPiece(x2, y2);
     return (
       p1 != p2 &&
-      [V.QUEEN, V.ROOK, V.KNIGHT, V.BISHOP].includes(p1) &&
-      [V.QUEEN, V.ROOK, V.KNIGHT, V.BISHOP].includes(p2) &&
+      [V.QUEEN, V.RN, V.BN, V.ROOK, V.KNIGHT, V.BISHOP].includes(p1) &&
+      [V.QUEEN, V.RN, V.BN, V.ROOK, V.KNIGHT, V.BISHOP].includes(p2) &&
       (p1 != V.QUEEN || p2 == V.KNIGHT) &&
       (p1 != V.QUEEN || p2 == V.KNIGHT) &&
-      (p2 != V.QUEEN || p1 == V.KNIGHT)
+      (p2 != V.QUEEN || p1 == V.KNIGHT) &&
+      (p1 != V.RN || p2 == V.BISHOP) &&
+      (p2 != V.RN || p1 == V.BISHOP) &&
+      (p1 != V.BN || p2 == V.ROOK) &&
+      (p2 != V.BN || p1 == V.ROOK)
     );
   }
 
     );
   }
 
diff --git a/client/src/variants/Yote.js b/client/src/variants/Yote.js
new file mode 100644 (file)
index 0000000..0d81759
--- /dev/null
@@ -0,0 +1,7 @@
+import { ChessRules } from "@/base_rules";
+
+export class YoteRules extends ChessRules {
+
+  // TODO
+
+};
index 5322ce9..e419e90 100644 (file)
@@ -57,16 +57,19 @@ insert or ignore into Variants (name, description) values
   ('Doublemove2', 'Double moves (v2)'),
   ('Dynamo', 'Push and pull'),
   ('Eightpieces', 'Each piece is unique'),
   ('Doublemove2', 'Double moves (v2)'),
   ('Dynamo', 'Push and pull'),
   ('Eightpieces', 'Each piece is unique'),
+  ('Emergo', 'Stacking Checkers variant'),
   ('Empire', 'Empire versus Kingdom'),
   ('Enpassant', 'Capture en passant'),
   ('Evolution', 'Faster development'),
   ('Extinction', 'Capture all of a kind'),
   ('Empire', 'Empire versus Kingdom'),
   ('Enpassant', 'Capture en passant'),
   ('Evolution', 'Faster development'),
   ('Extinction', 'Capture all of a kind'),
+  ('Fanorona', 'Malagasy Draughts'),
   ('Football', 'Score a goal'),
   ('Forward', 'Moving forward'),
   ('Freecapture', 'Capture both colors'),
   ('Fugue', 'Baroque Music'),
   ('Fullcavalry', 'Lancers everywhere'),
   ('Fusion', 'Fusion pieces (v1)'),
   ('Football', 'Score a goal'),
   ('Forward', 'Moving forward'),
   ('Freecapture', 'Capture both colors'),
   ('Fugue', 'Baroque Music'),
   ('Fullcavalry', 'Lancers everywhere'),
   ('Fusion', 'Fusion pieces (v1)'),
+  ('Gomoku', 'Align five stones'),
   ('Grand', 'Big board'),
   ('Grasshopper', 'Long jumps over pieces'),
   ('Gridolina', 'Jump the borders'),
   ('Grand', 'Big board'),
   ('Grasshopper', 'Long jumps over pieces'),
   ('Gridolina', 'Jump the borders'),
@@ -84,6 +87,7 @@ insert or ignore into Variants (name, description) values
   ('Knightpawns', 'Knight versus pawns'),
   ('Knightrelay1', 'Move like a knight (v1)'),
   ('Knightrelay2', 'Move like a knight (v2)'),
   ('Knightpawns', 'Knight versus pawns'),
   ('Knightrelay1', 'Move like a knight (v1)'),
   ('Knightrelay2', 'Move like a knight (v2)'),
+  ('Konane', 'Hawaiian Checkers'),
   ('Koopa', 'Stun & kick pieces'),
   ('Koth', 'King of the Hill'),
   ('Losers', 'Get strong at self-mate'),
   ('Koopa', 'Stun & kick pieces'),
   ('Koth', 'King of the Hill'),
   ('Losers', 'Get strong at self-mate'),
@@ -154,4 +158,5 @@ insert or ignore into Variants (name, description) values
   ('Wildebeest', 'Balanced sliders & leapers'),
   ('Wormhole', 'Squares disappear'),
   ('Xiangqi', 'Chinese Chess'),
   ('Wildebeest', 'Balanced sliders & leapers'),
   ('Wormhole', 'Squares disappear'),
   ('Xiangqi', 'Chinese Chess'),
+  ('Yote', 'African Draughts'),
   ('Zen', 'Reverse captures');
   ('Zen', 'Reverse captures');