Some Chakart fixes/adjustments
[vchess.git] / client / src / variants / Chakart.js
index 5f20132..86a867a 100644 (file)
@@ -30,22 +30,6 @@ export class ChakartRules extends ChessRules {
     return true;
   }
 
-  hoverHighlight([x, y]) {
-    if (this.subTurn == 1) return false;
-    const L = this.firstMove.length;
-    const fm = this.firstMove[L-1];
-    if (fm.end.effect != 0) return false;
-    const deltaX = Math.abs(fm.appear[0].x - x);
-    const deltaY = Math.abs(fm.appear[0].y - y);
-    return (
-      (this.board[x][y] == V.EMPTY || this.getColor(x, y) == 'a') &&
-      (
-        (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) ||
-        (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1)
-      )
-    );
-  }
-
   static get IMMOBILIZE_CODE() {
     return {
       'p': 's',
@@ -236,7 +220,7 @@ export class ChakartRules extends ChessRules {
         [V.KING]: captured[11]
       }
     };
-    this.firstMove = [];
+    this.effects = [];
     this.subTurn = 1;
   }
 
@@ -323,11 +307,10 @@ export class ChakartRules extends ChessRules {
     }
     else {
       // Subturn == 2
-      const L = this.firstMove.length;
-      const fm = this.firstMove[L-1];
-      switch (fm.end.effect) {
+      const L = this.effects.length;
+      switch (this.effects[L-1]) {
         case "kingboo":
-          // Exchange position with any piece,
+          // Exchange position with any visible piece,
           // except pawns if arriving on last rank.
           const lastRank = { 'w': 0, 'b': 7 };
           const color = this.turn;
@@ -335,12 +318,13 @@ export class ChakartRules extends ChessRules {
           for (let i=0; i<8; i++) {
             for (let j=0; j<8; j++) {
               const colIJ = this.getColor(i, j);
+              const pieceIJ = this.getPiece(i, j);
               if (
                 (i != x || j != y) &&
                 this.board[i][j] != V.EMPTY &&
+                pieceIJ != V.INVISIBLE_QUEEN &&
                 colIJ != 'a'
               ) {
-                const pieceIJ = this.getPiece(i, j);
                 if (
                   (pieceIJ != V.PAWN || x != lastRank[colIJ]) &&
                   (allowLastRank || i != lastRank[color])
@@ -364,72 +348,21 @@ export class ChakartRules extends ChessRules {
           if (x >= V.size.x) moves = this.getReserveMoves([x, y]);
           break;
         case "daisy":
-          // Play again with the same piece
-          if (fm.appear[0].x == x && fm.appear[0].y == y)
-            moves = super.getPotentialMovesFrom([x, y]);
+          // Play again with any piece
+          moves = super.getPotentialMovesFrom([x, y]);
           break;
       }
     }
     return moves;
   }
 
-  // Helper for getBasicMove()
+  // Helper for getBasicMove(): banana/bomb effect
   getRandomSquare([x, y], steps) {
-    const color = this.turn;
-    const validSteps = steps.filter(s => {
-      const [i, j] = [x + s[0], y + s[1]];
-      return (
-        V.OnBoard(i, j) &&
-        (this.board[i][j] == V.EMPTY || this.getColor(i, j) != color)
-      );
-    });
-    if (validSteps.length == 0)
-      // Can happen after mushroom jump
-      return [x, y];
+    const validSteps = steps.filter(s => V.OnBoard(x + s[0], y + s[1]));
     const step = validSteps[randInt(validSteps.length)];
     return [x + step[0], y + step[1]];
   }
 
-  canMove([x, y], piece) {
-    const color = this.getColor(x, y);
-    const oppCol = V.GetOppCol(color);
-    piece = piece || this.getPiece(x, y);
-    if (piece == V.PAWN) {
-      const forward = (color == 'w' ? -1 : 1);
-      return (
-        V.OnBoard(x + forward, y) &&
-        (
-          this.board[x + forward][y] == V.EMPTY ||
-          (
-            V.OnBoard(x + forward, y + 1) &&
-            this.board[x + forward][y + 1] != V.EMPTY &&
-            this.getColor[x + forward, y + 1] == oppCol
-          ) ||
-          (
-            V.OnBoard(x + forward, y - 1) &&
-            this.board[x + forward][y - 1] != V.EMPTY &&
-            this.getColor[x + forward, y - 1] == oppCol
-          )
-        )
-      );
-    }
-    // Checking one step is enough:
-    const steps =
-      [V.KING, V.QUEEN].includes(piece)
-        ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
-        : V.steps[piece];
-    for (let step of steps) {
-      const [i, j] = [x + step[0], y + step[1]];
-      if (
-        V.OnBoard(i, j) &&
-        (this.board[i][j] == V.EMPTY || this.getColor(i, j) != color)
-      ) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   // Apply mushroom, bomb or banana effect (hidden to the player).
   // Determine egg effect, too, and apply its first part if possible.
   getBasicMove_aux(psq1, sq2, tr, initMove) {
@@ -473,11 +406,11 @@ export class ChakartRules extends ChessRules {
     // The move starts normally, on board:
     let move = super.getBasicMove([x1, y1], [x2, y2], tr);
     if (!!tr) move.promoteInto = tr.c + tr.p; //in case of (chomped...)
-    const L = this.firstMove.length;
+    const L = this.effects.length;
     if (
       [V.PAWN, V.KNIGHT].includes(piece1) &&
       !!initMove &&
-      (this.subTurn == 1 || this.firstMove[L-1].end.effect == "daisy")
+      (this.subTurn == 1 || this.effects[L-1] == "daisy")
     ) {
       switch (piece1) {
         case V.PAWN: {
@@ -545,12 +478,13 @@ export class ChakartRules extends ChessRules {
       const oppLastRank = (color == 'w' ? 7 : 0);
       for (let i=0; i<8; i++) {
         for (let j=0; j<8; j++) {
+          const piece = this.getPiece(i, j);
           if (
             (i != move.vanish[0].x || j != move.vanish[0].y) &&
             this.board[i][j] != V.EMPTY &&
+            piece != V.INVISIBLE_QUEEN &&
             this.getColor(i, j) == color
           ) {
-            const piece = this.getPiece(i, j);
             if (piece != V.KING && (piece != V.PAWN || i != oppLastRank))
               pieces.push({ x: i, y: j, p: piece });
           }
@@ -585,27 +519,10 @@ export class ChakartRules extends ChessRules {
         // No egg effects at subTurn 2
         return;
       // 1) Determine the effect (some may be impossible)
-      let effects = ["kingboo", "koopa", "chomp", "bowser"];
+      let effects = ["kingboo", "koopa", "chomp", "bowser", "daisy"];
       if (Object.values(this.captured[color1]).some(c => c >= 1))
         effects.push("toadette");
       const lastRank = { 'w': 0, 'b': 7 };
-      let canPlayAgain = undefined;
-      if (
-        move.appear[0].p == V.PAWN &&
-        move.appear[0].x == lastRank[color1]
-      ) {
-        // Always possible: promote into a queen, rook or king
-        canPlayAgain = true;
-      }
-      else {
-        move.end.effect = "daisy";
-        V.PlayOnBoard(this.board, move);
-        const square = [move.appear[0].x, move.appear[0].y];
-        canPlayAgain = this.canMove(square, piece1);
-        V.UndoOnBoard(this.board, move);
-        delete move.end["effect"];
-      }
-      if (canPlayAgain) effects.push("daisy");
       if (
         this.board.some((b,i) =>
           b.some(cell => {
@@ -671,17 +588,22 @@ export class ChakartRules extends ChessRules {
         ];
         if (
           V.OnBoard(i, j) &&
-          (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
+          (
+            this.board[i][j] == V.EMPTY ||
+            this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
+            this.getColor(i, j) == 'a'
+          )
         ) {
           move.appear[0].x = i;
           move.appear[0].y = j;
           if (this.board[i][j] != V.EMPTY) {
             const object = this.getPiece(i, j);
+            const color = this.getColor(i, j);
             move.vanish.push(
               new PiPo({
                 x: i,
                 y: j,
-                c: 'a',
+                c: color,
                 p: object
               })
             );
@@ -711,11 +633,12 @@ export class ChakartRules extends ChessRules {
         if (
           V.OnBoard(next[0], next[1]) &&
           this.board[next[0]][next[1]] != V.EMPTY &&
+          this.getPiece(next[0], next[1]) != V.INVISIBLE_QUEEN &&
           this.getColor(next[0], next[1]) != 'a'
         ) {
           const afterNext = [next[0] + step[0], next[1] + step[1]];
           if (V.OnBoard(afterNext[0], afterNext[1])) {
-            const afterColor = this.getColor(afterNext[0], afterNext[1])
+            const afterColor = this.getColor(afterNext[0], afterNext[1]);
             if (
               this.board[afterNext[0]][afterNext[1]] == V.EMPTY ||
               afterColor != color1
@@ -790,13 +713,15 @@ export class ChakartRules extends ChessRules {
             const [i, j] = [finalSquare[0] + s[0], finalSquare[1] + s[1]];
             return (
               V.OnBoard(i, j) &&
+              // NOTE: do not place a bomb or banana on the invisible queen!
               (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
             );
           });
         if (validSteps.length >= 1) {
+          const randIdx = randInt(validSteps.length);
           const [x, y] = [
-            finalSquare[0] + validSteps[0][0],
-            finalSquare[1] + validSteps[0][1]
+            finalSquare[0] + validSteps[randIdx][0],
+            finalSquare[1] + validSteps[randIdx][1]
           ];
           move.appear.push(
             new PiPo({
@@ -896,14 +821,16 @@ export class ChakartRules extends ChessRules {
     let moves = [];
     if (
       this.board[x + shiftX][y] == V.EMPTY ||
-      this.getColor(x + shiftX, y) == 'a'
+      this.getColor(x + shiftX, y) == 'a' ||
+      this.getPiece(x + shiftX, y) == V.INVISIBLE_QUEEN
     ) {
       this.addPawnMoves([x, y], [x + shiftX, y], moves);
       if (
         [firstRank, firstRank + shiftX].includes(x) &&
         (
           this.board[x + 2 * shiftX][y] == V.EMPTY ||
-          this.getColor(x + 2 * shiftX, y) == 'a'
+          this.getColor(x + 2 * shiftX, y) == 'a' ||
+          this.getPiece(x + 2 * shiftX, y) == V.INVISIBLE_QUEEN
         )
       ) {
         moves.push(this.getBasicMove({ x: x, y: y }, [x + 2 * shiftX, y]));
@@ -914,6 +841,8 @@ export class ChakartRules extends ChessRules {
         y + shiftY >= 0 &&
         y + shiftY < sizeY &&
         this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+        // Pawns cannot capture invisible queen this way!
+        this.getPiece(x + shiftX, y + shiftY) != V.INVISIBLE_QUEEN &&
         ['a', oppCol].includes(this.getColor(x + shiftX, y + shiftY))
       ) {
         this.addPawnMoves([x, y], [x + shiftX, y + shiftY], moves);
@@ -955,6 +884,7 @@ export class ChakartRules extends ChessRules {
           V.OnBoard(i, j) &&
           (
             this.board[i][j] == V.EMPTY ||
+            this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
             (
               this.getColor(i, j) == 'a' &&
               [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
@@ -1017,66 +947,42 @@ export class ChakartRules extends ChessRules {
   getAllPotentialMoves() {
     if (this.subTurn == 1) return super.getAllPotentialMoves();
     let moves = [];
-    const L = this.firstMove.length;
-    const fm = this.firstMove[L-1];
-    switch (fm.end.effect) {
-      case 0:
-        moves.push({
-          start: { x: -1, y: -1 },
-          end: { x: -1, y: -1 },
-          appear: [],
-          vanish: []
-        });
-        for (
-          let step of
-          (fm.vanish[0].p == V.ROOK ? V.steps[V.BISHOP] : V.steps[V.ROOK])
-        ) {
-          const [i, j] = [fm.appear[0].x + step[0], fm.appear[0].y + step[1]];
-          if (
-            V.OnBoard(i, j) &&
-            (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
-          ) {
-            let m = new Move({
-              start: { x: -1, y: -1 },
-              end: { x: i, y: j },
-              appear: [
-                new PiPo({
-                  x: i,
-                  y: j,
-                  c: 'a',
-                  p: (fm.vanish[0].p == V.ROOK ? V.BANANA : V.BOMB)
-                })
-              ],
-              vanish: []
-            });
-            if (this.board[i][j] != V.EMPTY) {
-              m.vanish.push(
-                new PiPo({ x: i, y: j, c: 'a', p: this.getPiece(i, j) }));
-            }
-            moves.push(m);
-          }
-        }
-        break;
+    const color = this.turn;
+    const L = this.effects.length;
+    switch (this.effects[L-1]) {
       case "kingboo": {
-        const [x, y] = [fm.appear[0].x, fm.appear[0].y];
+        let allPieces = [];
         for (let i=0; i<8; i++) {
           for (let j=0; j<8; j++) {
             const colIJ = this.getColor(i, j);
+            const pieceIJ = this.getPiece(i, j);
             if (
-              i != x &&
-              j != y &&
+              i != x && j != y &&
               this.board[i][j] != V.EMPTY &&
-              colIJ != 'a'
+              colIJ != 'a' &&
+              pieceIJ != V.INVISIBLE_QUEEN
             ) {
-              const movedUnit = new PiPo({
-                x: x,
-                y: y,
-                c: colIJ,
-                p: this.getPiece(i, j)
+              allPieces.push({ x: i, y: j, c: colIJ, p: pieceIJ });
+            }
+          }
+        }
+        for (let x=0; x<8; x++) {
+          for (let y=0; y<8; y++) {
+            if (this.getColor(i, j) == color) {
+              // Add exchange with something
+              allPieces.forEach(pp => {
+                if (pp.x != i || pp.y != j) {
+                  const movedUnit = new PiPo({
+                    x: x,
+                    y: y,
+                    c: pp.c,
+                    p: pp.p
+                  });
+                  let mMove = this.getBasicMove({ x: x, y: y }, [pp.x, pp.y]);
+                  mMove.appear.push(movedUnit);
+                  moves.push(mMove);
+                }
               });
-              let mMove = this.getBasicMove({ x: x, y: y }, [i, j]);
-              mMove.appear.push(movedUnit);
-              moves.push(mMove);
             }
           }
         }
@@ -1089,68 +995,12 @@ export class ChakartRules extends ChessRules {
         break;
       }
       case "daisy":
-        moves = super.getPotentialMovesFrom([fm.appear[0].x, fm.appear[0].y]);
+        moves = super.getAllPotentialMoves();
         break;
     }
     return moves;
   }
 
-  doClick(square) {
-    const L = this.firstMove.length;
-    const fm = (L > 0 ? this.firstMove[L-1] : null);
-    if (
-      isNaN(square[0]) ||
-      this.subTurn == 1 ||
-      !([0, "daisy"].includes(fm.end.effect))
-    ) {
-      return null;
-    }
-    const [x, y] = [square[0], square[1]];
-    const deltaX = Math.abs(fm.appear[0].x - x);
-    const deltaY = Math.abs(fm.appear[0].y - y);
-    if (
-      fm.end.effect == 0 &&
-      (this.board[x][y] == V.EMPTY || this.getColor(x, y) == 'a') &&
-      (
-        (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) ||
-        (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1)
-      )
-    ) {
-      let m = new Move({
-        start: { x: -1, y: -1 },
-        end: { x: x, y: y },
-        appear: [
-          new PiPo({
-            x: x,
-            y: y,
-            c: 'a',
-            p: (fm.vanish[0].p == V.ROOK ? V.BANANA : V.BOMB)
-          })
-        ],
-        vanish: []
-      });
-      if (this.board[x][y] != V.EMPTY) {
-        m.vanish.push(
-          new PiPo({ x: x, y: y, c: 'a', p: this.getPiece(x, y) }));
-      }
-      return m;
-    }
-    else if (
-      fm.end.effect == "daisy" &&
-      deltaX == 0 && deltaY == 0 &&
-      !this.canMove([x, y])
-    ) {
-      // No possible move: return empty move
-      return {
-        start: { x: -1, y: -1 },
-        end: { x: -1, y: -1 },
-        appear: [],
-        vanish: []
-      };
-    }
-    return null;
-  }
-
   play(move) {
 //    if (!this.states) this.states = [];
 //    const stateFen = this.getFen();
@@ -1159,8 +1009,8 @@ export class ChakartRules extends ChessRules {
     move.flags = JSON.stringify(this.aggregateFlags());
     V.PlayOnBoard(this.board, move);
     move.turn = [this.turn, this.subTurn];
-    if ([0, "kingboo", "toadette", "daisy"].includes(move.end.effect)) {
-      this.firstMove.push(move);
+    if (["kingboo", "toadette", "daisy"].includes(move.end.effect)) {
+      this.effects.push(move.end.effect);
       this.subTurn = 2;
     }
     else {
@@ -1175,7 +1025,11 @@ export class ChakartRules extends ChessRules {
     if (move.end.effect == "toadette") this.reserve = this.captured;
     else this.reserve = undefined;
     const color = move.turn[0];
-    if (move.vanish.length == 2 && move.vanish[1].c != 'a') {
+    if (
+      move.vanish.length == 2 &&
+      move.vanish[1].c != 'a' &&
+      move.appear.length == 1 //avoid king Boo!
+    ) {
       // Capture: update this.captured
       let capturedPiece = move.vanish[1].p;
       if (capturedPiece == V.INVISIBLE_QUEEN) capturedPiece = V.QUEEN;
@@ -1239,8 +1093,8 @@ export class ChakartRules extends ChessRules {
   undo(move) {
     this.disaggregateFlags(JSON.parse(move.flags));
     V.UndoOnBoard(this.board, move);
-    if ([0, "kingboo", "toadette", "daisy"].includes(move.end.effect))
-      this.firstMove.pop();
+    if (["kingboo", "toadette", "daisy"].includes(move.end.effect))
+      this.effects.pop();
     else this.movesCount--;
     this.turn = move.turn[0];
     this.subTurn = move.turn[1];
@@ -1302,9 +1156,9 @@ export class ChakartRules extends ChessRules {
     return "*";
   }
 
-  static GenRandInitFen(randomness) {
+  static GenRandInitFen(options) {
     return (
-      SuicideRules.GenRandInitFen(randomness).slice(0, -1) +
+      SuicideRules.GenRandInitFen(options).slice(0, -1) +
       // Add Peach + Mario flags + capture counts
       "1111 000000000000"
     );
@@ -1416,7 +1270,7 @@ export class ChakartRules extends ChessRules {
       move.vanish.every(v => v.c != 'a')
     ) {
       // King Boo exchange
-      return move.vanish[1].p.toUpperCase() + finalSquare;
+      return V.CoordsToSquare(move.start) + finalSquare;
     }
     const piece = move.vanish[0].p;
     let notation = undefined;