From b9ce3d0fbe6cf8cba01912706ad578144bc9b42f Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Fri, 24 Apr 2020 01:47:24 +0200
Subject: [PATCH] Advance on Chakart, fix Football GenRandInitFen() + try
 fixing newly introduced bug in Game.vue

---
 client/public/images/pieces/Chakart/bi.svg |   1 +
 client/public/images/pieces/Chakart/wi.svg |   1 +
 client/src/variants/Alice.js               |  24 ++++
 client/src/variants/Chakart.js             | 154 ++++++++++++++++++++-
 client/src/variants/Football.js            |  33 +++++
 client/src/views/Faq.vue                   |   1 +
 client/src/views/Game.vue                  |   6 +-
 7 files changed, 211 insertions(+), 9 deletions(-)
 create mode 120000 client/public/images/pieces/Chakart/bi.svg
 create mode 120000 client/public/images/pieces/Chakart/wi.svg

diff --git a/client/public/images/pieces/Chakart/bi.svg b/client/public/images/pieces/Chakart/bi.svg
new file mode 120000
index 00000000..1355bb5e
--- /dev/null
+++ b/client/public/images/pieces/Chakart/bi.svg
@@ -0,0 +1 @@
+../../empty.svg
\ No newline at end of file
diff --git a/client/public/images/pieces/Chakart/wi.svg b/client/public/images/pieces/Chakart/wi.svg
new file mode 120000
index 00000000..1355bb5e
--- /dev/null
+++ b/client/public/images/pieces/Chakart/wi.svg
@@ -0,0 +1 @@
+../../empty.svg
\ No newline at end of file
diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js
index 15e629a4..a3d3ff43 100644
--- a/client/src/variants/Alice.js
+++ b/client/src/variants/Alice.js
@@ -60,6 +60,30 @@ export class AliceRules extends ChessRules {
     return undefined; //default
   }
 
