Fix SuctionChess, draft HiddenRules (unfinished)
authorBenjamin Auder <benjamin.auder@somewhere>
Mon, 24 Feb 2020 00:24:14 +0000 (01:24 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Mon, 24 Feb 2020 00:24:14 +0000 (01:24 +0100)
24 files changed:
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/Hidden/en.pug [new file with mode: 0644]
client/src/translations/rules/Hidden/es.pug [new file with mode: 0644]
client/src/translations/rules/Hidden/fr.pug [new file with mode: 0644]
client/src/translations/rules/Suction/en.pug
client/src/translations/rules/Suction/es.pug
client/src/translations/rules/Suction/fr.pug
client/src/utils/printDiagram.js
client/src/variants/Alice.js
client/src/variants/Antiking.js
client/src/variants/Baroque.js
client/src/variants/Checkered.js
client/src/variants/Crazyhouse.js
client/src/variants/Dark.js
client/src/variants/Grand.js
client/src/variants/Hidden.js [new file with mode: 0644]
client/src/variants/Recycle.js
client/src/variants/Suction.js
client/src/variants/Wildebeest.js
server/db/populate.sql

index 7e4057b..7949d7f 100644 (file)
@@ -53,11 +53,6 @@ export const ChessRules = class ChessRules {
     return "all";
   }
 
     return "all";
   }
 
-  // Path to pieces
-  static getPpath(b) {
-    return b; //usual pieces in pieces/ folder
-  }
-
   // Turn "wb" into "B" (for FEN)
   static board2fen(b) {
     return b[0] == "w" ? b[1].toUpperCase() : b[1];
   // Turn "wb" into "B" (for FEN)
   static board2fen(b) {
     return b[0] == "w" ? b[1].toUpperCase() : b[1];
@@ -160,6 +155,11 @@ export const ChessRules = class ChessRules {
     return V.CoordToColumn(coords.y) + (V.size.x - coords.x);
   }
 
     return V.CoordToColumn(coords.y) + (V.size.x - coords.x);
   }
 
+  // Path to pieces
+  getPpath(b) {
+    return b; //usual pieces in pieces/ folder
+  }
+
   // Aggregates flags into one object
   aggregateFlags() {
     return this.castleFlags;
   // Aggregates flags into one object
   aggregateFlags() {
     return this.castleFlags;
@@ -379,7 +379,9 @@ export const ChessRules = class ChessRules {
   // INITIALIZATION
 
   constructor(fen) {
   // INITIALIZATION
 
   constructor(fen) {
-    this.re_init(fen);
+    // In printDiagram() fen isn't supply because only getPpath() is used
+    if (fen)
+      this.re_init(fen);
   }
 
   // Fen string fully describes the game state
   }
 
   // Fen string fully describes the game state
index 6268002..d442346 100644 (file)
@@ -88,7 +88,7 @@ export default {
                   attrs: {
                     src:
                       "/images/pieces/" +
                   attrs: {
                     src:
                       "/images/pieces/" +
-                      V.getPpath(this.vr.board[ci][cj]) +
+                      this.vr.getPpath(this.vr.board[ci][cj], this.userColor, this.score) +
                       ".svg"
                   }
                 })
                       ".svg"
                   }
                 })
@@ -154,7 +154,7 @@ export default {
                 attrs: {
                   src:
                     "/images/pieces/" +
                 attrs: {
                   src:
                     "/images/pieces/" +
-                    this.vr.getReservePpath(playingColor, i) +
+                    this.vr.getReservePpath(i, playingColor) +
                     ".svg"
                 }
               }),
                     ".svg"
                 }
               }),
@@ -181,7 +181,7 @@ export default {
                 attrs: {
                   src:
                     "/images/pieces/" +
                 attrs: {
                   src:
                     "/images/pieces/" +
-                    this.vr.getReservePpath(oppCol, i) +
+                    this.vr.getReservePpath(i, oppCol) +
                     ".svg"
                 }
               }),
                     ".svg"
                 }
               }),
