Fix Benedict-Dark
[xogo.git] / base_rules.js
index 2d95f6c..ded104e 100644 (file)
@@ -118,13 +118,13 @@ export default class ChessRules {
   ////////////////////
   // COORDINATES UTILS
 
-  // a3 --> {x:10, y:3}
+  // 3a --> {x:3, y:10}
   static SquareToCoords(sq) {
     return ArrayFun.toObject(["x", "y"],
                              [0, 1].map(i => parseInt(sq[i], 36)));
   }
 
-  // {x:1, y:12} --> 1c
+  // {x:11, y:12} --> bc
   static CoordsToSquare(cd) {
     return Object.values(cd).map(c => c.toString(36)).join("");
   }
@@ -162,12 +162,12 @@ export default class ChessRules {
 
   // Turn "wb" into "B" (for FEN)
   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)
   fen2board(f) {
-    return f.charCodeAt(0) <= 90 ? "w" + f.toLowerCase() : "b" + f;
+    return (f.charCodeAt(0) <= 90 ? "w" + f.toLowerCase() : "b" + f);
   }
 
   // Setup the initial random-or-not (asymmetric-or-not) position
@@ -672,7 +672,7 @@ export default class ChessRules {
     }
     else
       this.r_pieces = { 'w': {}, 'b': {} };
-    let chessboard = 
+    let chessboard =
       document.getElementById(this.containerId).querySelector(".chessboard");
     if (!r)
       r = chessboard.getBoundingClientRect();
@@ -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...
   }
 
@@ -1065,13 +1065,7 @@ export default class ChessRules {
 
   // Am I allowed to move thing at square x,y ?
   canIplay(x, y) {
-    return (
-      this.playerColor == this.turn &&
-      (
-        (typeof x == "number" && this.getColor(x, y) == this.turn) ||
-        (typeof x == "string" && x == this.turn) //reserve
-      )
-    );
+    return (this.playerColor == this.turn && this.getColor(x, y) == this.turn);
   }
 
   ////////////////////////
@@ -1342,7 +1336,7 @@ export default class ChessRules {
           }
         }
         if (!this.options["rifle"])
-          m.appear.pop(); //nothin appears
+          m.appear.pop(); //nothing appears
       }
     });
   }
