Some fixes, and add 2 variants: Checkless and Parachute
authorBenjamin Auder <benjamin.auder@somewhere>
Wed, 25 Mar 2020 13:52:22 +0000 (14:52 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Wed, 25 Mar 2020 13:52:22 +0000 (14:52 +0100)
35 files changed:
TODO
client/src/base_rules.js
client/src/translations/about/en.pug
client/src/translations/about/es.pug
client/src/translations/about/fr.pug
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Checkless/en.pug [new file with mode: 0644]
client/src/translations/rules/Checkless/es.pug [new file with mode: 0644]
client/src/translations/rules/Checkless/fr.pug [new file with mode: 0644]
client/src/translations/rules/Parachute/en.pug [new file with mode: 0644]
client/src/translations/rules/Parachute/es.pug [new file with mode: 0644]
client/src/translations/rules/Parachute/fr.pug [new file with mode: 0644]
client/src/variants/Atomic.js
client/src/variants/Ball.js
client/src/variants/Baroque.js
client/src/variants/Checkless.js [new file with mode: 0644]
client/src/variants/Crazyhouse.js
client/src/variants/Dynamo.js [new file with mode: 0644]
client/src/variants/Hidden.js
client/src/variants/Marseille.js
client/src/variants/Parachute.js [new file with mode: 0644]
client/src/variants/Recycle.js
client/src/variants/Rifle.js
client/src/variants/Royalrace.js
client/src/variants/Schess.js
client/src/variants/Shatranj.js
client/src/variants/Suicide.js
client/src/variants/Upsidedown.js
client/src/views/Game.vue
client/src/views/Hall.vue
server/db/populate.sql
server/models/User.js
server/sockets.js

diff --git a/TODO b/TODO
index 7d46a80..8e81cc7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,17 +1,7 @@
 # New variants
 # New variants
-Landing pieces from empty board:
-https://www.chessvariants.com/diffsetup.dir/unachess.html
-Parachute v1 & 2
-
-Generator variant, called "Matrix" ?
-Peces on first rank never move but generate new pieces. Pawn don't generate.
-A generator captured and replaced by a similar piece does not generate.
-King does not generate. No castling. En passant possible?
-Goal is still checkmate.
+Maxima, Interweave, Roccoco
 
 Take(a)n(d)make : if capture a piece, take its power for the last of the turn and make a move like it.
 
 Take(a)n(d)make : if capture a piece, take its power for the last of the turn and make a move like it.
-If a pawn taken: direction of the capturer.
+If a pawn taken: direction of the capturer, can capture enemy.
 
 
-Dynamo chess
-
-Maxima, Interweave, Roccoco
+Dynamo chess --> dur...
index e142839..37e756e 100644 (file)
@@ -224,9 +224,11 @@ export const ChessRules = class ChessRules {
     const s = move.start,
           e = move.end;
     if (
     const s = move.start,
           e = move.end;
     if (
-      Math.abs(s.x - e.x) == 2 &&
       s.y == e.y &&
       s.y == e.y &&
-      (move.appear.length > 0 && move.appear[0].p == V.PAWN)
+      Math.abs(s.x - e.x) == 2 &&
+      // Next conditions for variants like Atomic or Rifle, Recycle...
+      (move.appear.length > 0 && move.appear[0].p == V.PAWN) &&
+      (move.vanish.length > 0 && move.vanish[0].p == V.PAWN)
     ) {
       return {
         x: (s.x + e.x) / 2,
     ) {
       return {
         x: (s.x + e.x) / 2,
@@ -941,6 +943,7 @@ export const ChessRules = class ChessRules {
   }
 
   // Stop at the first move found
   }
 
   // Stop at the first move found
+  // TODO: not really, it explores all moves from a square but one would suffice.
   atLeastOneMove() {
     const color = this.turn;
     for (let i = 0; i < V.size.x; i++) {
   atLeastOneMove() {
     const color = this.turn;
     for (let i = 0; i < V.size.x; i++) {
index 6d55194..6e888b9 100644 (file)
@@ -50,4 +50,5 @@ h3 Related links
   div
     a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
     span &nbsp;(in French)
   div
     a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
     span &nbsp;(in French)
+  a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
   a(href="https://brainking.com/") brainking.com
index 66aa429..0986e01 100644 (file)
@@ -47,4 +47,5 @@ h3 Enlaces relacionados
   div
     a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
     span &nbsp;(en francés)
   div
     a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
     span &nbsp;(en francés)
+  a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
   a(href="https://brainking.com/") brainking.com
index b423603..849aad1 100644 (file)
@@ -46,4 +46,5 @@ h3 Liens connexes
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
+  a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
   a(href="https://brainking.com/") brainking.com
index 75d9b89..7ce045f 100644 (file)
@@ -177,6 +177,7 @@ export const translations = {
   "Keep antiking in check (v2)": "Keep antiking in check (v2)",
   "Kings cross the 8x8 board": "Kings cross the 8x8 board",
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
   "Keep antiking in check (v2)": "Keep antiking in check (v2)",
   "Kings cross the 8x8 board": "Kings cross the 8x8 board",
   "Kings cross the 11x11 board": "Kings cross the 11x11 board",
+  "Landing on the board": "Landing on the board",
   "Laws of attraction": "Laws of attraction",
   "Long jumps over pieces": "Long jumps over pieces",
   "Lose all pieces": "Lose all pieces",
   "Laws of attraction": "Laws of attraction",
   "Long jumps over pieces": "Long jumps over pieces",
   "Lose all pieces": "Lose all pieces",
@@ -189,6 +190,7 @@ export const translations = {
   "Move like a knight (v2)": "Move like a knight (v2)",
   "Move twice": "Move twice",
   "Neverending rows": "Neverending rows",
   "Move like a knight (v2)": "Move like a knight (v2)",
   "Move twice": "Move twice",
   "Neverending rows": "Neverending rows",
+  "No-check mode": "No-check mode",
   "Pawns move diagonally": "Pawns move diagonally",
   "Play at the same time": "Play at the same time",
   "Powerful pieces": "Powerful pieces",
   "Pawns move diagonally": "Pawns move diagonally",
   "Play at the same time": "Play at the same time",
   "Powerful pieces": "Powerful pieces",
index 465f39f..40ba5c8 100644 (file)
@@ -177,6 +177,7 @@ export const translations = {
   "Keep antiking in check (v2)": "Mantener el antirey en jaque (v2)",
   "Kings cross the 8x8 board": "Los reyes cruzan el 8x8 tablero",
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
   "Keep antiking in check (v2)": "Mantener el antirey en jaque (v2)",
   "Kings cross the 8x8 board": "Los reyes cruzan el 8x8 tablero",
   "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero",
+  "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",
   "Lose all pieces": "Perder todas las piezas",
   "Laws of attraction": "Las leyes de las atracciones",
   "Long jumps over pieces": "Saltos largos sobre las piezas",
   "Lose all pieces": "Perder todas las piezas",
@@ -189,6 +190,7 @@ export const translations = {
   "Move like a knight (v2)": "Moverse como un caballo (v2)",
   "Move twice": "Mover dos veces",
   "Neverending rows": "Filas interminables",
   "Move like a knight (v2)": "Moverse como un caballo (v2)",
   "Move twice": "Mover dos veces",
   "Neverending rows": "Filas interminables",
+  "No-check mode": "Modo sin jaque",
   "Pawns move diagonally": "Peones se mueven en diagonal",
   "Play at the same time": "Jugar al mismo tiempo",
   "Powerful pieces": "Piezas poderosas",
   "Pawns move diagonally": "Peones se mueven en diagonal",
   "Play at the same time": "Jugar al mismo tiempo",
   "Powerful pieces": "Piezas poderosas",
index 62dd3ba..8954f7b 100644 (file)
@@ -177,6 +177,7 @@ export const translations = {
   "Keep antiking in check (v2)": "Gardez l'antiroi en échec (v2)",
   "Kings cross the 8x8 board": "Les rois traversent l'échiquier 8x8",
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
   "Keep antiking in check (v2)": "Gardez l'antiroi en échec (v2)",
   "Kings cross the 8x8 board": "Les rois traversent l'échiquier 8x8",
   "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11",
+  "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",
   "Lose all pieces": "Perdez toutes les pièces",
   "Laws of attraction": "Les lois de l'attraction",
   "Long jumps over pieces": "Sauts longs par dessus les pièces",
   "Lose all pieces": "Perdez toutes les pièces",
@@ -189,6 +190,7 @@ export const translations = {
   "Move like a knight (v2)": "Bouger comme un cavalier (v2)",
   "Move twice": "Jouer deux coups",
   "Neverending rows": "Rangées sans fin",
   "Move like a knight (v2)": "Bouger comme un cavalier (v2)",
   "Move twice": "Jouer deux coups",
   "Neverending rows": "Rangées sans fin",
+  "No-check mode": "Mode sans échec",
   "Pawns move diagonally": "Les pions vont en diagonale",
   "Play at the same time": "Jouer en même temps",
   "Powerful pieces": "Pièces puissantes",
   "Pawns move diagonally": "Les pions vont en diagonale",
   "Play at the same time": "Jouer en même temps",
   "Powerful pieces": "Pièces puissantes",
diff --git a/client/src/translations/rules/Checkless/en.pug b/client/src/translations/rules/Checkless/en.pug
new file mode 100644 (file)
index 0000000..ce3ca48
--- /dev/null
@@ -0,0 +1,42 @@
+p.boxed
+  | Giving check is forbidden, unless it is a checkmate.
+
+p.
+  Neither player is allowed to give a check, with the exception of checkmate.
+  Thus, the king is much more powerful than in orthodox chess: as long as
+  he can (potentially) escape, he doesn't fear attacks.
+  So the king can be used to defend pieces in an unusual way.
+
+p.
+  On the following diagram, 1.Qxa6 threatens 2.Bb6
+  with a mate to follow by Qxa7.
+  The black rook cannot take because it would check the white king.
+
+figure.diagram-container
+  .diagram
+    | fen:1k1r1b2/rP1b2p1/pQ1pp1nq/K4p1p/P4PnP/2PN2P1/3PP1B1/R1RN2B1:
+  figcaption 1.Qxd8+ is forbidden because 1...Bc8 would be possible.
+
+h3 Disambiguation
+
+p.
+  1.Qf7# is checkmate on the left diagram, because if the king takes
+  then the rook on h8 gives check but not checkmate.
+  However, on the right diagram 1.Qf7+ runs into 1...Kxf7#, which is now
+  a legal move because the white king is checkmated.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:K5kr/8/5Q2/8/8/8/8/8:
+  .diagram.diag22
+    | fen:K5kr/RB6/5Q2/8/8/8/8/7b:
+  figcaption 1.Qf7 mates on the left, but not on the right.
+
+h3 Source
+
+p
+  a(href="https://www.chessvariants.com/usualeq.dir/checklss.html")
+    | Checkless chess
+  | &nbsp;on chessvariants.com, and the 
+  a(href="https://en.wikipedia.org/wiki/Checkless_chess") Wikipedia page
+  | .
diff --git a/client/src/translations/rules/Checkless/es.pug b/client/src/translations/rules/Checkless/es.pug
new file mode 100644 (file)
index 0000000..1f40860
--- /dev/null
@@ -0,0 +1,43 @@
+p.boxed
+  | Se prohíbe el jaque, a menos que sea un jaque mate.
+
+p.
+  Ningún jugador tiene derecho a dar jaque, excepto el jaque mate.
+  Por lo tanto, el rey es claramente más poderoso que en el ajedrez ortodoxo:
+  mientras pueda (potencialmente) escapar, no tiene miedo a los ataques.
+  El rey puede ser usado para defender las piezas de una manera inusual.
+
+p.
+  En el siguiente diagrama, 1.Qxa6 amenaza a 2.Bb6 con un jaque mate
+  seguido de Qxa7.
+  La torre negra no puede tomar porque daría jaque al rey blanco.
+
+figure.diagram-container
+  .diagram
+    | fen:1k1r1b2/rP1b2p1/pQ1pp1nq/K4p1p/P4PnP/2PN2P1/3PP1B1/R1RN2B1:
+  figcaption 1.Qxd8+ está prohibido porque 1...Bc8 sería posible.
+
+h3 Desambiguación
+
+p.
+  1.Qf7# mat en el diagrama de la izquierda, porque si el rey toma
+  la torre h8 da jaque pero no jaque mate.
+  Sin embargo, en el diagrama de la derecha 1.Qf7+ permite 1...Kxf7#,
+  que ahora es legal porque el rey blanco es en jaque.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:K5kr/8/5Q2/8/8/8/8/8:
+  .diagram.diag22
+    | fen:K5kr/RB6/5Q2/8/8/8/8/7b:
+  figcaption 1.Qf7 mate a la izquierda, pero no a la derecha.
+
+h3 Fuente
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/usualeq.dir/checklss.html")
+    | variante Checkless
+  | &nbsp;en chessvariants.com, y la 
+  a(href="https://en.wikipedia.org/wiki/Checkless_chess") página wikipedia
+  | .
diff --git a/client/src/translations/rules/Checkless/fr.pug b/client/src/translations/rules/Checkless/fr.pug
new file mode 100644 (file)
index 0000000..d42c937
--- /dev/null
@@ -0,0 +1,43 @@
+p.boxed
+  | Donner échec est interdit, sauf si c'est un échec et mat.
+
+p.
+  Aucun joueur n'a le droit de donner échec, à l'exception du mat.
+  Ainsi, le roi est nettement plus puissant qu'aux échecs orthodoxes :
+  tant qu'il peut (potentiellement) s'échapper, il ne craint pas les attaques.
+  Le roi peut alors être utilisé pour défendre les pièces d'une manière
+  inhabituelle.
+
+  p.
+  Sur le diagramme suivant, 1.Qxa6 menaçe 2.Bb6 avec un mat à suivre par Qxa7.
+  La tour noire ne peut pas prendre car elle mettrait le roi blanc en échec.
+
+figure.diagram-container
+  .diagram
+    | fen:1k1r1b2/rP1b2p1/pQ1pp1nq/K4p1p/P4PnP/2PN2P1/3PP1B1/R1RN2B1:
+  figcaption 1.Qxd8+ est interdit car 1...Bc8 serait possible.
+
+h3 Désambiguïsation
+
+p.
+  1.Qf7# fait mat sur le diagramme de gauche, car si le roi prend alors
+  la tour h8 donne échec mais pas mat.
+  Cependant, sur le diagramme de droite 1.Qf7+ permet 1...Kxf7#,
+  qui est maintenant légal car le roi blanc est mat.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:K5kr/8/5Q2/8/8/8/8/8:
+  .diagram.diag22
+    | fen:K5kr/RB6/5Q2/8/8/8/8/7b:
+  figcaption 1.Qf7 mate à gauche, mais pas à droite.
+
+h3 Source
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/usualeq.dir/checklss.html")
+    | variante Checkless
+  | &nbsp;sur chessvariants.com, et la 
+  a(href="https://en.wikipedia.org/wiki/Checkless_chess") page Wikipedia
+  | .
diff --git a/client/src/translations/rules/Parachute/en.pug b/client/src/translations/rules/Parachute/en.pug
new file mode 100644 (file)
index 0000000..61ef945
--- /dev/null
@@ -0,0 +1,35 @@
+p.boxed
+  | The board is initially empty.
+  | Add a piece (not giving check) or move one at each turn.
+
+p.
+  The king can be added at any moment, but while he hasn't landed
+  no capture can be done. So you could move or land your king "into check"
+  if your opponent didn't land his king yet.
+
+p.
+  Giving check with a landed piece is forbidden
+  (assuming both kings are on the board).
+
+p.
+  Pawns can be landed on the four first ranks only. A pawn on the first
+  rank can jump two squares, and be captured en passant.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:8/8/3b2r1/3R4/k3Q3/3R4/8/8:
+  .diagram.diag22
+    | fen:8/8/K2b2r1/3R4/k1N1Q3/3R4/5q2/8:
+  figcaption.
+    Left: no white king, so the black king is safe.
+    Right: black to move, there is no way to avoid mate.
+
+h3 Source
+
+p
+  a(href="https://www.chessvariants.com/diffsetup.dir/unachess.html")
+    | Unachess II
+  | &nbsp;on chessvariants.com. Unachess I gives a too large advantage
+  | to white, in the few games I could play.
+
+p Inventors: Jeff Miller and Edward Jackman (1995)
diff --git a/client/src/translations/rules/Parachute/es.pug b/client/src/translations/rules/Parachute/es.pug
new file mode 100644 (file)
index 0000000..fe04d4f
--- /dev/null
@@ -0,0 +1,37 @@
+p.boxed
+  | El tablero está inicialmente vacío.
+  | Agregue una pieza (sin dar jaque) o mueva una cada turno.
+
+p.
+  Se puede agregar el rey en cualquier momento, pero hasta que haya aterrizado
+  las capturas están prohibidas. Para que puedas moverte o dejarte caer
+  su rey "en jaque" si el oponente aún no ha colocado el suyo.
+
+p.
+  No puedes dar jaque con una pieza en paracaídas
+  (asumiendo que los dos reyes están en el tablero).
+
+p.
+  Los peones se pueden soltar solo en las primeras cuatro filas.
+  Un peón en la primera fila puede saltar dos espacios,
+  y quedar atrapado en passant.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:8/8/3b2r1/3R4/k3Q3/3R4/8/8:
+  .diagram.diag22
+    | fen:8/8/K2b2r1/3R4/k1N1Q3/3R4/5q2/8:
+  figcaption.
+    Izquierda: no hay rey blanco, por lo que el rey negro está en seguridad.
+    Derecha: juegan las negras, no puede evitar el jaque mate.
+
+h3 Fuente
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/diffsetup.dir/unachess.html")
+    | variante Unachess II
+  | &nbsp;en chessvariants.com. Unachess I da demasiada ventaja
+  | a las blancas, en los pocos juegos que he podido jugar.
+
+p Inventores: Jeff Miller y Edward Jackman (1995)
diff --git a/client/src/translations/rules/Parachute/fr.pug b/client/src/translations/rules/Parachute/fr.pug
new file mode 100644 (file)
index 0000000..c0a2a9f
--- /dev/null
@@ -0,0 +1,37 @@
+p.boxed
+  | L'échiquier est initialement vide.
+  | Ajoutez une pièce (sans donner échec) ou déplacez-en une à chaque tour.
+
+p.
+  Le roi peut être ajouté à tout moment, mais tant qu'il n'a pas atterri
+  les captures sont interdites. Ainsi vous pourriez déplacer ou parachuter
+  votre roi "en échec" si l'adversaire n'a pas encore posé le sien.
+
+p.
+  On ne peut pas donner échec avec une pièce parachutée
+  (supposant que les deux rois sont sur l'échiquier).
+
+p.
+  Les pions peuvent être parachutés sur les quatre premières rangées seulement.
+  Un pion sur la première rangée peut sauter de deux cases,
+  et être capturé en passant.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:8/8/3b2r1/3R4/k3Q3/3R4/8/8:
+  .diagram.diag22
+    | fen:8/8/K2b2r1/3R4/k1N1Q3/3R4/5q2/8:
+  figcaption.
+    Gauche : pas de roi blanc, donc le roi noir est en sécurité.
+    Droite : trait aux noirs, on ne peut pas éviter le mat.
+
+h3 Source
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/diffsetup.dir/unachess.html")
+    | variante Unachess II
+  | &nbsp;sur chessvariants.com. Unachess I donne un trop grand avantage
+  aux blancs, dans les quelques parties que j'ai pu jouer.
+
+p Inventeurs : Jeff Miller et Edward Jackman (1995)
index 889f2df..90089e8 100644 (file)
@@ -1,13 +1,6 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
 export class AtomicRules extends ChessRules {
 import { ChessRules, PiPo } from "@/base_rules";
 
 export class AtomicRules extends ChessRules {
-  getEpSquare(moveOrSquare) {
-    if (typeof moveOrSquare !== "object" || moveOrSquare.appear.length > 0)
-      return super.getEpSquare(moveOrSquare);
-    // Capturing move: no en-passant
-    return undefined;
-  }
-
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
 
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
 
index cd504a9..85e33e6 100644 (file)
@@ -14,9 +14,6 @@ export class BallRules extends ChessRules {
   static get HasFlags() {
     return false;
   }
   static get HasFlags() {
     return false;
   }
-  static get HasCastle() {
-    return false;
-  }
 
   static get CHAMPION() {
     return 'c';
 
   static get CHAMPION() {
     return 'c';
index 92bae77..031ea72 100644 (file)
@@ -7,10 +7,6 @@ export class BaroqueRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasCastle() {
-    return false;
-  }
-
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
diff --git a/client/src/variants/Checkless.js b/client/src/variants/Checkless.js
new file mode 100644 (file)
index 0000000..8861214
--- /dev/null
@@ -0,0 +1,44 @@
+import { ChessRules } from "@/base_rules";
+
+export class ChecklessRules extends ChessRules {
+  // Cannot use super.atLeastOneMove: lead to infinite recursion
+  atLeastOneMove_aux() {
+    const color = this.turn;
+    const oppCol = V.GetOppCol(color);
+    for (let i = 0; i < V.size.x; i++) {
+      for (let j = 0; j < V.size.y; j++) {
+        if (this.getColor(i, j) == color) {
+          const moves = this.getPotentialMovesFrom([i, j]);
+          if (moves.length > 0) {
+            for (let k = 0; k < moves.length; k++) {
+              let res = false;
+              this.play(moves[k]);
+              res = !this.underCheck(color) && !this.underCheck(oppCol);
+              this.undo(moves[k]);
+              if (res) return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  filterValid(moves) {
+    if (moves.length == 0) return [];
+    const color = this.turn;
+    const oppCol = V.GetOppCol(color);
+    return moves.filter(m => {
+      this.play(m);
+      // Move shouldn't result in self-check:
+      let res = !this.underCheck(color);
+      if (res) {
+        const checkOpp = this.underCheck(oppCol);
+        // If checking the opponent, he shouldn't have any legal move:
+        res = !checkOpp || checkOpp && !this.atLeastOneMove_aux();
+      }
+      this.undo(m);
+      return res;
+    });
+  }
+};
index 2307eaf..576f7d2 100644 (file)
@@ -31,13 +31,6 @@ export class CrazyhouseRules extends ChessRules {
     );
   }
 
     );
   }
 
-  getEpSquare(moveOrSquare) {
-    if (typeof moveOrSquare !== "object" || moveOrSquare.vanish.length > 0)
-      return super.getEpSquare(moveOrSquare);
-    // Landing move: no en-passant
-    return undefined;
-  }
-
   static GenRandInitFen(randomness) {
     return ChessRules.GenRandInitFen(randomness) + " 0000000000 -";
   }
   static GenRandInitFen(randomness) {
     return ChessRules.GenRandInitFen(randomness) + " 0000000000 -";
   }
diff --git a/client/src/variants/Dynamo.js b/client/src/variants/Dynamo.js
new file mode 100644 (file)
index 0000000..c76c040
--- /dev/null
@@ -0,0 +1,5 @@
+import { ChessRules } from "@/base_rules";
+
+export class DynamoRules extends ChessRules {
+  // TODO
+};
index ef5b0bb..2fcab24 100644 (file)
@@ -7,10 +7,6 @@ export class HiddenRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasCastle() {
-    return false;
-  }
-
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
index ed9965b..940d9ba 100644 (file)
@@ -124,7 +124,7 @@ export class MarseilleRules extends ChessRules {
     if (piece == V.KING && move.appear.length > 0) {
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
     if (piece == V.KING && move.appear.length > 0) {
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
-      if (V.HasCastle) this.castleFlags[c] = [V.size.y, V.size.y];
+      this.castleFlags[c] = [V.size.y, V.size.y];
       return;
     }
     const oppCol = V.GetOppCol(c);
       return;
     }
     const oppCol = V.GetOppCol(c);
diff --git a/client/src/variants/Parachute.js b/client/src/variants/Parachute.js
new file mode 100644 (file)
index 0000000..0718635
--- /dev/null
@@ -0,0 +1,210 @@
+import { ChessRules, PiPo, Move } from "@/base_rules";
+
+export class ParachuteRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
+
+  static IsGoodFen(fen) {
+    if (!ChessRules.IsGoodFen(fen)) return false;
+    const fenParsed = V.ParseFen(fen);
+    // 5) Check reserves
+    if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{12,12}$/))
+      return false;
+    return true;
+  }
+
+  static ParseFen(fen) {
+    const fenParts = fen.split(" ");
+    return Object.assign(
+      ChessRules.ParseFen(fen),
+      { reserve: fenParts[4] }
+    );
+  }
+
+  static GenRandInitFen() {
+    // ChessRules.PIECES order is P, R, N, B, Q, K:
+    return "8/8/8/8/8/8/8/8 w 0 - 822211822211";
+  }
+
+  getFen() {
+    return super.getFen() + " " + this.getReserveFen();
+  }
+
+  getFenForRepeat() {
+    return super.getFenForRepeat() + "_" + this.getReserveFen();
+  }
+
+  getReserveFen() {
+    let counts = new Array(12);
+    for (let i = 0; i < V.PIECES.length; i++) {
+      counts[i] = this.reserve["w"][V.PIECES[i]];
+      counts[6 + i] = this.reserve["b"][V.PIECES[i]];
+    }
+    return counts.join("");
+  }
+
+  setOtherVariables(fen) {
+    super.setOtherVariables(fen);
+    const fenParsed = V.ParseFen(fen);
+    // Also init reserves (used by the interface to show landable pieces)
+    this.reserve = {
+      w: {
+        [V.PAWN]: parseInt(fenParsed.reserve[0]),
+        [V.ROOK]: parseInt(fenParsed.reserve[1]),
+        [V.KNIGHT]: parseInt(fenParsed.reserve[2]),
+        [V.BISHOP]: parseInt(fenParsed.reserve[3]),
+        [V.QUEEN]: parseInt(fenParsed.reserve[4]),
+        [V.KING]: parseInt(fenParsed.reserve[5])
+      },
+      b: {
+        [V.PAWN]: parseInt(fenParsed.reserve[6]),
+        [V.ROOK]: parseInt(fenParsed.reserve[7]),
+        [V.KNIGHT]: parseInt(fenParsed.reserve[8]),
+        [V.BISHOP]: parseInt(fenParsed.reserve[9]),
+        [V.QUEEN]: parseInt(fenParsed.reserve[10]),
+        [V.KING]: parseInt(fenParsed.reserve[11])
+      }
+    };
+  }
+
+  getColor(i, j) {
+    if (i >= V.size.x) return i == V.size.x ? "w" : "b";
+    return this.board[i][j].charAt(0);
+  }
+
+  getPiece(i, j) {
+    if (i >= V.size.x) return V.RESERVE_PIECES[j];
+    return this.board[i][j].charAt(1);
+  }
+
+  // Used by the interface:
+  getReservePpath(index, color) {
+    return color + V.RESERVE_PIECES[index];
+  }
+
+  // Ordering on reserve pieces (matching V.PIECES order)
+  static get RESERVE_PIECES() {
+    return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
+  }
+
+  getReserveMoves([x, y]) {
+    const color = this.turn;
+    const oppCol = V.GetOppCol(color);
+    const p = V.RESERVE_PIECES[y];
+    if (this.reserve[color][p] == 0) return [];
+    let moves = [];
+    let boundary =
+      p == V.PAWN
+        // Pawns can land only on 4 first ranks:
+        ? (color == 'w' ? [4, 8] : [0, 4])
+        : [0, 8];
+    for (let i = boundary[0]; i < boundary[1]; i++) {
+      for (let j = 0; j < 8; j++) {
+        if (this.board[i][j] == V.EMPTY) {
+          let mv = new Move({
+            appear: [
+              new PiPo({
+                x: i,
+                y: j,
+                c: color,
+                p: p
+              })
+            ],
+            vanish: [],
+            start: { x: x, y: y }, //a bit artificial...
+            end: { x: i, y: j }
+          });
+          this.play(mv);
+          // Landing with check is forbidden:
+          if (!this.underCheck(oppCol)) moves.push(mv);
+          this.undo(mv);
+        }
+      }
+    }
+    return moves;
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    let moves =
+      x >= 8
+        ? this.getReserveMoves([x, y])
+        : super.getPotentialMovesFrom([x, y]);
+    // Forbid captures if king not landed yet:
+    if (x < 8 && moves.length > 0 && this.kingPos[moves[0].appear[0].c][0] < 0)
+      moves = moves.filter(m => m.vanish.length == 1);
+    return moves;
+  }
+
+  getAllValidMoves() {
+    let moves = super.getAllValidMoves();
+    const color = this.turn;
+    for (let i = 0; i < V.RESERVE_PIECES.length; i++)
+      moves = moves.concat(
+        this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
+      );
+    return this.filterValid(moves);
+  }
+
+  isAttacked(sq, color) {
+    // While the king hasn't landed, nothing is attacked:
+    if (this.kingPos[color][0] < 0) return false;
+    return super.isAttacked(sq, color);
+  }
+
+  atLeastOneMove() {
+    if (!super.atLeastOneMove()) {
+      // Search one reserve move
+      for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+        let moves = this.filterValid(
+          this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
+        );
+        if (moves.length > 0) return true;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  prePlay(move) {
+    super.prePlay(move);
+    if (move.vanish.length == 0) this.reserve[this.turn][move.appear[0].p]--;
+  }
+
+  postUndo(move) {
+    if (move.vanish.length == 0) this.reserve[this.turn][move.appear[0].p]++;
+    // (Potentially) Reset king position
+    if (move.appear[0].p == V.KING) {
+      const c = move.appear[0].c;
+      if (move.vanish.length == 0)
+        // Landing king
+        this.kingPos[c] = [-1, -1];
+      else
+        // King movement
+        this.kingPos[c] = [move.start.x, move.start.y];
+    }
+  }
+
+  static get SEARCH_DEPTH() {
+    return 1;
+  }
+
+  evalPosition() {
+    let evaluation = super.evalPosition();
+    // Add reserves:
+    for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+      const p = V.RESERVE_PIECES[i];
+      evaluation += this.reserve["w"][p] * V.VALUES[p];
+      evaluation -= this.reserve["b"][p] * V.VALUES[p];
+    }
+    return evaluation;
+  }
+
+  getNotation(move) {
+    if (move.vanish.length > 0) return super.getNotation(move);
+    // Parachutage:
+    const piece =
+      move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
+    return piece + "@" + V.CoordsToSquare(move.end);
+  }
+};
index 8b070fa..adaceb0 100644 (file)
@@ -27,13 +27,6 @@ export class RecycleRules extends ChessRules {
     );
   }
 
     );
   }
 
-  getEpSquare(moveOrSquare) {
-    if (typeof moveOrSquare !== "object" || moveOrSquare.vanish.length > 0)
-      return super.getEpSquare(moveOrSquare);
-    // Landing move: no en-passant
-    return undefined;
-  }
-
   static GenRandInitFen(randomness) {
     return ChessRules.GenRandInitFen(randomness) + " 0000000000";
   }
   static GenRandInitFen(randomness) {
     return ChessRules.GenRandInitFen(randomness) + " 0000000000";
   }
index 98661ae..94a6d05 100644 (file)
@@ -1,13 +1,6 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class RifleRules extends ChessRules {
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class RifleRules extends ChessRules {
-  getEpSquare(moveOrSquare) {
-    if (typeof moveOrSquare !== "object" || moveOrSquare.appear.length > 0)
-      return super.getEpSquare(moveOrSquare);
-    // Capturing move: no en-passant
-    return undefined;
-  }
-
   getBasicMove([sx, sy], [ex, ey], tr) {
     let mv = new Move({
       appear: [],
   getBasicMove([sx, sy], [ex, ey], tr) {
     let mv = new Move({
       appear: [],
index 39ee02a..21a62c8 100644 (file)
@@ -7,10 +7,6 @@ export class RoyalraceRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasCastle() {
-    return false;
-  }
-
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
index ba5730e..07556a5 100644 (file)
@@ -50,7 +50,7 @@ export class SchessRules extends ChessRules {
   setFlags(fenflags) {
     super.setFlags(fenflags); //castleFlags
     this.pieceFlags = {
   setFlags(fenflags) {
     super.setFlags(fenflags); //castleFlags
     this.pieceFlags = {
-      w: [...Array(8)], //pawns can move 2 squares?
+      w: [...Array(8)], //pieces can generate Hawk or Elephant?
       b: [...Array(8)]
     };
     const flags = fenflags.substr(4); //skip first 4 letters, for castle
       b: [...Array(8)]
     };
     const flags = fenflags.substr(4); //skip first 4 letters, for castle
@@ -293,7 +293,7 @@ export class SchessRules extends ChessRules {
     }
     this.updateCastleFlags(move, piece);
 
     }
     this.updateCastleFlags(move, piece);
 
-    const oppCol = V.GetOppCol(color);
+    const oppCol = this.turn;
     const firstRank = (color == 'w' ? 7 : 0);
     const oppFirstRank = 7 - firstRank;
     // Does this move turn off a piece init square flag?
     const firstRank = (color == 'w' ? 7 : 0);
     const oppFirstRank = 7 - firstRank;
     // Does this move turn off a piece init square flag?
index 00b1456..105331f 100644 (file)
@@ -5,10 +5,6 @@ export class ShatranjRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasCastle() {
-    return false;
-  }
-
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
index c81d355..2693f84 100644 (file)
@@ -7,10 +7,6 @@ export class SuicideRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasCastle() {
-    return false;
-  }
-
   static get PawnSpecs() {
     return Object.assign(
       {},
   static get PawnSpecs() {
     return Object.assign(
       {},
index 2076824..abffd8f 100644 (file)
@@ -7,10 +7,6 @@ export class UpsidedownRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static get HasCastle() {
-    return false;
-  }
-
   static get HasEnpassant() {
     return false;
   }
   static get HasEnpassant() {
     return false;
   }
@@ -73,8 +69,9 @@ export class UpsidedownRules extends ChessRules {
       pieces["w"].join("").toUpperCase() +
       "/PPPPPPPP/8/8/8/8/pppppppp/" +
       pieces["b"].join("") +
       pieces["w"].join("").toUpperCase() +
       "/PPPPPPPP/8/8/8/8/pppppppp/" +
       pieces["b"].join("") +
+      // No castle, no en-passant:
       " w 0"
       " w 0"
-    ); //no castle, no en-passant
+    );
   }
 
   static get SEARCH_DEPTH() {
   }
 
   static get SEARCH_DEPTH() {
index 3171855..e5b45ec 100644 (file)
@@ -474,10 +474,12 @@ export default {
           // TODO: shuffling and random filtering on server,
           // if the room is really crowded.
           Object.keys(data.sockIds).forEach(sid => {
           // TODO: shuffling and random filtering on server,
           // if the room is really crowded.
           Object.keys(data.sockIds).forEach(sid => {
-            // TODO: test sid != user.sid was already done on server
             if (sid != this.st.user.sid) {
             if (sid != this.st.user.sid) {
-              this.people[sid] = { tmpIds: data.sockIds[sid] };
               this.send("askidentity", { target: sid });
               this.send("askidentity", { target: sid });
+              this.people[sid] = { tmpIds: data.sockIds[sid] };
+            } else {
+              // Complete my tmpIds:
+              Object.assign(this.people[sid].tmpIds, data.sockIds[sid]);
             }
           });
           break;
             }
           });
           break;
index 132c31b..5c5dc33 100644 (file)
@@ -588,7 +588,6 @@ export default {
           // TODO: shuffling and random filtering on server,
           // if the room is really crowded.
           Object.keys(data.sockIds).forEach(sid => {
           // TODO: shuffling and random filtering on server,
           // if the room is really crowded.
           Object.keys(data.sockIds).forEach(sid => {
-            // TODO: test sid != user.sid was already done on server
             if (sid != this.st.user.sid) {
               // Pick a target tmpId (+page) at random:
               const pt = Object.values(data.sockIds[sid]);
             if (sid != this.st.user.sid) {
               // Pick a target tmpId (+page) at random:
               const pt = Object.values(data.sockIds[sid]);
index 28d186c..1895777 100644 (file)
@@ -16,6 +16,7 @@ insert or ignore into Variants (name,description) values
   ('Cannibal', 'Capture powers'),
   ('Capture', 'Mandatory captures'),
   ('Checkered', 'Shared pieces'),
   ('Cannibal', 'Capture powers'),
   ('Capture', 'Mandatory captures'),
   ('Checkered', 'Shared pieces'),
+  ('Checkless', 'No-check mode'),
   ('Chess960', 'Standard rules'),
   ('Circular', 'Run forward'),
   ('Coregal', 'Two royal pieces'),
   ('Chess960', 'Standard rules'),
   ('Circular', 'Run forward'),
   ('Coregal', 'Two royal pieces'),
@@ -36,6 +37,7 @@ insert or ignore into Variants (name,description) values
   ('Losers', 'Get strong at self-mate'),
   ('Magnetic', 'Laws of attraction'),
   ('Marseille', 'Move twice'),
   ('Losers', 'Get strong at self-mate'),
   ('Magnetic', 'Laws of attraction'),
   ('Marseille', 'Move twice'),
+  ('Parachute', 'Landing on the board'),
   ('Perfect', 'Powerful pieces'),
   ('Racingkings', 'Kings cross the 8x8 board'),
   ('Rifle', 'Shoot pieces'),
   ('Perfect', 'Powerful pieces'),
   ('Racingkings', 'Kings cross the 8x8 board'),
   ('Rifle', 'Shoot pieces'),
index e3e5408..ab826d9 100644 (file)
@@ -149,6 +149,7 @@ const UserModel = {
           // Remove users unlogged for > 24h
           if (!u.sessionToken && tsNow - u.created > day)
           {
           // Remove users unlogged for > 24h
           if (!u.sessionToken && tsNow - u.created > day)
           {
+            toRemove.push(u.id);
             UserModel.notify(
               u,
               "Your account has been deleted because " +
             UserModel.notify(
               u,
               "Your account has been deleted because " +
index 65e4163..25abadb 100644 (file)
@@ -124,13 +124,12 @@ module.exports = function(wss) {
           // From Game
           let sockIds = {};
           Object.keys(clients[page]).forEach(k => {
           // From Game
           let sockIds = {};
           Object.keys(clients[page]).forEach(k => {
-            // Avoid polling myself: no new information to get
-            if (k != sid) {
-              sockIds[k] = {};
-              Object.keys(clients[page][k]).forEach(x => {
+            sockIds[k] = {};
+            Object.keys(clients[page][k]).forEach(x => {
+              // Avoid polling my tmpId: no information to get
+              if (k != sid || x != tmpId)
                 sockIds[k][x] = { focus: clients[page][k][x].focus };
                 sockIds[k][x] = { focus: clients[page][k][x].focus };
-              });
-            }
+            });
           });
           send(socket, { code: "pollclients", sockIds: sockIds });
           break;
           });
           send(socket, { code: "pollclients", sockIds: sockIds });
           break;
@@ -139,30 +138,30 @@ module.exports = function(wss) {
           // From Hall
           let sockIds = {};
           Object.keys(clients["/"]).forEach(k => {
           // From Hall
           let sockIds = {};
           Object.keys(clients["/"]).forEach(k => {
-            // Avoid polling myself: no new information to get
-            if (k != sid) {
-              sockIds[k] = {};
-              Object.keys(clients[page][k]).forEach(x => {
+            sockIds[k] = {};
+            Object.keys(clients[page][k]).forEach(x => {
+              // Avoid polling my tmpId: no information to get
+              if (k != sid || x != tmpId) {
                 sockIds[k][x] = {
                   page: "/",
                   focus: clients[page][k][x].focus
                 };
                 sockIds[k][x] = {
                   page: "/",
                   focus: clients[page][k][x].focus
                 };
-              });
-            }
+              }
+            });
           });
           // NOTE: a "gamer" could also just be an observer
           Object.keys(clients).forEach(p => {
             if (p.indexOf("/game/") >= 0) {
               Object.keys(clients[p]).forEach(k => {
           });
           // NOTE: a "gamer" could also just be an observer
           Object.keys(clients).forEach(p => {
             if (p.indexOf("/game/") >= 0) {
               Object.keys(clients[p]).forEach(k => {
-                if (k != sid) {
-                  if (!sockIds[k]) sockIds[k] = {};
-                  Object.keys(clients[p][k]).forEach(x => {
+                if (!sockIds[k]) sockIds[k] = {};
+                Object.keys(clients[p][k]).forEach(x => {
+                  if (k != sid || x != tmpId) {
                     sockIds[k][x] = {
                       page: p,
                       focus: clients[p][k][x].focus
                     };
                     sockIds[k][x] = {
                       page: p,
                       focus: clients[p][k][x].focus
                     };
-                  });
-                }
+                  }
+                });
               });
             }
           });
               });
             }
           });