@@ -255,7 +255,7 @@ export default {
                 attrs: {
                   src:
                     "/images/pieces/" +
                 attrs: {
                   src:
                     "/images/pieces/" +
-                    V.getPpath(m.appear[0].c + m.appear[0].p) +
+                    this.vr.getPpath(m.appear[0].c + m.appear[0].p) +
                     ".svg"
                 },
                 class: { "choice-piece": true },
                     ".svg"
                 },
                 class: { "choice-piece": true },
index d1759eb..920e19c 100644 (file)
@@ -148,5 +148,6 @@ export const translations = {
   "Reuse pieces": "Reuse pieces",
   "Reverse captures": "Reverse captures",
   "Shared pieces": "Shared pieces",
   "Reuse pieces": "Reuse pieces",
   "Reverse captures": "Reverse captures",
   "Shared pieces": "Shared pieces",
-  "Standard rules": "Standard rules"
+  "Standard rules": "Standard rules",
+  "Unidentified pieces": "Unidentified pieces"
 };
 };
index 3698845..d3ead1a 100644 (file)
@@ -148,5 +148,6 @@ export const translations = {
   "Reuse pieces": "Reutilizar piezas",
   "Reverse captures": "Capturas invertidas",
   "Shared pieces": "Piezas compartidas",
   "Reuse pieces": "Reutilizar piezas",
   "Reverse captures": "Capturas invertidas",
   "Shared pieces": "Piezas compartidas",
-  "Standard rules": "Reglas estandar"
+  "Standard rules": "Reglas estandar",
+  "Unidentified pieces": "Piezas no identificadas"
 };
 };
index 647c35e..5abf762 100644 (file)
@@ -148,5 +148,6 @@ export const translations = {
   "Reuse pieces": "Réutiliser les pièces",
   "Reverse captures": "Captures inversées",
   "Shared pieces": "Pièces partagées",
   "Reuse pieces": "Réutiliser les pièces",
   "Reverse captures": "Captures inversées",
   "Shared pieces": "Pièces partagées",
-  "Standard rules": "Règles usuelles"
+  "Standard rules": "Règles usuelles",
+  "Unidentified pieces": "Pièces non identifiées"
 };
 };
