Chakart ready soon
[xogo.git] / variants / Chakart / class.js
index 04c0864..768f50e 100644 (file)
@@ -1,11 +1,11 @@
-import ChessRules from "/base_rules";
-import GiveawayRules from "/variants/Giveaway";
+import ChessRules from "/base_rules.js";
+import GiveawayRules from "/variants/Giveaway/class.js";
 import { ArrayFun } from "/utils/array.js";
 import { Random } from "/utils/alea.js";
 import PiPo from "/utils/PiPo.js";
 import Move from "/utils/Move.js";
 
-export class ChakartRules extends ChessRules {
+export default class ChakartRules extends ChessRules {
 
   static get Options() {
     return {
@@ -76,11 +76,12 @@ export class ChakartRules extends ChessRules {
   }
 
   genRandInitFen(seed) {
-    const gr = new GiveawayRules({mode: "suicide"}, true);
+    const gr = new GiveawayRules(
+      {mode: "suicide", options: {}, genFenOnly: true});
     return (
-      gr.genRandInitFen(seed).slice(0, -1) +
+      gr.genRandInitFen(seed).slice(0, -17) +
       // Add Peach + Mario flags + capture counts
-      '{"flags":"1111", "ccount":"000000000000"}'
+      '{"flags":"1111","ccount":"000000000000"}'
     );
   }
 
@@ -125,23 +126,22 @@ export class ChakartRules extends ChessRules {
   }
 
   getCapturedFen() {
-    const res = ['w', 'b'].map(c => {
-      Object.values(this.captured[c])
-    });
+    const res = ['w', 'b'].map(c => Object.values(this.captured[c]));
     return res[0].concat(res[1]).join("");
   }
 
   setOtherVariables(fenParsed) {
     super.setOtherVariables(fenParsed);
     // Initialize captured pieces' counts from FEN
-    const allCapts = fenParsed.captured.split("").map(x => parseInt(x, 10));
+    const allCapts = fenParsed.ccount.split("").map(x => parseInt(x, 10));
     const pieces = ['p', 'r', 'n', 'b', 'q', 'k'];
     this.captured = {
-      w: Array.toObject(pieces, allCapts.slice(0, 6)),
-      b: Array.toObject(pieces, allCapts.slice(6, 12))
+      w: ArrayFun.toObject(pieces, allCapts.slice(0, 6)),
+      b: ArrayFun.toObject(pieces, allCapts.slice(6, 12))
     };
     this.reserve = { w: {}, b: {} }; //to be replaced by this.captured
     this.moveStack = [];
+    this.egg = null;
   }
 
   // For Toadette bonus
@@ -175,32 +175,40 @@ export class ChakartRules extends ChessRules {
     return moves;
   }
 
-// TODO: rethink from here:
-
-// allow pawns 
-  // queen invisible move, king shell: special functions
-
-// prevent pawns from capturing invisible queen (post)
-// post-process: 
-
-//events : playPlusVisual after mouse up, playReceived (include animation) on opp move
-// ==> if move.cont (banana...) self re-call playPlusVisual (rec ?)
-
   // Moving something. Potential effects resolved after playing
-  getPotentialMovesFrom([x, y], bonus) {
+  getPotentialMovesFrom([x, y]) {
     let moves = [];
-    if (bonus == "toadette")
+    if (this.egg == "toadette")
       return this.getDropMovesFrom([x, y]);
-    else if (bonus == "kingboo") {
+    if (this.egg == "kingboo") {
       const initPiece = this.getPiece(x, y);
       const color = this.getColor(x, y);
       const oppCol = C.GetOppCol(color);
-      // Only allow to swap pieces (TODO: restrict for pawns)
+      // Only allow to swap pieces
       for (let i=0; i<this.size.x; i++) {
         for (let j=0; j<this.size.y; j++) {
-          if ((i != x || j != y) && this.board[i][j] != "") {
-            const pstart = new PiPo({x: x, y: y, p: initPiece, c: color});
-            const pend =
+          const colIJ = this.getColor(i, j);
+          const pieceIJ = this.getPiece(i, j);
+          if (
+            (i != x || j != y) &&
+            ['w', 'b'].includes(colIJ) &&
+            // Next conditions = no pawn on last rank
+            (
+              initPiece != 'p' ||
+              (
+                (color != 'w' || i != 0) &&
+                (color != 'b' || i != this.size.x - 1)
+              )
+            )
+            &&
+            (
+              pieceIJ != 'p' ||
+              (
+                (colIJ != 'w' || x != 0) &&
+                (colIJ != 'b' || x != this.size.x - 1)
+              )
+            )
+          ) {
             let m = this.getBasicMove([x, y], [i, j]);
             m.appear.push(
               new PiPo({x: x, y: y, p: this.getPiece(i, j), c: oppCol}));
@@ -214,19 +222,19 @@ export class ChakartRules extends ChessRules {
     switch (this.getPiece(x, y)) {
       case 'p':
         moves = this.getPawnMovesFrom([x, y]); //apply promotions
-        // TODO: add mushroom on init square
         break;
       case 'q':
         moves = this.getQueenMovesFrom([x, y]);
         break;
-      case 'k',
+      case 'k':
         moves = this.getKingMovesFrom([x, y]);
         break;
       case 'n':
-        moves = super.getPotentialMovesFrom([x, y]);
-        // TODO: add egg on init square
+        moves = this.getKnightMovesFrom([x, y]);
         break;
-      default:
+      case 'b':
+      case 'r':
+        // explicitely listing types to avoid moving immobilized piece
         moves = super.getPotentialMovesFrom([x, y]);
     }
     return moves;
@@ -243,40 +251,39 @@ export class ChakartRules extends ChessRules {
       this.getColor(x + shiftX, y) == 'a' ||
       this.getPiece(x + shiftX, y) == V.INVISIBLE_QUEEN
     ) {
-
-      // TODO:
-      this.addPawnMoves([x, y], [x + shiftX, y], moves);
+      moves.push(this.getBasicMove([x, y], [x + shiftX, y]));
       if (
         [firstRank, firstRank + shiftX].includes(x) &&
         (
-          this.board[x + 2 * shiftX][y] == V.EMPTY ||
+          this.board[x + 2 * shiftX][y] == "" ||
           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]));
+        moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
       }
     }
     for (let shiftY of [-1, 1]) {
       if (
         y + shiftY >= 0 &&
-        y + shiftY < sizeY &&
-        this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+        y + shiftY < this.size.y &&
+        this.board[x + shiftX][y + shiftY] != "" &&
         // 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);
+        moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
       }
     }
+    super.pawnPostProcess(moves, color, oppCol);
     return moves;
   }
 
   getQueenMovesFrom(sq) {
-    const normalMoves = super.getPotentialQueenMoves(sq);
+    const normalMoves = super.getPotentialMovesOf('q', sq);
     // If flag allows it, add 'invisible movements'
     let invisibleMoves = [];
-    if (this.powerFlags[this.turn][V.QUEEN]) {
+    if (this.powerFlags[this.turn]['q']) {
       normalMoves.forEach(m => {
         if (
           m.appear.length == 1 &&
@@ -295,16 +302,15 @@ export class ChakartRules extends ChessRules {
   }
 
   getKingMovesFrom([x, y]) {
-    let moves = super.getPotentialKingMoves([x, y]);
-    const color = this.turn;
+    let moves = super.getPotentialMovesOf('k', [x, y]);
     // If flag allows it, add 'remote shell captures'
-    if (this.powerFlags[this.turn][V.KING]) {
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(step => {
+    if (this.powerFlags[this.turn]['k']) {
+      super.pieces()['k'].moves[0].steps.forEach(step => {
         let [i, j] = [x + step[0], y + step[1]];
         while (
-          V.OnBoard(i, j) &&
+          this.onBoard(i, j) &&
           (
-            this.board[i][j] == V.EMPTY ||
+            this.board[i][j] == "" ||
             this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
             (
               this.getColor(i, j) == 'a' &&
@@ -315,19 +321,17 @@ export class ChakartRules extends ChessRules {
           i += step[0];
           j += step[1];
         }
-        if (V.OnBoard(i, j)) {
+        if (this.onBoard(i, j)) {
           const colIJ = this.getColor(i, j);
-          if (colIJ != color) {
+          if (colIJ != this.turn) {
             // May just destroy a bomb or banana:
             moves.push(
               new Move({
-                start: { x: x, y: y},
-                end: { x: i, y: j },
+                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)
-                  })
+                  new PiPo({x: i, y: j, c: colIJ, p: this.getPiece(i, j)})
                 ]
               })
             );
@@ -338,14 +342,25 @@ export class ChakartRules extends ChessRules {
     return moves;
   }
 
-  // TODO: can merge prePlay into play() ==> no need to distinguish
+  getKnightMovesFrom([x, y]) {
+    // Add egg on initial square:
+    return super.getPotentialMovesOf('n', [x, y]).map(m => {
+      m.appear.push(new PiPo({p: "e", c: "a", x: x, y: y}));
+      return m;
+    });
+  }
+
 /// if any of my pieces was immobilized, it's not anymore.
   //if play set a piece immobilized, then mark it
-  prePlay(move) {
-    if (move.effect == "toadette")
+  play(move) {
+    if (move.effect == "toadette") {
       this.reserve = this.captured;
-    else
-      this.reserve = { w: {}, b: {} };;
+      this.re_drawReserve([this.turn]);
+    }
+    else if (this.reserve) {
+      this.reserve = { w: {}, b: {} };
+      this.re_drawReserve([this.turn]);
+    }
     const color = this.turn;
     if (
       move.vanish.length == 2 &&
@@ -361,7 +376,8 @@ export class ChakartRules extends ChessRules {
       this.captured[move.vanish[1].c][capturedPiece]++;
     }
     else if (move.vanish.length == 0) {
-      if (move.appear.length == 0 || move.appear[0].c == 'a') return;
+      if (move.appear.length == 0 || move.appear[0].c == 'a')
+        return;
       // A piece is back on board
       this.captured[move.appear[0].c][move.appear[0].p]--;
     }
@@ -411,10 +427,6 @@ export class ChakartRules extends ChessRules {
         }
       }
     }
-  }
-
-  play(move) {
-    this.prePlay(move);
     this.playOnBoard(move);
     if (["kingboo", "toadette", "daisy"].includes(move.effect)) {
       this.effect = move.effect;
@@ -425,18 +437,27 @@ export class ChakartRules extends ChessRules {
       this.movesCount++;
       this.subTurn = 1;
     }
+
+
+    if (move.egg)
+      this.displayBonus(move.egg);
+    else if (this.egg)
+      this.egg = null; //the egg is consumed
+  }
+
+  displayBonus(egg) {
+    alert(egg); //TODO: nicer display
   }
 
   filterValid(moves) {
     return moves;
   }
 
-  // idée : on joue le coup, puis son effet est déterminé, puis la suite (si suite)
-  // est jouée automatiquement ou demande action utilisateur, etc jusqu'à coup terminal.
   tryMoveFollowup(move, cb) {
-    if (this.getColor(move.end.x, move.end.y) == 'a') {
+    // Warning: at this stage, the move is played
+    if (move.vanish.length == 2 && move.vanish[1].c == 'a') {
       // effect, or bonus/malus
-      const endType = this.getPiece(m.end.x, m.end.y);
+      const endType = move.vanish[1].p;
       switch (endType) {
         case V.EGG:
           this.applyRandomBonus(move, cb);
@@ -448,20 +469,48 @@ export class ChakartRules extends ChessRules {
               endType == V.BANANA
                 ? [[1, 1], [1, -1], [-1, 1], [-1, -1]]
                 : [[1, 0], [-1, 0], [0, 1], [0, -1]]);
-          const nextMove = this.getBasicMove([move.end.x, move.end.y], dest);
-          cb(nextMove);
+          cb(this.getBasicMove([move.end.x, move.end.y], dest));
           break;
         }
-        case V.MUSHROOM:
-          // aller dans direction, saut par dessus pièce adverse
-          // ou amie (tjours), new step si roi caval pion
+        case V.MUSHROOM: {
+          let step = [move.end.x - move.start.x, move.end.y - move.start.y];
+          if ([0, 1].some(i => step[i] >= 2 && step[1-i] != 1)) {
+            // Slider, multi-squares: normalize step
+            for (let j of [0, 1])
+              step[j] = step[j] / Math.abs(step[j]) || 0;
+          }
+          const nextSquare = [move.end.x + step[0], move.end.y + step[1]];
+          if (this.onBoard(nextSquare[0], nextSquare[1])) {
+            if (
+              this.board[nextSquare[0]][nextSquare[1]] != "" &&
+              this.getColor(nextSquare[0], nextSquare[1]) != 'a'
+            ) {
+              // (try to) jump
+              const afterSquare =
+                [nextSquare[0] + step[0], nextSquare[1] + step[1]];
+              if (
+                this.onBoard(afterSquare[0], afterSquare[1]) &&
+                this.getColor(afterSquare[0], afterSquare[1]) != this.turn
+              ) {
+                cb(this.getBasicMove([move.end.x, move.end.y], afterSquare));
+              }
+            }
+            else if (!['b', 'r', 'q'].includes(move.vanish[0].p))
+              // Take another step forward if not slider move
+              cb(this.getBasicMove([move.end.x, move.end.y], nextSquare));
+          }
           break;
+        }
       }
     }
   }
 
-  applyRandomBonnus(move, cb) {
-    // TODO: determine bonus/malus, and then 
+  applyRandomBonus(move, cb) {
+    // TODO: determine bonus/malus, and then ...
+    // if toadette, daisy or kingboo : do not call cb
+    this.egg = "daisy"; //not calling cb in this case
+    this.displayBonus(this.egg);
+    move.egg = this.egg; //for play() by opponent
   }
 
   // Helper to apply banana/bomb effect
@@ -470,13 +519,12 @@ export class ChakartRules extends ChessRules {
     const step = validSteps[Random.randInt(validSteps.length)];
     return [x + step[0], y + step[1]];
   }
-// TODO: turn change indicator ?!
+
+  // Warning: if play() is called, then move.end changed.
   playPlusVisual(move, r) {
     this.moveStack.push(move);
     this.play(move);
     this.playVisual(move, r);
-    if (move.bonus)
-      alert(move.bonus); //TODO: nicer display
     this.tryMoveFollowup(move, (nextMove) => {
       if (nextMove)
         this.playPlusVisual(nextMove, r);