+  // king can be l or L (on the other mirror side)
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
+    const rows = position.split("/");
+    if (rows.length != V.size.x) return false;
+    let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 };
+    for (let row of rows) {
+      let sumElts = 0;
+      for (let i = 0; i < row.length; i++) {
+        if (['K','k','L','l'].includes(row[i])) kings[row[i]]++;
+        if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
+        else {
+          const num = parseInt(row[i]);
+          if (isNaN(num)) return false;
+          sumElts += num;
+        }
+      }
+      if (sumElts != V.size.y) return false;
+    }
+    if (kings['k'] + kings['l'] != 1 || kings['K'] + kings['L'] != 1)
+      return false;
+    return true;
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const rows = V.ParseFen(fen).position.split("/");
diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js
index ee3695e7..d52686a0 100644
--- a/client/src/variants/Chakart.js
+++ b/client/src/variants/Chakart.js
@@ -11,7 +11,6 @@ export class ChakartRules extends ChessRules {
 // keep track of captured pieces: comme Grand; pieces can get back to board with toadette bonus.
 // --> pour ce bonus, passer "capture" temporairement en "reserve" pour permettre de jouer le coup.
 
-  // FEN : castle flags + flags peach (power used?) + Mario (invisibility used? --> move notation Q??)
   // "pièces" supplémentaires : bananes, bombes, champis, bonus --> + couleur ?
   //   (Semble mieux sans couleur => couleur spéciale indiquant que c'est pas jouable)
   // (Attention: pas jouables cf. getPotentialMoves...)
@@ -21,16 +20,61 @@ export class ChakartRules extends ChessRules {
     return this.subTurn == 2; //&& this.firstMove.donkey or wario or bonus roi boo
   }
 
-  // king can be l or L (immobilized) --> copy-paste from Alice variant
+  static get IMMOBILIZE_CODE() {
+    return {
+      'p': 's',
+      'r': 'u',
+      'n': 'o',
+      'b': 'c',
+      'q': 't',
+      'k': 'l'
+    };
+  }
+
+  static get IMMOBILIZE_DECODE() {
+    return {
+      's': 'p',
+      'u': 'r',
+      'o': 'n',
+      'c': 'b',
+      't': 'q',
+      'l': 'k'
+    };
+  }
+
+  static get INVISIBLE_QUEEN() {
+    return 'i';
+  }
+
+  getPpath(b) {
+    let prefix = "";
+    if (
+      b[1] == V.INVISIBLE_QUEEN ||
+      Object.keys(V.IMMOBILIZE_DECODE).includes(b[1])
+    ) {
+      prefix = "Chakart/";
+    }
+    return prefix + b;
+  }
+
+  static ParseFen(fen) {
+    const fenParts = fen.split(" ");
+    return Object.assign(
+      ChessRules.ParseFen(fen),
+      { captured: fenParts[5] }
+    );
+  }
+
+  // King can be l or L (immobilized) --> similar to Alice variant
   static IsGoodPosition(position) {
     if (position.length == 0) return false;
     const rows = position.split("/");
     if (rows.length != V.size.x) return false;
-    let kings = { "k": 0, "K": 0 };
+    let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 };
     for (let row of rows) {
       let sumElts = 0;
       for (let i = 0; i < row.length; i++) {
-        if (['K','k'].includes(row[i])) kings[row[i]]++;
+        if (['K','k','L','l'].includes(row[i])) kings[row[i]]++;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
           const num = parseInt(row[i]);
@@ -40,15 +84,88 @@ export class ChakartRules extends ChessRules {
       }
       if (sumElts != V.size.y) return false;
     }
-    if (Object.values(kings).some(v => v != 1)) return false;
+    if (kings['k'] + kings['l'] != 1 || kings['K'] + kings['L'] != 1)
+      return false;
     return true;
   }
 
+  static IsGoodFlags(flags) {
+    // 4 for castle + 4 for Peach + Mario w, b
+    return !!flags.match(/^[a-z]{4,4}[01]{4,4}$/);
+  }
+
+  setFlags(fenflags) {
+    super.setFlags(fenflags); //castleFlags
+    this.powerFlags = {
+      w: [...Array(2)], //king can send red shell? Queen can be invisible?
+      b: [...Array(2)]
+    };
+    const flags = fenflags.substr(4); //skip first 4 letters, for castle
+    for (let c of ["w", "b"]) {
+      for (let i = 0; i < 2; i++)
+        this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 2) + i) == "1";
+    }
+  }
+
+  aggregateFlags() {
+    return [this.castleFlags, this.powerFlags];
+  }
+
+  disaggregateFlags(flags) {
+    this.castleFlags = flags[0];
+    this.powerFlags = flags[1];
+  }
+
+  getFen() {
+    return super.getFen() + " " + this.getCapturedFen();
+  }
+
+  getFenForRepeat() {
+    return super.getFenForRepeat() + "_" + this.getCapturedFen();
+  }
+
+  getCapturedFen() {
+    let counts = [...Array(10).fill(0)];
+    let i = 0;
+    for (let p of [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.PAWN])
+      counts[i] = this.captured["w"][p];
+      counts[5 + i] = this.captured["b"][p];
+      i++;
+    }
+    return counts.join("");
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
+    const fenParsed = V.ParseFen(fen);
+    // Initialize captured pieces' counts from FEN
+    this.captured = {
+      w: {
+        [V.ROOK]: parseInt(fenParsed.captured[0]),
+        [V.KNIGHT]: parseInt(fenParsed.captured[1]),
+        [V.BISHOP]: parseInt(fenParsed.captured[2]),
+        [V.QUEEN]: parseInt(fenParsed.captured[3]),
+        [V.PAWN]: parseInt(fenParsed.captured[4]),
+      },
+      b: {
+        [V.ROOK]: parseInt(fenParsed.captured[5]),
+        [V.KNIGHT]: parseInt(fenParsed.captured[6]),
+        [V.BISHOP]: parseInt(fenParsed.captured[7]),
+        [V.QUEEN]: parseInt(fenParsed.captured[8]),
+        [V.PAWN]: parseInt(fenParsed.captured[9]),
+      }
+    };
     this.subTurn = 1;
   }
 
