From bc2bc396ec4df092f218b58a0fbf08ba7eb8ca6e Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Fri, 17 Jun 2022 21:34:03 +0200
Subject: [PATCH] Chakart ready soon

---
 base_rules.js               |  12 ++--
 common.css                  |   9 +++
 utils/alea.js               |   4 +-
 variants.js                 |   2 +-
 variants/Chakart/class.js   | 120 ++++++++++++++++++++++++++----------
 variants/Chakart/rules.html |   4 +-
 variants/Giveaway/class.js  |   3 +
 7 files changed, 112 insertions(+), 42 deletions(-)

diff --git a/base_rules.js b/base_rules.js
index dac7bb4..864bb33 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -371,14 +371,14 @@ export default class ChessRules {
   //////////////////
   // INITIALIZATION
 
-  constructor(o, genFenOnly) {
+  constructor(o) {
     this.options = o.options;
     // Fill missing options (always the case if random challenge)
     (V.Options.select || []).concat(V.Options.input || []).forEach(opt => {
       if (this.options[opt.variable] === undefined)
         this.options[opt.variable] = opt.defaut;
     });
-    if (genFenOnly)
+    if (o.genFenOnly)
       // This object will be used only for initial FEN generation
       return;
     this.playerColor = o.color;
@@ -1046,7 +1046,11 @@ export default class ChessRules {
   }
 
   static GetColorClass(c) {
-    return (c == 'w' ? "white" : "black");
+    if (c == 'w')
+      return "white";
+    if (c == 'b')
+      return "black";
+    return ""; //unidentified color
   }
 
   // Assume square i,j isn't empty
@@ -2161,7 +2165,7 @@ export default class ChessRules {
     move.appear.forEach(a => {
       this.g_pieces[a.x][a.y] = document.createElement("piece");
       this.g_pieces[a.x][a.y].classList.add(this.pieces()[a.p]["class"]);
-      this.g_pieces[a.x][a.y].classList.add(a.c == "w" ? "white" : "black");
+      this.g_pieces[a.x][a.y].classList.add(C.GetColorClass(a.c));
       this.g_pieces[a.x][a.y].style.width = pieceWidth + "px";
       this.g_pieces[a.x][a.y].style.height = pieceWidth + "px";
       const [ip, jp] = this.getPixelPosition(a.x, a.y, r);
diff --git a/common.css b/common.css
index ece4aba..a69f4fb 100644
--- a/common.css
+++ b/common.css
@@ -117,6 +117,15 @@ h4 {
   margin: 10px 0;
 }
 
+#gameInfos > .rules > ul {
+  list-style-type: square;
+  padding-left: 30px;
+}
+#gameInfos > .rules > ol {
+  list-style-type: numeric;
+  padding-left: 30px;
+}
+
 #gameInfos > .rules > a {
   padding-bottom: 1px;
   border-bottom: 1px dotted black;
diff --git a/utils/alea.js b/utils/alea.js
index 8b5a4ea..aa3aa57 100644
--- a/utils/alea.js
+++ b/utils/alea.js
@@ -31,14 +31,14 @@ export const Random = {
     n = n || 1;
     let cpArr = arr.map(e => e);
     for (let index = 0; index < n; index++) {
-      const rand = randInt(index, arr.length);
+      const rand = Random.randInt(index, arr.length);
       [ cpArr[index], cpArr[rand] ] = [ cpArr[rand], cpArr[index] ];
     }
     return cpArr.slice(0, n);
   },
 
   shuffle: function(arr) {
-    return sample(arr, arr.length);
+    return Random.sample(arr, arr.length);
   }
 
 };
diff --git a/variants.js b/variants.js
index 13dac98..dc7540e 100644
--- a/variants.js
+++ b/variants.js
@@ -27,7 +27,7 @@ const variants = [
 //  {name: 'Capablanca', desc: 'Capablanca Chess', disp: 'Capablanca Chess'},
   {name: 'Capture', desc: 'Mandatory captures'},
 //  {name: 'Castle', desc: 'Win by castling long'},
-//  {name: 'Chakart', desc: 'Capture the princess'},
+  {name: 'Chakart', desc: 'Capture the princess'},
 //  {name: 'Checkered', desc: 'Shared pieces'},
 //  {name: 'Checkless', desc: 'No-check mode'},
   {name: 'Chess960', disp: "Chess 960", desc: "Standard rules"},
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 5bb1bde..768f50e 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -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,9 +76,10 @@ 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"}'
     );
@@ -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
@@ -176,18 +176,39 @@ export class ChakartRules extends ChessRules {
   }
 
   // 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]);
-    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 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}));
@@ -205,7 +226,7 @@ export class ChakartRules extends ChessRules {
       case 'q':
         moves = this.getQueenMovesFrom([x, y]);
         break;
-      case 'k',
+      case 'k':
         moves = this.getKingMovesFrom([x, y]);
         break;
       case 'n':
@@ -245,7 +266,7 @@ export class ChakartRules extends ChessRules {
     for (let shiftY of [-1, 1]) {
       if (
         y + shiftY >= 0 &&
-        y + shiftY < sizeY &&
+        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 &&
@@ -416,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);
@@ -439,26 +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;
+        }
       }
     }
   }
 
-  playVisual(move, r) {
-    super.playVisual(move, r);
-    if (move.bonus)
-      alert(move.bonus); //TODO: nicer display
-  }
-
   applyRandomBonus(move, cb) {
-    // TODO: determine bonus/malus, and then 
+    // 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
diff --git a/variants/Chakart/rules.html b/variants/Chakart/rules.html
index c87be5c..f2e5068 100644
--- a/variants/Chakart/rules.html
+++ b/variants/Chakart/rules.html
@@ -4,7 +4,9 @@
   <li>Mushrooms speed-up your pieces.</li>
   <li>Bananas redirect by one square orthogonally.</li>
   <li>Bombs redirect by one square diagonally.</li>
-  <li>Eggs hide either a bonus or malus, as indicated...</li>
+  <li>Eggs hide either a bonus or malus: see full description.</li>
 </ul>
 
+<a href="">Full rules description</a>.
+
 <p class="author">Charlotte Blard &amp; Benjamin Auder (2020).</p>
diff --git a/variants/Giveaway/class.js b/variants/Giveaway/class.js
index 77f5c74..71f70be 100644
--- a/variants/Giveaway/class.js
+++ b/variants/Giveaway/class.js
@@ -37,6 +37,9 @@ export default class GiveawayRules extends ChessRules {
   }
 
   genRandInitFen(seed) {
+    if (this.options["mode"] == "losers")
+      return super.genRandInitFen(seed);
+
     if (this.options["randomness"] == 0) {
       return (
         'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 {"enpassant":"-"}'
-- 
2.44.0