Fix Benedict-Dark
authorBenjamin Auder <benjamin.auder@somewhere>
Wed, 25 May 2022 10:03:04 +0000 (12:03 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Wed, 25 May 2022 10:03:04 +0000 (12:03 +0200)
base_rules.js
variants/Balanced/rules.html
variants/Benedict/class.js

index 4805f17..ded104e 100644 (file)
@@ -748,13 +748,13 @@ export default class ChessRules {
     for (let x=0; x<this.size.x; x++) {
       for (let y=0; y<this.size.y; y++) {
         if (!this.enlightened[x][y] && this.oldEnlightened[x][y]) {
-          let elt = document.getElementById(this.coordsToId(x, y));
+          let elt = document.getElementById(this.coordsToId({x: x, y: y}));
           elt.classList.add("in-shadow");
           if (this.g_pieces[x][y])
             this.g_pieces[x][y].classList.add("hidden");
         }
         else if (this.enlightened[x][y] && !this.oldEnlightened[x][y]) {
-          let elt = document.getElementById(this.coordsToId(x, y));
+          let elt = document.getElementById(this.coordsToId({x: x, y: y}));
           elt.classList.remove("in-shadow");
           if (this.g_pieces[x][y])
             this.g_pieces[x][y].classList.remove("hidden");
@@ -1043,7 +1043,7 @@ export default class ChessRules {
 
   // Piece type on square (i,j)
   getPieceType(i, j) {
-    const p = (typeof i == "string" ? j : this.board[i][j].charAt(1));
+    const p = this.getPiece(i, j);
     return C.CannibalKings[p] || p; //a cannibal king move as...
   }
 
@@ -1413,7 +1413,8 @@ export default class ChessRules {
       "#": "r",
       "$": "n",
       "%": "b",
-      "*": "q"
+      "*": "q",
+      "k": "k"
     };
   }
 
@@ -1429,10 +1430,7 @@ export default class ChessRules {
   }
 
   isKing(symbol) {
-    return (
-      symbol == 'k' ||
-      (this.options["cannibal"] && C.CannibalKings[symbol])
-    );
+    return !!C.CannibalKings[symbol];
   }
 
   // For Madrasi:
@@ -1528,7 +1526,6 @@ export default class ChessRules {
     let moves = [];
     // Find reverse captures (opponent takes)
     const color = this.getColor(x, y);
-    const pieceType = this.getPieceType(x, y);
     const oppCol = C.GetOppCol(color);
     for (let i=0; i<this.size.x; i++) {
       for (let j=0; j<this.size.y; j++) {
@@ -1537,10 +1534,9 @@ export default class ChessRules {
           this.canTake([i, j], [x, y]) &&
           !this.isImmobilized([i, j])
         ) {
-          const piece = this.getPieceType(i, j);
-          if (zen && C.CannibalKingCode[piece])
+          if (zen && this.isKing(this.getPiece(i, j)))
             continue; //king not captured in this way
-          const stepSpec = this.pieces(oppCol, i, j)[piece];
+          const stepSpec = this.pieces(oppCol, i, j)[this.getPieceType(i, j)];
           const attacks = stepSpec.attack || stepSpec.moves;
           for (let a of attacks) {
             for (let s of a.steps) {
@@ -1862,12 +1858,12 @@ export default class ChessRules {
         let square = kingPos,
             res = true; //a priori valid
         if (m.vanish.some(v => {
-          return (v.p == "k" || C.CannibalKings[v.p]) && v.c == color;
+          return C.CannibalKings[v.p] && v.c == color;
         })) {
           // Search king in appear array:
           const newKingIdx =
             m.appear.findIndex(a => {
-              return (a.p == "k" || C.CannibalKings[a.p]) && a.c == color;
+              return C.CannibalKings[a.p] && a.c == color;
             });
           if (newKingIdx >= 0)
             square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y];
@@ -1898,13 +1894,17 @@ export default class ChessRules {
 
   // Apply a move on board
   playOnBoard(move) {
-    for (let psq of move.vanish) this.board[psq.x][psq.y] = "";
-    for (let psq of move.appear) this.board[psq.x][psq.y] = psq.c + psq.p;
+    for (let psq of move.vanish)
+      this.board[psq.x][psq.y] = "";
+    for (let psq of move.appear)
+      this.board[psq.x][psq.y] = psq.c + psq.p;
   }
   // Un-apply the played move
   undoOnBoard(move) {
-    for (let psq of move.appear) this.board[psq.x][psq.y] = "";
-    for (let psq of move.vanish) this.board[psq.x][psq.y] = psq.c + psq.p;
+    for (let psq of move.appear)
+      this.board[psq.x][psq.y] = "";
+    for (let psq of move.vanish)
+      this.board[psq.x][psq.y] = psq.c + psq.p;
   }
 
   updateCastleFlags(move) {
@@ -2093,7 +2093,6 @@ export default class ChessRules {
     return (color == "w" ? "0-1" : "1-0");
   }
 
-  // NOTE: quite suboptimal for eg. Benedict (not a big deal I think)
   playVisual(move, r) {
     move.vanish.forEach(v => {
       // TODO: next "if" shouldn't be required
index e7bb01e..6d5fa49 100644 (file)
@@ -1,3 +1,3 @@
-<p>White plays first, thenn Black plays two moves, then White plays two moves, and after that the game proceeds normally.</p>
+<p>White plays first, then Black plays two moves, then White plays two moves, and after that the game proceeds normally.</p>
 
 <p>See <a href="https://arxiv.org/abs/2108.02547">the (draft) article</a>.</p>
index 7c77e8b..88609d7 100644 (file)
@@ -22,10 +22,6 @@ export default class BenedictRules extends ChessRules {
     return false;
   }
 
-  canTake() {
-    return false;
-  }
-
   // Find potential captures from a square
   // follow steps from x,y until something is met.
   findAttacks([x, y]) {
@@ -39,54 +35,96 @@ export default class BenedictRules extends ChessRules {
         let [i, j] = [x + step[0], this.computeY(y + step[1])];
         let nbSteps = 1;
         while (this.onBoard(i, j) && this.board[i][j] == "") {
-          if (a.range <= nbSteps++) continue outerLoop;
+          if (a.range <= nbSteps++)
+            continue outerLoop;
           i += step[0];
           j = this.computeY(j + step[1]);
         }
-        if (this.onBoard(i, j) && this.getColor(i, j) == oppCol)
+        if (
+          this.onBoard(i, j) && this.getColor(i, j) == oppCol &&
+          (!this.options["zen"] || this.getPieceType(i, j) == "k")
+        ) {
           squares[C.CoordsToSquare({x: i, y: j})] = true;
+        }
       }
     }
     return Object.keys(squares);
   }
 
   postProcessPotentialMoves(moves) {
-    if (moves.length == 0) return moves;
-    const [x, y] = [moves[0].end.x, moves[0].end.y];
+    if (moves.length == 0)
+      return moves;
     const color = this.getColor(moves[0].start.x, moves[0].start.y);
     const oppCol = C.GetOppCol(color);
-    moves = super.postProcessPotentialMoves(moves);
+    // Remove captures (NOTE: altering canTake has side effects,
+    // Benedict is still based on captures even if they are forbidden):
+    moves = super.postProcessPotentialMoves(moves)
+                 .filter(m => this.board[m.end.x][m.end.y] == "");
     moves.forEach(m => {
-      this.playOnBoard(m);
-      let attacks;
+      super.playOnBoard(m);
+      let attacks = this.findAttacks([m.end.x, m.end.y])
       if (this.options["zen"]) {
         let endSquares = {};
-        super.getZenCaptures(x, y).forEach(c => {
+        super.findCapturesOn([m.end.x, m.end.y], true).forEach(c => {
           endSquares[C.CoordsToSquare(c.end)] = true;
         });
-        attacks = Object.keys(endSquares);
+        Array.prototype.push.apply(attacks, Object.keys(endSquares));
       }
-      else attacks = this.findAttacks([m.end.x, m.end.y])
-      this.undoOnBoard(m);
+      super.undoOnBoard(m);
+      m.flips = [];
       attacks.map(C.SquareToCoords).forEach(a => {
-        const p = this.getPiece(a.x, a.y);
-        m.appear.push(new PiPo({x: a.x, y: a.y, c: color, p: p}));
-        m.vanish.push(new PiPo({x: a.x, y: a.y, c: oppCol, p: p}));
+        m.flips.push({x: a.x, y: a.y});
       });
     });
     return moves;
   }
 
-  // Moves cannot flip our king's color, so (almost) all are valid
+  playOnBoard(move) {
+    super.playOnBoard(move);
+    this.flipColorOf(move.flips);
+  }
+  undoOnBoard(move) {
+    super.undoOnBoard(move);
+    this.flipColorOf(move.flips);
+  }
+
+  flipColorOf(flips) {
+    for (let xy of flips) {
+      const newColor = C.GetOppCol(this.getColor(xy.x, xy.y));
+      this.board[xy.x][xy.y] = newColor + this.board[xy.x][xy.y][1];
+    }
+  }
+
+  postPlay(move) {
+    if (this.options["balance"] && [1, 3].includes(this.movesCount)) {
+      // If enemy king is flipped: game over
+      const oppCol = C.GetOppCol(move.vanish[0].c);
+      const oppKingPos = this.searchKingPos(oppCol);
+      if (oppKingPos[0] < 0) {
+        this.turn = oppCol;
+        this.movesCount++;
+        return;
+      }
+    }
+    super.postPlay(move);
+  }
+
+  // Moves cannot flip our king's color, so all are valid
   filterValid(moves) {
-    if (this.options["balance"] && [1, 3].includes(this.movesCount))
-      return moves.filter(m => m.vanish.every(v => v.p != C.KING));
     return moves;
   }
 
-  // Since it's used just for the king, and there are no captures:
+  // A king under (regular) check flips color, and the game is over.
   underCheck(square, color) {
     return false;
   }
 
+  playVisual(move, r) {
+    super.playVisual(move, r);
+    move.flips.forEach(f => {
+      this.g_pieces[f.x][f.y].classList.toggle("white");
+      this.g_pieces[f.x][f.y].classList.toggle("black");
+    });
+  }
+
 };