Simplify Monster + Doublemove2. Smoother example games
[vchess.git] / client / src / variants / Doublemove2.js
index d1afff8..080d13b 100644 (file)
@@ -79,40 +79,23 @@ export class Doublemove2Rules extends ChessRules {
     return moves;
   }
 
-  isAttacked(sq, color, castling) {
-    const singleMoveAttack = super.isAttacked(sq, color);
-    if (singleMoveAttack) return true;
-    if (!!castling) {
-      if (this.subTurn == 1)
-        // Castling at move 1 could be done into check
-        return false;
-      return singleMoveAttack;
-    }
-    // Double-move allowed:
-    const curTurn = this.turn;
-    this.turn = color;
-    const moves1 = super.getAllPotentialMoves();
-    this.turn = curTurn;
-    for (let move of moves1) {
-      this.play(move);
-      const res = super.isAttacked(sq, color);
-      this.undo(move);
-      if (res) return res;
-    }
+  isAttacked(sq, color) {
+    // Goal is king capture => no checks
     return false;
   }
 
   filterValid(moves) {
-    if (this.subTurn == 1) {
-      return moves.filter(m1 => {
-        this.play(m1);
-        // NOTE: no recursion because next call will see subTurn == 2
-        const res = super.atLeastOneMove();
-        this.undo(m1);
-        return res;
-      });
-    }
-    return super.filterValid(moves);
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0");
+    return "*";
   }
 
   play(move) {
@@ -139,12 +122,14 @@ export class Doublemove2Rules extends ChessRules {
     const firstRank = c == "w" ? V.size.x - 1 : 0;
 
     if (piece == V.KING && move.appear.length > 0) {
-      this.kingPos[c][0] = move.appear[0].x;
-      this.kingPos[c][1] = move.appear[0].y;
+      this.kingPos[c] = [move.appear[0].x, move.appear[0].y];
       this.castleFlags[c] = [V.size.y, V.size.y];
       return;
     }
     const oppCol = V.GetOppCol(c);
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      // Opponent's king is captured, game over
+      this.kingPos[oppCol] = [-1, -1];
     const oppFirstRank = V.size.x - 1 - firstRank;
     if (
       move.start.x == firstRank && //our rook moves?
@@ -152,7 +137,8 @@ export class Doublemove2Rules extends ChessRules {
     ) {
       const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
       this.castleFlags[c][flagIdx] = V.size.y;
-    } else if (
+    }
+    else if (
       move.end.x == oppFirstRank && //we took opponent rook?
       this.castleFlags[oppCol].includes(move.end.y)
     ) {
@@ -175,28 +161,47 @@ export class Doublemove2Rules extends ChessRules {
       this.turn = V.GetOppCol(this.turn);
     }
     if (this.movesCount > 0) this.subTurn = 3 - this.subTurn;
-    super.postUndo(move);
+    this.postUndo(move);
   }
 
-  static get VALUES() {
-    return {
-      p: 1,
-      r: 5,
-      n: 3,
-      b: 3,
-      q: 7, //slightly less than in orthodox game
-      k: 1000
-    };
+  postUndo(move) {
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      // Opponent's king was captured
+      this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
+    super.postUndo(move);
   }
 
-  // No alpha-beta here, just adapted min-max at depth 1(+1)
+  // No alpha-beta here, just adapted min-max at depth 2(+1)
   getComputerMove() {
+    const maxeval = V.INFINITY;
     const color = this.turn;
+    const oppCol = V.GetOppCol(this.turn);
+
+    // Search best (half) move for opponent turn
+    const getBestMoveEval = () => {
+      let score = this.getCurrentScore();
+      if (score != "*") return maxeval * (score == "1-0" ? 1 : -1);
+      let moves = this.getAllValidMoves();
+      let res = oppCol == "w" ? -maxeval : maxeval;
+      for (let m of moves) {
+        this.play(m);
+        score = this.getCurrentScore();
+        if (score != "*") {
+          // King captured
+          this.undo(m);
+          return maxeval * (score == "1-0" ? 1 : -1);
+        }
+        const evalPos = this.evalPosition();
+        res = oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos);
+        this.undo(m);
+      }
+      return res;
+    };
+
     const moves11 = this.getAllValidMoves();
     if (this.movesCount == 0)
       // First white move at random:
       return moves11[randInt(moves11.length)];
-
     let doubleMoves = [];
     // Rank moves using a min-max at depth 2
     for (let i = 0; i < moves11.length; i++) {
@@ -206,7 +211,8 @@ export class Doublemove2Rules extends ChessRules {
         this.play(moves12[j]);
         doubleMoves.push({
           moves: [moves11[i], moves12[j]],
-          eval: this.evalPosition()
+          // Small fluctuations to uniformize play a little
+          eval: getBestMoveEval() + 0.05 - Math.random() / 10
         });
         this.undo(moves12[j]);
       }