Merge Atomic1 & 2
[vchess.git] / client / src / base_rules.js
index 6502679..0876473 100644 (file)
@@ -22,8 +22,8 @@ export const Move = class Move {
   constructor(o) {
     this.appear = o.appear;
     this.vanish = o.vanish;
-    this.start = o.start ? o.start : { x: o.vanish[0].x, y: o.vanish[0].y };
-    this.end = o.end ? o.end : { x: o.appear[0].x, y: o.appear[0].y };
+    this.start = o.start || { x: o.vanish[0].x, y: o.vanish[0].y };
+    this.end = o.end || { x: o.appear[0].x, y: o.appear[0].y };
   }
 };
 
@@ -34,6 +34,34 @@ export const ChessRules = class ChessRules {
   //////////////
   // MISC UTILS
 
+  static get Options() {
+    return {
+      select: [
+        {
+          label: "Randomness",
+          variable: "randomness",
+          defaut: 0,
+          options: [
+            { label: "Deterministic", value: 0 },
+            { label: "Symmetric random", value: 1 },
+            { label: "Asymmetric random", value: 2 }
+          ]
+        }
+      ],
+      check: []
+    };
+  }
+
+  static AbbreviateOptions(opts) {
+    return "";
+    // Randomness is a special option: (TODO?)
+    //return "R" + opts.randomness;
+  }
+
+  static IsValidOptions(opts) {
+    return true;
+  }
+
   // Some variants don't have flags:
   static get HasFlags() {
     return true;
@@ -107,16 +135,15 @@ export const ChessRules = class ChessRules {
     return V.CanFlip;
   }
 
+  // NOTE: these will disappear once each variant has its dedicated SVG board.
   // For (generally old) variants without checkered board
   static get Monochrome() {
     return false;
   }
-
   // Some games are drawn unusually (bottom right corner is black)
   static get DarkBottomRight() {
     return false;
   }
-
   // Some variants require lines drawing
   static get Lines() {
     if (V.Monochrome) {
@@ -135,6 +162,19 @@ export const ChessRules = class ChessRules {
   static get LoseOnRepetition() {
     return false;
   }
+  // And in some others (Iceage), repetitions should be ignored:
+  static get IgnoreRepetition() {
+    return false;
+  }
+  loseOnRepetition() {
+    // In some variants, result depends on the position:
+    return V.LoseOnRepetition;
+  }
+
+  // At some stages, some games could wait clicks only:
+  onlyClick() {
+    return false;
+  }
 
   // Some variants use click infos:
   doClick() {
@@ -163,7 +203,6 @@ export const ChessRules = class ChessRules {
 
   // Check if FEN describes a board situation correctly
   static IsGoodFen(fen) {
-console.log("ddd");
     const fenParsed = V.ParseFen(fen);
     // 1) Check position
     if (!V.IsGoodPosition(fenParsed.position)) return false;
@@ -331,8 +370,8 @@ console.log("ddd");
   // FEN UTILS
 
   // Setup the initial random (asymmetric) position
-  static GenRandInitFen(randomness) {
-    if (randomness == 0)
+  static GenRandInitFen(options) {
+    if (!options.randomness || options.randomness == 0)
       // Deterministic:
       return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
 
@@ -340,7 +379,7 @@ console.log("ddd");
     let flags = "";
     // Shuffle pieces on first (and last rank if randomness == 2)
     for (let c of ["w", "b"]) {
-      if (c == 'b' && randomness == 1) {
+      if (c == 'b' && options.randomness == 1) {
         pieces['b'] = pieces['w'];
         flags += flags;
         break;
@@ -438,8 +477,10 @@ console.log("ddd");
       // if more than 9 consecutive free spaces, break the integer,
       // otherwise FEN parsing will fail.
       if (count <= 9) return count;
-      // Currently only boards of size up to 11 or 12:
-      return "9" + (count - 9);
+      // Most boards of size < 18:
+      if (count <= 18) return "9" + (count - 9);
+      // Except Gomoku:
+      return "99" + (count - 18);
     };
     let position = "";
     for (let i = 0; i < V.size.x; i++) {
@@ -663,7 +704,7 @@ console.log("ddd");
       case V.QUEEN: return this.getPotentialQueenMoves(sq);
       case V.KING: return this.getPotentialKingMoves(sq);
     }
-    return []; //never reached
+    return []; //never reached (but some variants may use it: Bario...)
   }
 
   // Build a regular move from its initial and destination squares.
@@ -707,14 +748,15 @@ console.log("ddd");
 
   // Generic method to find possible moves of non-pawn pieces:
   // "sliding or jumping"
-  getSlideNJumpMoves([x, y], steps, oneStep) {
+  getSlideNJumpMoves([x, y], steps, nbSteps) {
     let moves = [];
     outerLoop: for (let step of steps) {
       let i = x + step[0];
       let j = y + step[1];
+      let stepCounter = 0;
       while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
         moves.push(this.getBasicMove([x, y], [i, j]));
-        if (oneStep) continue outerLoop;
+        if (nbSteps && ++stepCounter >= nbSteps) continue outerLoop;
         i += step[0];
         j += step[1];
       }
@@ -755,9 +797,8 @@ console.log("ddd");
       if (!!promotions) finalPieces = promotions;
       else if (!!V.PawnSpecs.promotions) finalPieces = V.PawnSpecs.promotions;
     }
-    let tr = null;
     for (let piece of finalPieces) {
-      tr = (piece != V.PAWN ? { c: color, p: piece } : null);
+      const tr = (piece != V.PAWN ? { c: color, p: piece } : null);
       moves.push(this.getBasicMove([x1, y1], [x2, y2], tr));
     }
   }
@@ -857,7 +898,7 @@ console.log("ddd");
 
   // What are the knight moves from square x,y ?
   getPotentialKnightMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
+    return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1);
   }
 
   // What are the bishop moves from square x,y ?
@@ -868,19 +909,14 @@ console.log("ddd");
   // What are the queen moves from square x,y ?
   getPotentialQueenMoves(sq) {
     return this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP])
-    );
+      sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
   }
 
   // What are the king moves from square x,y ?
   getPotentialKingMoves(sq) {
     // Initialize with normal moves
     let moves = this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
+      sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
     if (V.HasCastle && this.castleFlags[this.turn].some(v => v < V.size.y))
       moves = moves.concat(this.getCastleMoves(sq));
     return moves;
@@ -1031,6 +1067,9 @@ console.log("ddd");
 
   // Stop at the first move found
   // TODO: not really, it explores all moves from a square (one is enough).
+  // Possible fix: add extra arg "oneMove" to getPotentialMovesFrom,
+  // and then return only boolean true at first move found
+  // (in all getPotentialXXXMoves() ... for all variants ...)
   atLeastOneMove() {
     const color = this.turn;
     for (let i = 0; i < V.size.x; i++) {
@@ -1061,20 +1100,24 @@ console.log("ddd");
 
   // Generic method for non-pawn pieces ("sliding or jumping"):
   // is x,y attacked by a piece of given color ?
-  isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
+  isAttackedBySlideNJump([x, y], color, piece, steps, nbSteps) {
     for (let step of steps) {
       let rx = x + step[0],
           ry = y + step[1];
-      while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
+      let stepCounter = 1;
+      while (
+        V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY &&
+        (!nbSteps || stepCounter < nbSteps)
+      ) {
         rx += step[0];
         ry += step[1];
+        stepCounter++;
       }
       if (
         V.OnBoard(rx, ry) &&
         this.board[rx][ry] != V.EMPTY &&
         this.getPiece(rx, ry) == piece &&
-        this.getColor(rx, ry) == color &&
-        this.canTake([rx, ry], [x, y]) //for Paco-Sako (TODO: necessary?)
+        this.getColor(rx, ry) == color
       ) {
         return true;
       }