Some fixes, work on Eightpieces draft, add a few capturing variants
[vchess.git] / client / src / base_rules.js
index 5b335b0..33b0fc9 100644 (file)
@@ -197,13 +197,10 @@ export const ChessRules = class ChessRules {
     const move = moveOrSquare;
     const s = move.start,
           e = move.end;
-    // NOTE: next conditions are first for Atomic, and last for Checkered
     if (
-      move.appear.length > 0 &&
       Math.abs(s.x - e.x) == 2 &&
       s.y == e.y &&
-      move.appear[0].p == V.PAWN &&
-      ["w", "b"].includes(move.appear[0].c)
+      move.appear[0].p == V.PAWN
     ) {
       return {
         x: (s.x + e.x) / 2,
@@ -238,11 +235,20 @@ export const ChessRules = class ChessRules {
   /////////////
   // FEN UTILS
 
-  // Setup the initial random (assymetric) position
-  static GenRandInitFen() {
+  // Setup the initial random (asymmetric) position
+  static GenRandInitFen(randomness) {
+    if (randomness == 0)
+      // Deterministic:
+      return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 1111 -";
+
     let pieces = { w: new Array(8), b: new Array(8) };
-    // Shuffle pieces on first and last rank
+    // Shuffle pieces on first (and last rank if randomness == 2)
     for (let c of ["w", "b"]) {
+      if (c == 'b' && randomness == 1) {
+        pieces['b'] = pieces['w'];
+        break;
+      }
+
       let positions = ArrayFun.range(8);
 
       // Get random squares for bishops
@@ -310,16 +316,24 @@ export const ChessRules = class ChessRules {
   // Return current fen (game state)
   getFen() {
     return (
-      this.getBaseFen() +
-      " " +
-      this.getTurnFen() +
-      " " +
+      this.getBaseFen() + " " +
+      this.getTurnFen() + " " +
       this.movesCount +
       (V.HasFlags ? " " + this.getFlagsFen() : "") +
       (V.HasEnpassant ? " " + this.getEnpassantFen() : "")
     );
   }
 
+  getFenForRepeat() {
+    // Omit movesCount, only variable allowed to differ
+    return (
+      this.getBaseFen() + "_" +
+      this.getTurnFen() +
+      (V.HasFlags ? "_" + this.getFlagsFen() : "") +
+      (V.HasEnpassant ? "_" + this.getEnpassantFen() : "")
+    );
+  }
+
   // Position part of the FEN string
   getBaseFen() {
     let position = "";
@@ -414,6 +428,7 @@ export const ChessRules = class ChessRules {
     this.INIT_COL_ROOK = { w: [-1, -1], b: [-1, -1] };
     this.kingPos = { w: [-1, -1], b: [-1, -1] }; //squares of white and black king
     const fenRows = V.ParseFen(fen).position.split("/");
+    const startRow = { 'w': V.size.x - 1, 'b': 0 };
     for (let i = 0; i < fenRows.length; i++) {
       let k = 0; //column index on board
       for (let j = 0; j < fenRows[i].length; j++) {
@@ -427,12 +442,16 @@ export const ChessRules = class ChessRules {
             this.INIT_COL_KING["w"] = k;
             break;
           case "r":
-            if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k;
-            else this.INIT_COL_ROOK["b"][1] = k;
+            if (i == startRow['b']) {
+              if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k;
+              else this.INIT_COL_ROOK["b"][1] = k;
+            }
             break;
           case "R":
-            if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k;
-            else this.INIT_COL_ROOK["w"][1] = k;
+            if (i == startRow['w']) {
+              if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k;
+              else this.INIT_COL_ROOK["w"][1] = k;
+            }
             break;
           default: {
             const num = parseInt(fenRows[i].charAt(j));
@@ -627,7 +646,6 @@ export const ChessRules = class ChessRules {
     const firstRank = color == "w" ? sizeX - 1 : 0;
     const startRank = color == "w" ? sizeX - 2 : 1;
     const lastRank = color == "w" ? 0 : sizeX - 1;
-    const pawnColor = this.getColor(x, y); //can be different for checkered
 
     // NOTE: next condition is generally true (no pawn on last rank)
     if (x + shiftX >= 0 && x + shiftX < sizeX) {
@@ -640,7 +658,7 @@ export const ChessRules = class ChessRules {
         for (let piece of finalPieces) {
           moves.push(
             this.getBasicMove([x, y], [x + shiftX, y], {
-              c: pawnColor,
+              c: color,
               p: piece
             })
           );
@@ -665,7 +683,7 @@ export const ChessRules = class ChessRules {
           for (let piece of finalPieces) {
             moves.push(
               this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
-                c: pawnColor,
+                c: color,
                 p: piece
               })
             );
@@ -983,7 +1001,7 @@ export const ChessRules = class ChessRules {
   // After move is played, update variables + flags
   updateVariables(move) {
     let piece = undefined;
-    // TODO: update variables before move is played, and just use this.turn ?
+    // TODO: update variables before move is played, and just use this.turn?
     // (doesn't work in general, think MarseilleChess)
     let c = undefined;
     if (move.vanish.length >= 1) {
@@ -1040,7 +1058,7 @@ export const ChessRules = class ChessRules {
   play(move) {
     // DEBUG:
 //    if (!this.states) this.states = [];
-//    const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
+//    const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen();
 //    this.states.push(stateFen);
 
     if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
@@ -1060,7 +1078,7 @@ export const ChessRules = class ChessRules {
     this.unupdateVariables(move);
 
     // DEBUG:
-//    const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
+//    const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen();
 //    if (stateFen != this.states[this.states.length-1]) debugger;
 //    this.states.pop();
   }
@@ -1115,16 +1133,18 @@ export const ChessRules = class ChessRules {
   getComputerMove() {
     const maxeval = V.INFINITY;
     const color = this.turn;
-    // 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();
 
     if (moves1.length == 0)
       // TODO: this situation should not happen
       return null;
 
-    // Rank moves using a min-max at depth 2
+    // Rank moves using a min-max at depth 2 (if search_depth >= 2!)
     for (let i = 0; i < moves1.length; i++) {
+      if (V.SEARCH_DEPTH == 1) {
+        moves1[i].eval = this.evalPosition();
+        continue;
+      }
       // Initial self evaluation is very low: "I'm checkmated"
       moves1[i].eval = (color == "w" ? -1 : 1) * maxeval;
       this.play(moves1[i]);
@@ -1172,19 +1192,9 @@ export const ChessRules = class ChessRules {
     });
 //    console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
 
-    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[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
-          return currentBest; //depth 2 at least
         this.play(moves1[i]);
         // 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
         moves1[i].eval =
@@ -1195,10 +1205,9 @@ export const ChessRules = class ChessRules {
       moves1.sort((a, b) => {
         return (color == "w" ? 1 : -1) * (b.eval - a.eval);
       });
-    } else return currentBest;
-//    console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
+    }
 
-    candidates = [0];
+    let candidates = [0];
     for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++)
       candidates.push(j);
     return moves1[candidates[randInt(candidates.length)]];