Check variants. All OK except Dark (bug), Checkered (missing internal moves stack...
[vchess.git] / client / src / base_rules.js
index 087b4f5..8cf86a8 100644 (file)
@@ -2,7 +2,7 @@
 // Variants generally inherit from it, and modify some parts.
 
 import { ArrayFun } from "@/utils/array";
-import { random, sample, shuffle } from "@/utils/alea";
+import { randInt, shuffle } from "@/utils/alea";
 
 export const PiPo = class PiPo //Piece+Position
 {
@@ -50,13 +50,13 @@ export const ChessRules = class ChessRules
   // Turn "wb" into "B" (for FEN)
   static board2fen(b)
   {
-    return b[0]=='w' ? b[1].toUpperCase() : b[1];
+    return (b[0]=='w' ? b[1].toUpperCase() : b[1]);
   }
 
   // Turn "p" into "bp" (for board)
   static fen2board(f)
   {
-    return f.charCodeAt()<=90 ? "w"+f.toLowerCase() : "b"+f;
+    return (f.charCodeAt()<=90 ? "w"+f.toLowerCase() : "b"+f);
   }
 
   // Check if FEN describe a position
@@ -192,7 +192,7 @@ export const ChessRules = class ChessRules
     // Argument is a move:
     const move = moveOrSquare;
     const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
-    // TODO: next conditions are first for Atomic, and last for Checkered
+    // NOTE: next conditions are first for Atomic, and last for Checkered
     if (move.appear.length > 0 && Math.abs(sx - ex) == 2
       && move.appear[0].p == V.PAWN && ["w","b"].includes(move.appear[0].c))
     {
@@ -243,25 +243,25 @@ export const ChessRules = class ChessRules
       let positions = ArrayFun.range(8);
 
       // Get random squares for bishops
-      let randIndex = 2 * random(4);
+      let randIndex = 2 * randInt(4);
       const bishop1Pos = positions[randIndex];
       // The second bishop must be on a square of different color
-      let randIndex_tmp = 2 * random(4) + 1;
+      let randIndex_tmp = 2 * randInt(4) + 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 = random(6);
+      randIndex = randInt(6);
       const knight1Pos = positions[randIndex];
       positions.splice(randIndex, 1);
-      randIndex = random(5);
+      randIndex = randInt(5);
       const knight2Pos = positions[randIndex];
       positions.splice(randIndex, 1);
 
       // Get random square for queen
-      randIndex = random(4);
+      randIndex = randInt(4);
       const queenPos = positions[randIndex];
       positions.splice(randIndex, 1);
 
@@ -409,8 +409,13 @@ export const ChessRules = class ChessRules
   //////////////////
   // INITIALIZATION
 
-  // Fen string fully describes the game state
   constructor(fen)
+  {
+    this.re_init(fen);
+  }
+
+  // Fen string fully describes the game state
+  re_init(fen)
   {
     const fenParsed = V.ParseFen(fen);
     this.board = V.GetBoard(fenParsed.position);
@@ -1057,8 +1062,6 @@ export const ChessRules = class ChessRules
       move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
     if (V.HasEnpassant)
       this.epSquares.push( this.getEpSquare(move) );
-    if (!move.color)
-      move.color = this.turn; //for interface
     V.PlayOnBoard(this.board, move);
     this.turn = V.GetOppCol(this.turn);
     this.movesCount++;
@@ -1125,7 +1128,6 @@ export const ChessRules = class ChessRules
   // Search depth: 2 for high branching factor, 4 for small (Loser chess, eg.)
   static get SEARCH_DEPTH() { return 3; }
 
-  // Assumption: at least one legal move
   // NOTE: works also for extinction chess because depth is 3...
   getComputerMove()
   {
@@ -1134,6 +1136,8 @@ export const ChessRules = class ChessRules
     // Some variants may show a bigger moves list to the human (Switching),
     // thus the argument "computer" below (which is generally ignored)
     let moves1 = this.getAllValidMoves("computer");
+    if (moves1.length == 0) //TODO: this situation should not happen
+      return null;
 
     // Can I mate in 1 ? (for Magnetic & Extinction)
     for (let i of shuffle(ArrayFun.range(moves1.length)))
@@ -1194,14 +1198,13 @@ export const ChessRules = class ChessRules
     let candidates = [0]; //indices of candidates moves
     for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
       candidates.push(j);
-    let currentBest = moves1[sample(candidates)];
-
-    // From here, depth >= 3: may take a while, so we control time
-    const timeStart = Date.now();
+    let currentBest = moves1[candidates[randInt(candidates.length)]];
 
     // Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...)
     if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE)
     {
+      // From here, depth >= 3: may take a while, so we control time
+      const timeStart = Date.now();
       for (let i=0; i<moves1.length; i++)
       {
         if (Date.now()-timeStart >= 5000) //more than 5 seconds
@@ -1222,7 +1225,7 @@ export const ChessRules = class ChessRules
     candidates = [0];
     for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
       candidates.push(j);
-    return moves1[sample(candidates)];
+    return moves1[candidates[randInt(candidates.length)]];
   }
 
   alphabeta(depth, alpha, beta)