Fix Eightpieces, add some simple variants, add a basic variants classification instea...
[vchess.git] / client / src / variants / Doublemove2.js
index 26428f1..f9d5e4f 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() {
+    // 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) {
@@ -127,7 +110,13 @@ export class Doublemove2Rules extends ChessRules {
     else {
       this.epSquares.push([epSq]);
       this.movesCount++;
-      if (this.movesCount == 1) this.turn = "b";
+      if (
+        this.movesCount == 1 ||
+        // King is captured at subTurn 1?
+        (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      ) {
+        this.turn = "b";
+      }
     }
     if (this.movesCount > 1) this.subTurn = 3 - this.subTurn;
     this.postPlay(move);
@@ -138,13 +127,17 @@ export class Doublemove2Rules extends ChessRules {
     const piece = move.vanish[0].p;
     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;
+    if (piece == V.KING) {
+      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];
+      move.captureKing = true; //for undo
+    }
     const oppFirstRank = V.size.x - 1 - firstRank;
     if (
       move.start.x == firstRank && //our rook moves?
@@ -152,7 +145,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 (
+    }
+    if (
       move.end.x == oppFirstRank && //we took opponent rook?
       this.castleFlags[oppCol].includes(move.end.y)
     ) {
@@ -164,7 +158,7 @@ export class Doublemove2Rules extends ChessRules {
   undo(move) {
     this.disaggregateFlags(JSON.parse(move.flags));
     V.UndoOnBoard(this.board, move);
-    if (this.subTurn == 2 || this.movesCount == 1) {
+    if (this.subTurn == 2 || this.movesCount == 1 || !!move.captureKing) {
       this.epSquares.pop();
       this.movesCount--;
       if (this.movesCount == 0) this.turn = "w";
@@ -175,18 +169,14 @@ 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 2(+1)
@@ -198,24 +188,16 @@ export class Doublemove2Rules extends ChessRules {
     // Search best (half) move for opponent turn
     const getBestMoveEval = () => {
       let score = this.getCurrentScore();
-      if (score != "*") {
-        if (score == "1/2") return 0;
-        return maxeval * (score == "1-0" ? 1 : -1);
-      }
+      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();
-        // Now turn is oppCol,2
         if (score != "*") {
-          if (score == "1/2")
-            res = oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0);
-          else {
-            // King captured
-            this.undo(m);
-            return maxeval * (score == "1-0" ? 1 : -1);
-          }
+          // 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);
@@ -224,34 +206,32 @@ export class Doublemove2Rules extends ChessRules {
       return res;
     };
 
-    let moves11 = this.getAllValidMoves();
-    let doubleMoves = [];
-    // Rank moves using a min-max at depth 2(+1)
+    const moves11 = this.getAllValidMoves();
+    if (this.movesCount == 0)
+      // First white move at random:
+      return moves11[randInt(moves11.length)];
+    let doubleMove = null;
+    let bestEval = Number.POSITIVE_INFINITY * (color == 'w' ? -1 : 1);
+    // Rank moves using a min-max at depth 2
     for (let i = 0; i < moves11.length; i++) {
       this.play(moves11[i]);
-      let moves12 = this.getAllValidMoves();
+      const moves12 = this.getAllValidMoves();
       for (let j = 0; j < moves12.length; j++) {
         this.play(moves12[j]);
-        doubleMoves.push({
-          moves: [moves11[i], moves12[j]],
-          eval: getBestMoveEval()
-        });
+        // Small fluctuations to uniformize play a little
+        const evalM = getBestMoveEval() + 0.05 - Math.random() / 10
+        if (
+          (color == 'w' && evalM > bestEval) ||
+          (color == 'b' && evalM < bestEval)
+        ) {
+          doubleMove = [moves11[i],  moves12[j]];
+          bestEval = evalM;
+        }
         this.undo(moves12[j]);
       }
       this.undo(moves11[i]);
     }
-
-    doubleMoves.sort((a, b) => {
-      return (color == "w" ? 1 : -1) * (b.eval - a.eval);
-    });
-    let candidates = [0]; //indices of candidates moves
-    for (
-      let i = 1;
-      i < doubleMoves.length && doubleMoves[i].eval == doubleMoves[0].eval;
-      i++
-    ) {
-      candidates.push(i);
-    }
-    return doubleMoves[randInt(candidates.length)].moves;
+    // TODO: not always the best move played (why ???)
+    return doubleMove;
   }
 };