+  getFlagsFen() {
+    let fen = super.getFlagsFen();
+    // Add power flags
+    for (let c of ["w", "b"])
+      for (let i = 0; i < 2; i++) fen += (this.powerFlags[c][i] ? "1" : "0");
+    return fen;
+  }
+
   getPotentialMovesFrom([x, y]) {
     // TODO: bananes et bombes limitent les déplacements (agissent comme un mur "capturable")
     // bananes jaunes et rouges ?! (agissant sur une seule couleur ?) --> mauvaise idée.
@@ -57,6 +174,7 @@ export class ChakartRules extends ChessRules {
     }
   //Détails :
   //Si une pièce pose quelque chose sur une case ça remplace ce qui y était déjà.
+  // TODO: un-immobilize my immobilized piece at the end of this turn, if any
   }
 
   getPotentialPawnMoves(sq) {
@@ -159,16 +277,40 @@ export class ChakartRules extends ChessRules {
     // TODO: king may also be "chomped"
     super.updateCastleFlags(move, piece);
   }
+  postPlay(move) {
+    super.postPlay(move);
+    if (move.vanish.length == 2 && move.appear.length == 1)
+      // Capture: update this.captured
+      this.captured[move.vanish[1].c][move.vanish[1].p]++;
+  }
+
+  postUndo(move) {
+    super.postUndo(move);
+    if (move.vanish.length == 2 && move.appear.length == 1)
+      this.captured[move.vanish[1].c][move.vanish[1].p]--;
+  }
 
   getCurrentScore() {
     if (this.kingPos[this.turn][0] < 0)
       // King captured (or "chomped")
       return this.turn == "w" ? "0-1" : "1-0";
-    //TODO: But = capturer la princesse adverse (téléportation possible donc pas but = arriver de l'autre côté)
     return '*';
   }
 
+  static GenRandInitFen(randomness) {
+    return (
+      ChessRules.GenRandInitFen(randomness).slice(0, -2) +
+      // Add Peach + Mario flags, re-add en-passant + capture counts
+      "0000 - 0000000000"
+    );
+  }
+
   getComputerMove() {
     // TODO: random mover
   }
+
+  getNotation(move) {
+    // invisibility used? --> move notation Q??
+
+  }
 };
diff --git a/client/src/variants/Football.js b/client/src/variants/Football.js
index 2acb24f4..b0678e16 100644
--- a/client/src/variants/Football.js
+++ b/client/src/variants/Football.js
@@ -77,4 +77,37 @@ export class FootballRules extends ChessRules {
     if (this.atLeastOneMove()) return "*";
     return "1/2";
   }
+
+  static GenRandInitFen(randomness) {
+    if (randomness == 0)
+      return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
+
+    let pieces = { w: new Array(8), b: new Array(8) };
+    for (let c of ["w", "b"]) {
+      if (c == 'b' && randomness == 1) {
+        pieces['b'] = pieces['w'];
+        break;
+      }
+
+      // Get random squares for every piece, totally freely
+      let positions = shuffle(ArrayFun.range(8));
+      const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
+      const rem2 = positions[0] % 2;
+      if (rem2 == positions[1] % 2) {
+        // Fix bishops (on different colors)
+        for (let i=2; i<8; i++) {
+          if (positions[i] % 2 != rem2)
+            [positions[1], positions[i]] = [positions[i], positions[1]];
+        }
+      }
+      for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
+    }
+    return (
+      pieces["b"].join("") +
+      "/pppppppp/8/8/8/8/PPPPPPPP/" +
+      pieces["w"].join("").toUpperCase() +
+      // En-passant allowed, but no flags
+      " w 0 -"
+    );
+  }
 };
diff --git a/client/src/views/Faq.vue b/client/src/views/Faq.vue
index a87e6bd7..3376afd2 100644
--- a/client/src/views/Faq.vue
+++ b/client/src/views/Faq.vue
@@ -61,6 +61,7 @@ export default {
   cursor: pointer
 
 .answer
+  margin-bottom: 10px
   ol, ul
     margin-top: 0
     margin-bottom: 0
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index 62611393..2beb10cb 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -753,8 +753,8 @@ export default {
           // Got opponent infos about last move
           this.gotLastate = true;
           this.lastate = data.data;
-          if (this.lastate.movesCount > this.gotMoveIdx)
-            this.gotMoveIdx = this.lastate.movesCount;
+          if (this.lastate.movesCount - 1 > this.gotMoveIdx)
+            this.gotMoveIdx = this.lastate.movesCount - 1;
           if (this.game.rendered)
             // Game is rendered (Board component)
             this.processLastate();
@@ -1273,7 +1273,7 @@ console.log(data.data);
       this.$nextTick(() => {
         this.game.rendered = true;
         // Did lastate arrive before game was rendered?
-        if (this.lastate) this.processLastate();
+        if (!!this.lastate) this.processLastate();
       });
       if (this.lastateAsked) {
         this.lastateAsked = false;
-- 
2.44.0