Fix Align4, fix mushrooms effect for Chakart
[xogo.git] / variants / Chakart / class.js
index a9b1908..24bbd3e 100644 (file)
@@ -64,10 +64,6 @@ export default class ChakartRules extends ChessRules {
     };
   }
 
-  static get INVISIBLE_QUEEN() {
-    return 'i';
-  }
-
   // Fictive color 'a', bomb banana mushroom egg
   static get BOMB() {
     return 'w'; //"Wario"
@@ -101,6 +97,7 @@ export default class ChakartRules extends ChessRules {
   pieces(color, x, y) {
     const specials = {
       'i': {"class": "invisible"}, //queen
+      '?': {"class": "mystery"}, //...initial square
       'e': {"class": "egg"},
       'm': {"class": "mushroom"},
       'd': {"class": "banana"},
@@ -115,14 +112,33 @@ export default class ChakartRules extends ChessRules {
       't': {"class": ["immobilized", "queen"]},
       'l': {"class": ["immobilized", "king"]}
     };
-    return Object.assign({}, specials, bowsered, super.pieces(color, x, y));
+    return Object.assign(
+      {
+        'y': {
+          // Virtual piece for "king remote shell captures"
+          moves: [],
+          attack: [
+            {
+              steps: [
+                [0, 1], [0, -1], [1, 0], [-1, 0],
+                [1, 1], [1, -1], [-1, 1], [-1, -1]
+              ]
+            }
+          ]
+        }
+      },
+      specials, bowsered, super.pieces(color, x, y));
   }
 
   genRandInitFen(seed) {
-    const gr = new GiveawayRules(
-      {mode: "suicide", options: {}, genFenOnly: true});
-    // Add Peach + mario flags
-    return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}';
+    const options = Object.assign({mode: "suicide"}, this.options);
+    const gr = new GiveawayRules({options: options, genFenOnly: true});
+    const baseFen = gr.genRandInitFen(seed);
+    const fenParts = baseFen.split(" ");
+    let others = JSON.parse(fenParts[3]);
+    delete others["enpassant"];
+    others["flags"] = "1111"; //Peach + Mario flags
+    return fenParts.slice(0, 3).join(" ") + " " + JSON.stringify(others);
   }
 
   fen2board(f) {
@@ -168,7 +184,7 @@ export default class ChakartRules extends ChessRules {
     this.moveStack = [];
     // Change seed (after FEN generation!!)
     // so that further calls differ between players:
-    Random.setSeed(Math.floor(10000 * Math.random()));
+    Random.setSeed(Math.floor(19840 * Math.random()));
   }
 
   // For Toadette bonus
@@ -182,11 +198,7 @@ export default class ChakartRules extends ChessRules {
       for (let j = 0; j < this.size.y; j++) {
         const pieceIJ = this.getPiece(i, j);
         const colIJ = this.getColor(i, j);
-        if (
-          this.board[i][j] == "" ||
-          colIJ == 'a' ||
-          pieceIJ == V.INVISIBLE_QUEEN
-        ) {
+        if (this.board[i][j] == "" || colIJ == 'a' || pieceIJ == 'i') {
           let m = new Move({
             start: {x: c, y: p},
             appear: [new PiPo({x: i, y: j, c: c, p: p})],
@@ -262,7 +274,7 @@ export default class ChakartRules extends ChessRules {
         case 'b':
         case 'r':
           // Explicitely listing types to avoid moving immobilized piece
-          moves = super.getPotentialMovesOf(piece, [x, y]);
+          moves = this.getPotentialMovesOf(piece, [x, y]);
           break;
       }
     }
@@ -272,7 +284,8 @@ export default class ChakartRules extends ChessRules {
   canStepOver(i, j) {
     return (
       this.board[i][j] == "" ||
-      [V.MUSHROOM, V.EGG].includes(this.getPiece(i, j)));
+      ['i', V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
+    );
   }
 
   getPawnMovesFrom([x, y]) {
@@ -281,51 +294,59 @@ export default class ChakartRules extends ChessRules {
     const shiftX = (color == 'w' ? -1 : 1);
     const firstRank = (color == "w" ? this.size.x - 1 : 0);
     let moves = [];
+    const frontPiece = this.getPiece(x + shiftX, y);
     if (
       this.board[x + shiftX][y] == "" ||
       this.getColor(x + shiftX, y) == 'a' ||
-      this.getPiece(x + shiftX, y) == V.INVISIBLE_QUEEN
+      frontPiece == 'i'
     ) {
       moves.push(this.getBasicMove([x, y], [x + shiftX, y]));
       if (
         [firstRank, firstRank + shiftX].includes(x) &&
+        ![V.BANANA, V.BOMB].includes(frontPiece) &&
         (
           this.board[x + 2 * shiftX][y] == "" ||
           this.getColor(x + 2 * shiftX, y) == 'a' ||
-          this.getPiece(x + 2 * shiftX, y) == V.INVISIBLE_QUEEN
+          this.getPiece(x + 2 * shiftX, y) == 'i'
         )
       ) {
         moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
       }
     }
     for (let shiftY of [-1, 1]) {
+      const nextY = this.getY(y + shiftY);
       if (
-        y + shiftY >= 0 &&
-        y + shiftY < this.size.y &&
-        this.board[x + shiftX][y + shiftY] != "" &&
+        nextY >= 0 &&
+        nextY < this.size.y &&
+        this.board[x + shiftX][nextY] != "" &&
         // 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.getPiece(x + shiftX, nextY) != 'i' &&
+        ['a', oppCol].includes(this.getColor(x + shiftX, nextY))
       ) {
-        moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
+        moves.push(this.getBasicMove([x, y], [x + shiftX, nextY]));
       }
     }
     this.pawnPostProcess(moves, color, oppCol);
-    // Add mushroom on before-last square
+    // Add mushroom on before-last square (+ potential segments)
     moves.forEach(m => {
-      let revStep = [m.start.x - m.end.x, m.start.y - m.end.y];
-      for (let i of [0, 1])
-        revStep[i] = revStep[i] / Math.abs(revStep[i]) || 0;
-      const [blx, bly] = [m.end.x + revStep[0], m.end.y + revStep[1]];
-      m.appear.push(new PiPo({x: blx, y: bly, c: 'a', p: 'm'}));
-      if (blx != x && this.board[blx][bly] != "") {
+      let [mx, my] = [x, y];
+      if (Math.abs(m.end.x - m.start.x) == 2)
+        mx = (m.start.x + m.end.x) / 2;
+      m.appear.push(new PiPo({x: mx, y: my, c: 'a', p: 'm'}));
+      if (mx != x && this.board[mx][my] != "") {
         m.vanish.push(new PiPo({
-          x: blx,
-          y: bly,
-          c: this.getColor(blx, bly),
-          p: this.getPiece(blx, bly)
+          x: mx,
+          y: my,
+          c: this.getColor(mx, my),
+          p: this.getPiece(mx, my)
         }));
       }
+      if (Math.abs(m.end.y - m.start.y) > 1) {
+        m.segments = [
+          [[x, y], [x, y]],
+          [[m.end.x, m.end.y], [m.end.x, m.end.y]]
+        ];
+      }
     });
     return moves;
   }
@@ -351,7 +372,7 @@ export default class ChakartRules extends ChessRules {
           m.vanish[0].c != 'a'
         ) {
           let im = JSON.parse(JSON.stringify(m));
-          im.appear[0].p = V.INVISIBLE_QUEEN;
+          im.appear[0].p = 'i';
           im.noAnimate = true;
           invisibleMoves.push(im);
         }
@@ -364,45 +385,36 @@ export default class ChakartRules extends ChessRules {
     let moves = this.getPotentialMovesOf('k', [x, y]);
     // If flag allows it, add 'remote shell captures'
     if (this.powerFlags[this.turn]['k']) {
-      super.pieces()['k'].moves[0].steps.forEach(step => {
-        let [i, j] = [x + step[0], y + step[1]];
-        while (
-          this.onBoard(i, j) &&
-          (
-            this.board[i][j] == "" ||
-            this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
-            (
-              this.getColor(i, j) == 'a' &&
-              [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
-            )
-          )
-        ) {
-          i += step[0];
-          j += step[1];
-        }
-        if (this.onBoard(i, j)) {
-          const colIJ = this.getColor(i, j);
-          if (colIJ != this.turn) {
-            // May just destroy a bomb or banana:
-            let shellCapture = new Move({
-              start: {x: x, y: y},
-              end: {x: i, y: j},
-              appear: [],
-              vanish: [
-                new PiPo({x: i, y: j, c: colIJ, p: this.getPiece(i, j)})
-              ]
-            });
-            shellCapture.shell = true; //easier play()
-            shellCapture.choice = 'z'; //to display in showChoices()
-            moves.push(shellCapture);
-          }
-        }
+      let shellCaptures = this.getPotentialMovesOf('y', [x, y]);
+      shellCaptures.forEach(sc => {
+        sc.shell = true; //easier play()
+        sc.choice = 'z'; //to display in showChoices()
+        // Fix move (Rifle style):
+        sc.vanish.shift();
+        sc.appear.shift();
       });
+      Array.prototype.push.apply(moves, shellCaptures);
     }
     return moves;
   }
 
   play(move) {
+    const color = this.turn;
+    const oppCol = C.GetOppCol(color);
+    if (
+      move.appear.length > 0 &&
+      move.appear[0].p == 'p' &&
+      (
+        (color == 'w' && move.end.x == 0) ||
+        (color == 'b' && move.end.x == this.size.x - 1)
+      )
+    ) {
+      // "Forgotten" promotion, which occurred after some effect
+      let moves = [move];
+      super.pawnPostProcess(moves, color, oppCol);
+      super.showChoices(moves);
+      return false;
+    }
     if (!move.nextComputed) {
       // Set potential random effects, so that play() is deterministic
       // from opponent viewpoint:
@@ -455,8 +467,6 @@ export default class ChakartRules extends ChessRules {
       move.nextComputed = true;
     }
     this.egg = move.egg;
-    const color = this.turn;
-    const oppCol = C.GetOppCol(color);
     if (move.egg == "toadette") {
       this.reserve = { w: {}, b: {} };
       // Randomly select a piece in pawnPromotions
@@ -471,10 +481,12 @@ export default class ChakartRules extends ChessRules {
     }
     if (move.shell)
       this.powerFlags[color]['k'] = false;
-    else if (move.appear.length > 0 && move.appear[0].p == V.INVISIBLE_QUEEN) {
+    else if (move.appear.length > 0 && move.appear[0].p == 'i') {
       this.powerFlags[move.appear[0].c]['q'] = false;
-      if (color != this.playerColor)
-        alert("Invisible queen!");
+      if (color == this.playerColor) {
+        move.appear.push(
+          new PiPo({x: move.start.x, y: move.start.y, c: color, p: '?'}));
+      }
     }
     if (color == this.playerColor) {
       // Look for an immobilized piece of my color: it can now move
@@ -501,15 +513,15 @@ export default class ChakartRules extends ChessRules {
         for (let j=0; j<8; j++) {
           if (
             this.board[i][j] != "" &&
-            this.getColor(i, j) == oppCol &&
-            this.getPiece(i, j) == V.INVISIBLE_QUEEN
+            this.getColor(i, j) == oppCol
           ) {
-            move.vanish.push(new PiPo({
-              x: i, y: j, c: oppCol, p: V.INVISIBLE_QUEEN
-            }));
-            move.appear.push(new PiPo({
-              x: i, y: j, c: oppCol, p: 'q'
-            }));
+            const pieceIJ = this.getPiece(i, j);
+            if (pieceIJ == 'i') {
+              move.vanish.push(new PiPo({x: i, y: j, c: oppCol, p: 'i'}));
+              move.appear.push(new PiPo({x: i, y: j, c: oppCol, p: 'q'}));
+            }
+            else if (pieceIJ == '?')
+              move.vanish.push(new PiPo({x: i, y: j, c: oppCol, p: '?'}));
           }
         }
       }
@@ -519,9 +531,10 @@ export default class ChakartRules extends ChessRules {
       this.movesCount++;
     }
     if (move.egg)
-      this.displayBonus(move.egg);
+      this.displayBonus(move);
     this.playOnBoard(move);
     this.nextMove = move.next;
+    return true;
   }
 
   // Helper to set and apply banana/bomb effect
@@ -636,34 +649,29 @@ export default class ChakartRules extends ChessRules {
   }
 
   getMushroomEffect(move) {
-    let step = [move.end.x - move.start.x, move.end.y - move.start.y];
-    if ([0, 1].some(i => Math.abs(step[i]) >= 2 && Math.abs(step[1-i]) != 1)) {
-      // Slider, multi-squares: normalize step
-      for (let j of [0, 1])
-        step[j] = step[j] / Math.abs(step[j]) || 0;
+    if (
+      typeof move.start.x == "string" || //drop move (toadette)
+      ['b', 'r', 'q'].includes(move.vanish[0].p) //slider
+    ) {
+      return null;
     }
+    let step = [move.end.x - move.start.x, move.end.y - move.start.y];
+    if (Math.abs(step[0]) == 2 && Math.abs(step[1]) == 0)
+      // Pawn initial 2-squares move: normalize step
+      step[0] /= 2;
     const nextSquare = [move.end.x + step[0], move.end.y + step[1]];
-    const afterSquare =
-      [nextSquare[0] + step[0], nextSquare[1] + step[1]];
     let nextMove = null;
-    this.playOnBoard(move); //HACK for getBasicMove() below
     if (
       this.onBoard(nextSquare[0], nextSquare[1]) &&
-      ['k', 'p', 'n'].includes(move.vanish[0].p) &&
-      !['w', 'b'].includes(this.getColor(nextSquare[0], nextSquare[1]))
+      (
+        this.board[nextSquare[0]][nextSquare[1]] == "" ||
+        this.getColor(nextSquare[0], nextSquare[1]) == 'a'
+      )
     ) {
-      // Speed up non-sliders
+      this.playOnBoard(move); //HACK for getBasicMove()
       nextMove = this.getBasicMove([move.end.x, move.end.y], nextSquare);
+      this.undoOnBoard(move);
     }
-    else if (
-      this.onBoard(afterSquare[0], afterSquare[1]) &&
-      this.board[nextSquare[0]][nextSquare[1]] != "" &&
-      this.getColor(nextSquare[0], nextSquare[1]) != 'a' &&
-      this.getColor(afterSquare[0], afterSquare[1]) != this.turn
-    ) {
-      nextMove = this.getBasicMove([move.end.x, move.end.y], afterSquare);
-    }
-    this.undoOnBoard(move);
     return nextMove;
   }
 
@@ -678,8 +686,13 @@ export default class ChakartRules extends ChessRules {
     return res;
   }
 
-  displayBonus(egg) {
-    alert(egg); //TODO: nicer display
+  displayBonus(move) {
+    let divBonus = document.createElement("div");
+    divBonus.classList.add("bonus-text");
+    divBonus.innerHTML = move.egg;
+    let container = document.getElementById(this.containerId);
+    container.appendChild(divBonus);
+    setTimeout(() => container.removeChild(divBonus), 2000);
   }
 
   atLeastOneMove() {
@@ -691,9 +704,10 @@ export default class ChakartRules extends ChessRules {
   }
 
   playPlusVisual(move, r) {
-    this.moveStack.push(move);
     const nextLines = () => {
-      this.play(move);
+      if (!this.play(move))
+        return;
+      this.moveStack.push(move);
       this.playVisual(move, r);
       if (this.nextMove)
         this.playPlusVisual(this.nextMove, r);
@@ -702,7 +716,7 @@ export default class ChakartRules extends ChessRules {
         this.moveStack = [];
       }
     };
-    if (this.moveStack.length == 1)
+    if (this.moveStack.length == 0)
       nextLines();
     else
       this.animate(move, nextLines);