diff --git a/client/src/translations/rules/Hidden/en.pug b/client/src/translations/rules/Hidden/en.pug
new file mode 100644 (file)
index 0000000..f98808f
--- /dev/null
@@ -0,0 +1,26 @@
+p.boxed
+  | If a piece captures one of the same kind, both disappear.
+
+// TODO:
+  // Due to random 2 ranks: no castling (would reveal too much)
+  // To not reveal much, also no en passant
+
+p.
+  The defensive power of pawns is thus increased, because they don't fear
+  captures (by other pawns).
+
+p.
+  Endings are also affected quite a lot, and sometimes new threats occur:
+  on the diagram, 3.Bxg7 wins a pawn because 3...Bxg7 would make both
+  bishops disappear.
+
+figure.diagram-container
+  .diagram
+    | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR:
+  figcaption After 1.b3 c5 2.Bb2 Nc6
+
+h3 Source
+
+p
+  a(href="https://www.chessvariants.com/rules/strate-go-chess") Strate-Go chess
+  | &nbsp;on chessvariants.com.
diff --git a/client/src/translations/rules/Hidden/es.pug b/client/src/translations/rules/Hidden/es.pug
new file mode 100644 (file)
index 0000000..b6adc80
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Si una pieza captura otra del mismo tipo, las dos desaparecen.
+
+p.
+  El poder defensivo de los peones aumenta así, ya que no temen
+  más capturas (por otros peones).
+
+p.
+  Las finales también se ven muy afectadas y, a veces, nuevas amenazas
+  ocurren: en el diagrama, 3.Bxg7 gana un peón porque 3...Bxg7 causaría
+  la desaparición de los dos alfiles.
+
+figure.diagram-container
+  .diagram
+    | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR:
+  figcaption Después de 1.b3 c5 2.Bb2 Nc6
+
+h3 Fuente
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/rules/antimatter-chess") variante Antimateria
+  | &nbsp;en chessvariants.com.
diff --git a/client/src/translations/rules/Hidden/fr.pug b/client/src/translations/rules/Hidden/fr.pug
new file mode 100644 (file)
index 0000000..7b1cdfa
--- /dev/null
@@ -0,0 +1,23 @@
+p.boxed
+  | Si une pièce en capture une autre du même type, les deux disparaissent.
+
+p.
+  Le pouvoir défensif des pions est ainsi augmenté, puisqu'ils ne craignent
+  plus les captures (par d'autres pions).
+
+p.
+  Les finales sont aussi beaucoup affectées, et parfois de nouvelles menaces
+  surviennent : sur le diagramme, 3.Bxg7 gagne un pion car 3...Bxg7 provoquerait
+  la disparition des deux fous.
+
+figure.diagram-container
+  .diagram
+    | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR:
+  figcaption After 1.b3 c5 2.Bb2 Nc6
+
+h3 Source
+
+p
+  | La 
+  a(href="https://www.chessvariants.com/rules/antimatter-chess") variante Antimatière
+  | &nbsp;sur chessvariants.com.
index 60ccf6a..36ca094 100644 (file)
@@ -5,10 +5,14 @@ p.
   When a piece is taken it is not removed from the board, but moved instead
   to the square that was initially occupied by the capturing piece.
 
   When a piece is taken it is not removed from the board, but moved instead
   to the square that was initially occupied by the capturing piece.
 
+p.
+  It is forbidden to "un-do" a capture right after it was played:
+  in the diagram position, 3...Bxg7 is not allowed.
+
 figure.diagram-container
   .diagram
 figure.diagram-container
   .diagram
-    | fen:rnbqkb1r/ppp1pppp/5B2/3p4/8/1P6/PnPPPPPP/RN1QKBNR:
-  figcaption After 1.b3 d5 2.Bb2 Nf6 3.Bxf6
+    | fen:rnbqk1nr/ppppppBp/6p1/8/8/1P6/PbPPPPPP/RN1QKBNR:
+  figcaption After 1.b3 g6 2.Bb2 Bg7 3.Bxg7
 
 ul
   li.
 
 ul
   li.
index 685b755..3bc9e27 100644 (file)
@@ -6,10 +6,14 @@ p.
   Cuando se captura una pieza, no se retira del tablero, sino
   solo se movió a la casilla originalmente ocupada por la pieza que capturó.
 
   Cuando se captura una pieza, no se retira del tablero, sino
   solo se movió a la casilla originalmente ocupada por la pieza que capturó.
 
+p.
+  Está prohibido "deshacer" una captura inmediatamente después de que se
+  haya jugado: en la posición del diagrama, 3...Bxg7 no está permitido.
+
 figure.diagram-container
   .diagram
 figure.diagram-container
   .diagram
-    | fen:rnbqkb1r/ppp1pppp/5B2/3p4/8/1P6/PnPPPPPP/RN1QKBNR:
-  figcaption Después de 1.b3 d5 2.Bb2 Nf6 3.Bxf6
+    | fen:rnbqk1nr/ppppppBp/6p1/8/8/1P6/PbPPPPPP/RN1QKBNR:
+  figcaption Después de 1.b3 g6 2.Bb2 Bg7 3.Bxg7
 
 ul
   li.
 
 ul
   li.
index 2097062..931517e 100644 (file)
@@ -6,10 +6,14 @@ p.
   Quand une pièce est capturée elle n'est pas retirée de l'échiquier, mais
   seulement déplacée sur la case initialement occupée par la pièce capturante.
 
   Quand une pièce est capturée elle n'est pas retirée de l'échiquier, mais
   seulement déplacée sur la case initialement occupée par la pièce capturante.
 
+p.
+  Il est interdit de "défaire" une capture juste après qu'elle a été jouée :
+  dans la position du diagramme, 3...Bxg7 n'est pas autorisé.
+
 figure.diagram-container
   .diagram
 figure.diagram-container
   .diagram
-    | fen:rnbqkb1r/ppp1pppp/5B2/3p4/8/1P6/PnPPPPPP/RN1QKBNR:
-  figcaption Après 1.b3 d5 2.Bb2 Nf6 3.Bxf6
+    | fen:rnbqk1nr/ppppppBp/6p1/8/8/1P6/PbPPPPPP/RN1QKBNR:
+  figcaption Après 1.b3 g6 2.Bb2 Bg7 3.Bxg7
 
 ul
   li.
 
 ul
   li.
index aebdc24..d4562bf 100644 (file)
@@ -70,6 +70,7 @@ export function getDiagram(args) {
   const orientation = args.orientation || "w";
   const markArray = getMarkArray(args.marks);
   const shadowArray = getShadowArray(args.shadow);
   const orientation = args.orientation || "w";
   const markArray = getMarkArray(args.marks);
   const shadowArray = getShadowArray(args.shadow);
+  const vr = new V(); //just for pieces images paths
   let boardDiv = "";
   const [startX, startY, inc] =
     orientation == "w" ? [0, 0, 1] : [V.size.x - 1, V.size.y - 1, -1];
   let boardDiv = "";
   const [startX, startY, inc] =
     orientation == "w" ? [0, 0, 1] : [V.size.x - 1, V.size.y - 1, -1];
@@ -87,7 +88,7 @@ export function getDiagram(args) {
         boardDiv +=
           "<img " +
           "src='/images/pieces/" +
         boardDiv +=
           "<img " +
           "src='/images/pieces/" +
-          V.getPpath(board[i][j]) +
+          vr.getPpath(board[i][j]) +
           ".svg' " +
           "class='piece'/>";
       }
           ".svg' " +
           "class='piece'/>";
       }
index e7daf7f..b16d728 100644 (file)
@@ -25,14 +25,14 @@ export const VariantRules = class AliceRules extends ChessRules {
     };
   }
 
     };
   }
 