@@ -1352,7 +1346,6 @@ export default class ChessRules {
     const lastRank = (color == "w" ? 0 : this.size.x - 1);
     const initPiece = this.getPiece(moves[0].start.x, moves[0].start.y);
     moves.forEach(m => {
-      let finalPieces = ["p"];
       const [x1, y1] = [m.start.x, m.start.y];
       const [x2, y2] = [m.end.x, m.end.y];
       const promotionOk = (
@@ -1361,34 +1354,30 @@ export default class ChessRules {
       );
       if (!promotionOk)
         return; //nothing to do
-      if (!this.options["pawnfall"]) {
-        if (
-          this.options["cannibal"] &&
-          this.board[x2][y2] != "" &&
-          this.getColor(x2, y2) == oppCol
-        ) {
-          finalPieces = [this.getPieceType(x2, y2)];
-        }
-        else
-          finalPieces = this.pawnPromotions;
+      if (this.options["pawnfall"]) {
+        m.appear.shift();
+        return;
       }
+      let finalPieces = ["p"];
+      if (
+        this.options["cannibal"] &&
+        this.board[x2][y2] != "" &&
+        this.getColor(x2, y2) == oppCol
+      ) {
+        finalPieces = [this.getPieceType(x2, y2)];
+      }
+      else
+        finalPieces = this.pawnPromotions;
       m.appear[0].p = finalPieces[0];
       if (initPiece == "!") //cannibal king-pawn
         m.appear[0].p = C.CannibalKingCode[finalPieces[0]];
       for (let i=1; i<finalPieces.length; i++) {
         const piece = finalPieces[i];
-        let tr = null;
-        if (!this.options["pawnfall"]) {
-          tr = {
-            c: color,
-            p: (initPiece != "!" ? piece : C.CannibalKingCode[piece])
-          };
-        }
+        const tr = {
+          c: color,
+          p: (initPiece != "!" ? piece : C.CannibalKingCode[piece])
+        };
         let newMove = this.getBasicMove([x1, y1], [x2, y2], tr);
-        if (this.options["pawnfall"]) {
-          newMove.appear.shift();
-          newMove.pawnfall = true; //required in prePlay()
-        }
         moreMoves.push(newMove);
       }
     });
@@ -1406,7 +1395,6 @@ export default class ChessRules {
         m.appear[0].x == m.start.x &&
         m.appear[0].y == m.start.y
       ) {
-        const promotionPiece0 = this.pawnSpecs.promotions[0];
         m.appear[0].p = this.pawnPromotions[0];
         for (let i=1; i<this.pawnPromotions.length; i++) {
           let newMv = JSON.parse(JSON.stringify(m));
@@ -1425,7 +1413,8 @@ export default class ChessRules {
       "#": "r",
       "$": "n",
       "%": "b",
-      "*": "q"
+      "*": "q",
+      "k": "k"
     };
   }
 
@@ -1441,10 +1430,7 @@ export default class ChessRules {
   }
 
   isKing(symbol) {
-    return (
-      symbol == 'k' ||
-      (this.options["cannibal"] && C.CannibalKings[symbol])
-    );
+    return !!C.CannibalKings[symbol];
   }
 
   // For Madrasi:
@@ -1540,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++) {
@@ -1549,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) {
@@ -1641,10 +1625,6 @@ export default class ChessRules {
           p: this.getPiece(ex, ey)
         })
       );
-      if (this.options["rifle"])
-        // Rifle captures are tricky in combination with Atomic etc,
-        // so it's useful to mark the move :
-        mv.capture = true;
       if (this.options["cannibal"] && destColor != initColor) {
         const lastIdx = mv.vanish.length - 1;
         let trPiece = mv.vanish[lastIdx].p;
@@ -1878,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];
@@ -1914,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) {
@@ -1948,31 +1932,26 @@ export default class ChessRules {
 
   prePlay(move) {
     if (
-      typeof move.start.x == "number" &&
-      (!this.options["teleport"] || this.subTurnTeleport == 1)
+      this.hasCastle &&
+      // If flags already off, no need to re-check:
+      Object.keys(this.castleFlags).some(c => {
+        return this.castleFlags[c].some(val => val < this.size.y)})
     ) {
-      // OK, not a drop move
-      if (
-        this.hasCastle &&
-        // If flags already off, no need to re-check:
-        Object.keys(this.castleFlags).some(c => {
-          return this.castleFlags[c].some(val => val < this.size.y)})
-      ) {
-        this.updateCastleFlags(move);
-      }
-      const initSquare = C.CoordsToSquare(move.start);
-      if (
-        this.options["crazyhouse"] &&
-        (!this.options["rifle"] || !move.capture)
-      ) {
+      this.updateCastleFlags(move);
+    }
+    if (this.options["crazyhouse"]) {
+      move.vanish.forEach(v => {
+        const square = C.CoordsToSquare({x: v.x, y: v.y});
+        if (this.ispawn[square])
+          delete this.ispawn[square];
+      });
+      if (move.appear.length > 0 && move.vanish.length > 0) {
+        // Assumption: something is moving
+        const initSquare = C.CoordsToSquare(move.start);
         const destSquare = C.CoordsToSquare(move.end);
-        if (this.ispawn[initSquare]) {
-          delete this.ispawn[initSquare];
-          this.ispawn[destSquare] = true;
-        }
-        else if (
-          move.vanish[0].p == "p" &&
-          move.appear[0].p != "p"
+        if (
+          this.ispawn[initSquare] ||
+          (move.vanish[0].p == "p" && move.appear[0].p != "p")
         ) {
           this.ispawn[destSquare] = true;
         }
@@ -1986,18 +1965,28 @@ export default class ChessRules {
       }
     }
     const minSize = Math.min(move.appear.length, move.vanish.length);
-    if (this.hasReserve && !move.pawnfall) {
+    if (
+      this.hasReserve &&
+      // Warning; atomic pawn removal isn't a capture
+      (!this.options["atomic"] || !this.rempawn || this.movesCount >= 1)
+    ) {
       const color = this.turn;
       for (let i=minSize; i<move.appear.length; i++) {
         // Something appears = dropped on board (some exceptions, Chakart...)
-        const piece = move.appear[i].p;
-        this.updateReserve(color, piece, this.reserve[color][piece] - 1);
+        if (move.appear[i].c == color) {
+          const piece = move.appear[i].p;
+          this.updateReserve(color, piece, this.reserve[color][piece] - 1);
+        }
       }
       for (let i=minSize; i<move.vanish.length; i++) {
         // Something vanish: add to reserve except if recycle & opponent
-        const piece = move.vanish[i].p;
-        if (this.options["crazyhouse"] || move.vanish[i].c == color)
+        if (
+          this.options["crazyhouse"] ||
+          (this.options["recycle"] && move.vanish[i].c == color)
+        ) {
+          const piece = move.vanish[i].p;
           this.updateReserve(color, piece, this.reserve[color][piece] + 1);
+        }
       }
     }
   }
@@ -2104,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
@@ -2154,6 +2142,10 @@ export default class ChessRules {
       return;
     }
     let movingPiece = this.getDomPiece(move.start.x, move.start.y);
+    if (!movingPiece) { //TODO this shouldn't be required
+      callback();
+      return;
+    }
     const initTransform = movingPiece.style.transform;
     let chessboard =
       document.getElementById(this.containerId).querySelector(".chessboard");
@@ -2180,18 +2172,10 @@ export default class ChessRules {
       const [i2, j2] = move.segments[index][1];
       const dep = this.getPixelPosition(i1, j1, r);
       const arr = this.getPixelPosition(i2, j2, r);
-
-console.log(dep,arr); //TODO: this seems right, but translations don't work well.
-
-      // Start from i1, j1:
-      movingPiece.style.transform =
-        `translate(${dep[0] - ix}px, ${dep[1] - iy}px)`;
-      movingPiece.style.transitionDuration = "0s";
       const distance =
         Math.sqrt((arr[0] - dep[0]) ** 2 + (arr[1] - dep[1]) ** 2);
       const duration = 0.2 + (distance / maxDist) * 0.3;
-      movingPiece.style.transform =
-        `translate(${arr[0] - dep[0]}px, ${arr[1] - dep[1]}px)`;
+      movingPiece.style.transform = `translate(${arr[0]}px, ${arr[1]}px)`;
       movingPiece.style.transitionDuration = duration + "s";
       setTimeout(cb, duration * 1000);
     };