-  static getPpath(b) {
-    return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
-  }
-
   static get PIECES() {
     return ChessRules.PIECES.concat(Object.keys(V.ALICE_PIECES));
   }
 
   static get PIECES() {
     return ChessRules.PIECES.concat(Object.keys(V.ALICE_PIECES));
   }
 
+  getPpath(b) {
+    return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const rows = V.ParseFen(fen).position.split("/");
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const rows = V.ParseFen(fen).position.split("/");
index 3b1b36e..f07f626 100644 (file)
@@ -3,10 +3,6 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export const VariantRules = class AntikingRules extends ChessRules {
 import { randInt } from "@/utils/alea";
 
 export const VariantRules = class AntikingRules extends ChessRules {
-  static getPpath(b) {
-    return b[1] == "a" ? "Antiking/" + b : b;
-  }
-
   static get ANTIKING() {
     return "a";
   }
   static get ANTIKING() {
     return "a";
   }
@@ -15,6 +11,10 @@ export const VariantRules = class AntikingRules extends ChessRules {
     return ChessRules.PIECES.concat([V.ANTIKING]);
   }
 
     return ChessRules.PIECES.concat([V.ANTIKING]);
   }
 
+  getPpath(b) {
+    return b[1] == "a" ? "Antiking/" + b : b;
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     this.antikingPos = { w: [-1, -1], b: [-1, -1] };
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     this.antikingPos = { w: [-1, -1], b: [-1, -1] };
index 6557b38..8f1f9ba 100644 (file)
@@ -11,17 +11,17 @@ export const VariantRules = class BaroqueRules extends ChessRules {
     return false;
   }
 
     return false;
   }
 
-  static getPpath(b) {
+  static get PIECES() {
+    return ChessRules.PIECES.concat([V.IMMOBILIZER]);
+  }
+
+  getPpath(b) {
     if (b[1] == "m")
       //'m' for Immobilizer (I is too similar to 1)
       return "Baroque/" + b;
     return b; //usual piece
   }
 
     if (b[1] == "m")
       //'m' for Immobilizer (I is too similar to 1)
       return "Baroque/" + b;
     return b; //usual piece
   }
 
-  static get PIECES() {
-    return ChessRules.PIECES.concat([V.IMMOBILIZER]);
-  }
-
   // No castling, but checks, so keep track of kings
   setOtherVariables(fen) {
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
   // No castling, but checks, so keep track of kings
   setOtherVariables(fen) {
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
index 0e4f0a0..d477a6d 100644 (file)
@@ -1,10 +1,6 @@
 import { ChessRules } from "@/base_rules";
 
 export const VariantRules = class CheckeredRules extends ChessRules {
 import { ChessRules } from "@/base_rules";
 
 export const VariantRules = class CheckeredRules extends ChessRules {
-  static getPpath(b) {
-    return b[0] == "c" ? "Checkered/" + b : b;
-  }
-
   static board2fen(b) {
     const checkered_codes = {
       p: "s",
   static board2fen(b) {
     const checkered_codes = {
       p: "s",
@@ -40,6 +36,10 @@ export const VariantRules = class CheckeredRules extends ChessRules {
     return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]);
   }
 
     return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]);
   }
 
+  getPpath(b) {
+    return b[0] == "c" ? "Checkered/" + b : b;
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Local stack of non-capturing checkered moves:
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Local stack of non-capturing checkered moves:
@@ -130,7 +130,7 @@ export const VariantRules = class CheckeredRules extends ChessRules {
         }
       }
       if (m.vanish.length == 1) moves.push(m);
         }
       }
       if (m.vanish.length == 1) moves.push(m);
-      //no capture
+      // No capture
       else {
         // A capture occured (m.vanish.length == 2)
         m.appear[0].c = "c";
       else {
         // A capture occured (m.vanish.length == 2)
         m.appear[0].c = "c";
@@ -156,7 +156,7 @@ export const VariantRules = class CheckeredRules extends ChessRules {
   // Does m2 un-do m1 ? (to disallow undoing checkered moves)
   oppositeMoves(m1, m2) {
     return (
   // Does m2 un-do m1 ? (to disallow undoing checkered moves)
   oppositeMoves(m1, m2) {
     return (
-      !!m1 &&
+      m1 &&
       m2.appear[0].c == "c" &&
       m2.appear.length == 1 &&
       m2.vanish.length == 1 &&
       m2.appear[0].c == "c" &&
       m2.appear.length == 1 &&
       m2.vanish.length == 1 &&
@@ -170,8 +170,8 @@ export const VariantRules = class CheckeredRules extends ChessRules {
   filterValid(moves) {
     if (moves.length == 0) return [];
     const color = this.turn;
   filterValid(moves) {
     if (moves.length == 0) return [];
     const color = this.turn;
+    const L = this.cmoves.length; //at least 1: init from FEN
     return moves.filter(m => {
     return moves.filter(m => {
-      const L = this.cmoves.length; //at least 1: init from FEN
       if (this.oppositeMoves(this.cmoves[L - 1], m)) return false;
       this.play(m);
       const res = !this.underCheck(color);
       if (this.oppositeMoves(this.cmoves[L - 1], m)) return false;
       this.play(m);
       const res = !this.underCheck(color);
index d7d82b8..24018d1 100644 (file)
@@ -104,7 +104,7 @@ export const VariantRules = class CrazyhouseRules extends ChessRules {
   }
 
   // Used by the interface:
   }
 
   // Used by the interface:
-  getReservePpath(color, index) {
+  getReservePpath(index, color) {
     return color + V.RESERVE_PIECES[index];
   }
 
     return color + V.RESERVE_PIECES[index];
   }
 
index d6515ef..3944ca1 100644 (file)
@@ -46,9 +46,7 @@ export const VariantRules = class DarkRules extends ChessRules {
                 V.OnBoard(i + pawnShift[color], j + shiftY) &&
                 this.board[i + pawnShift[color]][j + shiftY] == V.EMPTY
               ) {
                 V.OnBoard(i + pawnShift[color], j + shiftY) &&
                 this.board[i + pawnShift[color]][j + shiftY] == V.EMPTY
               ) {
-                this.enlightened[color][i + pawnShift[color]][
-                  j + shiftY
-                ] = true;
+                this.enlightened[color][i + pawnShift[color]][j + shiftY] = true;
               }
             }
           }
               }
             }
           }
@@ -83,25 +81,15 @@ export const VariantRules = class DarkRules extends ChessRules {
     return potentialMoves; //because there are no checks
   }
 
     return potentialMoves; //because there are no checks
   }
 
-  atLeastOneMove() {
-    if (this.kingPos[this.turn][0] < 0) return false;
-    return true; //TODO: is it right?
-  }
-
-  underCheck() {
-    return false; //there is no check
-  }
-
   getCheckSquares() {
     return [];
   }
 
   updateVariables(move) {
     super.updateVariables(move);
   getCheckSquares() {
     return [];
   }
 
   updateVariables(move) {
     super.updateVariables(move);
-    if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) {
-      // We took opponent king (because if castle vanish[1] is a rook)
+    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];
       this.kingPos[this.turn] = [-1, -1];
-    }
 
     // Update lights for both colors:
     this.updateEnlightened();
 
     // Update lights for both colors:
     this.updateEnlightened();
@@ -111,15 +99,9 @@ export const VariantRules = class DarkRules extends ChessRules {
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
     const oppCol = V.GetOppCol(c);
     super.unupdateVariables(move);
     const c = move.vanish[0].c;
     const oppCol = V.GetOppCol(c);
-    if (this.kingPos[oppCol][0] < 0) {
-      // Last move took opponent's king
-      for (let psq of move.vanish) {
-        if (psq.p == "k") {
-          this.kingPos[oppCol] = [psq.x, psq.y];
-          break;
-        }
-      }
-    }
+    if (this.kingPos[oppCol][0] < 0)
+      // Last move took opponent's king:
+      this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y];
 
     // Update lights for both colors:
     this.updateEnlightened();
 
     // Update lights for both colors:
     this.updateEnlightened();
@@ -129,12 +111,10 @@ export const VariantRules = class DarkRules extends ChessRules {
     const color = this.turn;
     const kp = this.kingPos[color];
     if (kp[0] < 0)
     const color = this.turn;
     const kp = this.kingPos[color];
     if (kp[0] < 0)
-      //king disappeared
+      // King disappeared
       return color == "w" ? "0-1" : "1-0";
       return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove())
-      // game not over
-      return "*";
-    return "1/2"; //no moves but kings still there (seems impossible)
+    // Assume that stalemate is impossible (I think so. Would need proof...)
+    return "*";
   }
 
   static get THRESHOLD_MATE() {
   }
 
   static get THRESHOLD_MATE() {
index c4e2eb1..d804f61 100644 (file)
@@ -5,10 +5,6 @@ import { randInt } from "@/utils/alea";
 // NOTE: initial setup differs from the original; see
 // https://www.chessvariants.com/large.dir/freeling.html
 export const VariantRules = class GrandRules extends ChessRules {
 // NOTE: initial setup differs from the original; see
 // https://www.chessvariants.com/large.dir/freeling.html
 export const VariantRules = class GrandRules extends ChessRules {
-  static getPpath(b) {
-    return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
-  }
-
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -35,6 +31,10 @@ export const VariantRules = class GrandRules extends ChessRules {
     return Object.assign(ChessRules.ParseFen(fen), { captured: fenParts[5] });
   }
 
     return Object.assign(ChessRules.ParseFen(fen), { captured: fenParts[5] });
   }
 
+  getPpath(b) {
+    return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
+  }
+
   getFen() {
     return super.getFen() + " " + this.getCapturedFen();
   }
   getFen() {
     return super.getFen() + " " + this.getCapturedFen();
   }
diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js
new file mode 100644 (file)
index 0000000..1836352
--- /dev/null
@@ -0,0 +1,185 @@
+import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
+export const VariantRules = class HiddenRules extends ChessRules {
+  static get HasFlags() {
+    return false;
+  }
+
+  static get HasEnpassant() {
+    return false;
+  }
+
+  // Analyse in Hidden mode makes no sense
+  static get CanAnalyze() {
+    return false;
+  }
+
+  // Moves are revealed only when game ends
+  static get ShowMoves() {
+    return "none";
+  }
+
+  static get HIDDEN_DECODE() {
+    return {
+      s: "p",
+      t: "q",
+      u: "r",
+      c: "b",
+      o: "n",
+      l: "k"
+    };
+  }
+  static get HIDDEN_CODE() {
+    return {
+      p: "s",
+      q: "t",
+      r: "u",
+      b: "c",
+      n: "o",
+      k: "l"
+    };
+  }
+
+  static get PIECES() {
+    return ChessRules.PIECES.concat(Object.values(V.HIDDEN_CODE));
+  }
+
+  // Scan board for kings positions (no castling)
+  scanKingsRooks(fen) {
+    this.kingPos = { w: [-1, -1], b: [-1, -1] };
+    const fenRows = V.ParseFen(fen).position.split("/");
+    for (let i = 0; i < fenRows.length; i++) {
+      let k = 0; //column index on board
+      for (let j = 0; j < fenRows[i].length; j++) {
+        switch (fenRows[i].charAt(j)) {
+          case "k":
+          case "l":
+            this.kingPos["b"] = [i, k];
+            break;
+          case "K":
+          case "L":
+            this.kingPos["w"] = [i, k];
+            break;
+          default: {
+            const num = parseInt(fenRows[i].charAt(j));
+            if (!isNaN(num)) k += num - 1;
+          }
+        }
+        k++;
+      }
+    }
+  }
+
+  getPpath(b, color, score) {
+    if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) {
+      // Supposed to be hidden.
+      if (score == "*" && (!color || color != b[0]))
+        return "Hidden/" + b[0] + "p";
+      // Else: condition OK to show the piece
+      return b[0] + V.HIDDEN_DECODE[b[1]];
+    }
+    // The piece is already not supposed to be hidden:
+    return b;
+  }
+
+  //getPotentialMovesFrom: TODO: write
+
+  // TODO: bishops on different colors, a1 --> h1, h2 --> a2 ?
+  static GenRandInitFen() {
+    let pieces = { w: new Array(8), b: new Array(8) };
+    // Shuffle pieces + pawns on two first ranks
+    for (let c of ["w", "b"]) {
+      let positions = ArrayFun.range(16);
+
+      // Get random squares for bishops
+      let randIndex = 2 * randInt(8);
+      const bishop1Pos = positions[randIndex];
+      // The second bishop must be on a square of different color
+      let randIndex_tmp = 2 * randInt(8) + 1;
+      const bishop2Pos = positions[randIndex_tmp];
+      // Remove chosen squares
+      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
+
+      // Get random squares for knights
+      randIndex = randInt(14);
+      const knight1Pos = positions[randIndex];
+      positions.splice(randIndex, 1);
+      randIndex = randInt(13);
+      const knight2Pos = positions[randIndex];
+      positions.splice(randIndex, 1);
+
+      // Get random square for queen
+      randIndex = randInt(12);
+      const queenPos = positions[randIndex];
+      positions.splice(randIndex, 1);
+
+      // Get random squares for pawns
+      // TODO...
+
+      // Rooks and king positions are now fixed,
+      // because of the ordering rook-king-rook
+      const rook1Pos = positions[0];
+      const kingPos = positions[1];
+      const rook2Pos = positions[2];
+
+      // Finally put the shuffled pieces in the board array
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
+    }
+    return (
+      pieces["b"].join("") +
+      "/pppppppp/8/8/8/8/PPPPPPPP/" +
+      pieces["w"].join("").toUpperCase() +
+      " w 0"
+    );
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  updateVariables(move) {
+    super.updateVariables(move);
+    if (
+      move.vanish.length >= 2 &&
+      [V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p)
+    ) {
+      // We took opponent king
+      this.kingPos[this.turn] = [-1, -1];
+    }
+  }
+
+  unupdateVariables(move) {
+    super.unupdateVariables(move);
+    const c = move.vanish[0].c;
+    const oppCol = V.GetOppCol(c);
+    if (this.kingPos[oppCol][0] < 0)
+      // Last move took opponent's king:
+      this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y];
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    const kp = this.kingPos[color];
+    if (kp[0] < 0)
+      // King disappeared
+      return color == "w" ? "0-1" : "1-0";
+    // Assume that stalemate is impossible:
+    return "*";
+  }
+
+  getComputerMove() {
+    // Just return a random move. TODO: something smarter...
+    const moves = this.getAllValidMoves();
+    return moves[randInt(moves.length)];
+  }
+};
index d99228c..50ccd74 100644 (file)
@@ -74,7 +74,7 @@ export const VariantRules = class RecycleRules extends ChessRules {
   }
 
   // Used by the interface:
   }
 
   // Used by the interface:
-  getReservePpath(color, index) {
+  getReservePpath(index, color) {
     return color + V.RESERVE_PIECES[index];
   }
 
     return color + V.RESERVE_PIECES[index];
   }
 
index bfb9fa0..38ee5c2 100644 (file)
@@ -1,6 +1,35 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
-export const VariantRules = class AntimatterRules extends ChessRules {
+export const VariantRules = class SuctionRules extends ChessRules {
+  setOtherVariables(fen) {
+    super.setOtherVariables(fen);
+    // Local stack of captures
+    this.cmoves = [];
+    const cmove = fen.split(" ")[5];
+    if (cmove == "-") this.cmoves.push(null);
+    else {
+      this.cmoves.push({
+        start: ChessRules.SquareToCoords(cmove.substr(0, 2)),
+        end: ChessRules.SquareToCoords(cmove.substr(2))
+      });
+    }
+  }
+
+  static IsGoodFen(fen) {
+    if (!ChessRules.IsGoodFen(fen)) return false;
+    const fenParts = fen.split(" ");
+    if (fenParts.length != 6) return false;
+    if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
+      return false;
+    return true;
+  }
+
+  getCmove(move) {
+    if (move.vanish.length == 2)
+      return { start: move.start, end: move.end };
+    return null;
+  }
+
   getBasicMove([sx, sy], [ex, ey]) {
     const startColor = this.getColor(sx, sy);
     const startPiece = this.getPiece(sx, sy);
   getBasicMove([sx, sy], [ex, ey]) {
     const startColor = this.getColor(sx, sy);
     const startPiece = this.getPiece(sx, sy);
@@ -121,6 +150,27 @@ export const VariantRules = class AntimatterRules extends ChessRules {
     return [];
   }
 
     return [];
   }
 
+  // Does m2 un-do m1 ? (to disallow undoing captures)
+  oppositeMoves(m1, m2) {
+    return (
+      m1 &&
+      m2.vanish.length == 2 &&
+      m1.start.x == m2.start.x &&
+      m1.end.x == m2.end.x &&
+      m1.start.y == m2.start.y &&
+      m1.end.y == m2.end.y
+    );
+  }
+
+  filterValid(moves) {
+    if (moves.length == 0) return [];
+    const color = this.turn;
+    return moves.filter(m => {
+      const L = this.cmoves.length; //at least 1: init from FEN
+      return !this.oppositeMoves(this.cmoves[L - 1], m);
+    });
+  }
+
   updateVariables(move) {
     super.updateVariables(move);
     if (move.vanish.length == 2) {
   updateVariables(move) {
     super.updateVariables(move);
     if (move.vanish.length == 2) {
@@ -139,12 +189,32 @@ export const VariantRules = class AntimatterRules extends ChessRules {
     }
   }
 
     }
   }
 
-  atLeastOneMove() {
-    return true;
+  static GenRandInitFen() {
+    // Add empty cmove:
+    return ChessRules.GenRandInitFen() + " -";
   }
 
   }
 
-  filterValid(moves) {
-    return moves;
+  getFen() {
+    const L = this.cmoves.length;
+    const cmoveFen = !this.cmoves[L - 1]
+      ? "-"
+      : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) +
+        ChessRules.CoordsToSquare(this.cmoves[L - 1].end);
+    return super.getFen() + " " + cmoveFen;
+  }
+
+  play(move) {
+    this.cmoves.push(this.getCmove(move));
+    super.play(move);
+  }
+
+  undo(move) {
+    this.cmoves.pop();
+    super.undo(move);
+  }
+
+  atLeastOneMove() {
+    return true;
   }
 
   getCheckSquares() {
   }
 
   getCheckSquares() {
index 933abbd..27b131d 100644 (file)
@@ -3,10 +3,6 @@ import { ArrayFun } from "@/utils/array";
 import { sample, randInt } from "@/utils/alea";
 
 export const VariantRules = class WildebeestRules extends ChessRules {
 import { sample, randInt } from "@/utils/alea";
 
 export const VariantRules = class WildebeestRules extends ChessRules {
-  static getPpath(b) {
-    return ([V.CAMEL, V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
-  }
-
   static get size() {
     return { x: 10, y: 11 };
   }
   static get size() {
     return { x: 10, y: 11 };
   }
@@ -52,6 +48,10 @@ export const VariantRules = class WildebeestRules extends ChessRules {
     return true;
   }
 
     return true;
   }
 
+  getPpath(b) {
+    return ([V.CAMEL, V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
+  }
+
   // There may be 2 enPassant squares (if pawn jump 3 squares)
   getEnpassantFen() {
     const L = this.epSquares.length;
   // There may be 2 enPassant squares (if pawn jump 3 squares)
   getEnpassantFen() {
     const L = this.epSquares.length;
index 2876ba7..384b735 100644 (file)
@@ -16,6 +16,7 @@ insert or ignore into Variants (name,description) values
   ('Enpassant', 'Capture en passant'),
   ('Extinction', 'Capture all of a kind'),
   ('Grand', 'Big board'),
   ('Enpassant', 'Capture en passant'),
   ('Extinction', 'Capture all of a kind'),
   ('Grand', 'Big board'),
+  ('Hidden', 'Unidentified pieces'),
   ('Losers', 'Lose all pieces'),
   ('Magnetic', 'Laws of attraction'),
   ('Marseille', 'Move twice'),
   ('Losers', 'Lose all pieces'),
   ('Magnetic', 'Laws of attraction'),
   ('Marseille', 'Move twice'),