Some fixes, improvements... main
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 9 May 2026 10:42:46 +0000 (12:42 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 9 May 2026 10:42:46 +0000 (12:42 +0200)
js/base_rules.js
js/server.js
variants/Alapo/class.js
variants/Apocalypse/class.js
variants/Atarigo/class.js
variants/Avalam/class.js
variants/Discoduel/class.js
variants/Giveaway/class.js
variants/Hex/class.js
variants/Sleepy/class.js
variants/Weiqi/class.js

index 5ff92f8..347d5b2 100644 (file)
@@ -66,7 +66,7 @@ export default class ChessRules {
         "cannibal",
         "capture",
         "crazyhouse",
-        "cylinder", //ok with all
+        "cylinder", //ok with ~all
         "dark",
         "doublemove",
         "madrasi",
@@ -117,6 +117,11 @@ export default class ChessRules {
     return this.hasReserve;
   }
 
+  // Some variants don't have kings, not relevant
+  static get HasKing() {
+    return true;
+  }
+
   get noAnimate() {
     return !!this.options["dark"];
   }
@@ -1187,6 +1192,8 @@ export default class ChessRules {
   }
 
   isKing(x, y, p) {
+    if (!V.HasKing)
+      return false;
     if (!p)
       p = this.getPiece(x, y);
     if (!this.options["cannibal"])
@@ -2244,6 +2251,8 @@ export default class ChessRules {
 
   // 'color' arg because some variants (e.g. Refusal) check opponent moves
   filterValid(moves, color) {
+    if (!V.HasKing)
+      return moves;
     color = color || this.turn;
     const oppCols = this.getOppCols(color);
     let kingPos = this.searchKingPos(color);
@@ -2401,9 +2410,11 @@ export default class ChessRules {
     if (move.next)
       return false;
     const color = this.turn;
-    const oppKingPos = this.searchKingPos(C.GetOppTurn(color));
-    if (oppKingPos.length == 0 || this.underCheck(oppKingPos, [color]))
-      return true;
+    if (V.HasKing) {
+      const oppKingPos = this.searchKingPos(C.GetOppTurn(color));
+      if (oppKingPos.length == 0 || this.underCheck(oppKingPos, [color]))
+        return true;
+    }
     return (
       (
         !this.options["balance"] ||
@@ -2453,6 +2464,8 @@ export default class ChessRules {
     // Shortcut in case the score was computed before:
     if (move.result)
       return move.result;
+    if (!V.HasKing)
+      return "*"; //can't guess better..
     const oppTurn = C.GetOppTurn(this.turn);
     const kingPos = {
       w: this.searchKingPos('w'),
index 2c0211e..1104b94 100644 (file)
@@ -54,7 +54,7 @@ function getRandomVariant() {
 if (params.dev) {
   const chokidar = require("chokidar");
   const watcher = chokidar.watch(
-    ["*.js", "*.css", "utils/", "variants/"],
+    ["*.js", "*.css", "css/", "js/", "pieces/", "utils/", "variants/"],
     { persistent: true }
   );
 
index 4ad6f88..fa047eb 100644 (file)
@@ -8,7 +8,7 @@ export default class AlapoRules extends ChessRules {
   static get Options() {
     return {
       select: C.Options.select,
-      styles: C.Options.styles.filter(s => s != "teleport")
+      styles: C.Options.styles.filter(s => !["recycle","teleport"].includes(s))
     };
   }
 
@@ -18,6 +18,9 @@ export default class AlapoRules extends ChessRules {
   get hasEnpassant() {
     return false;
   }
+  static get HasKing() {
+    return false;
+  }
 
   getSvgChessboard() {
     let board = super.getSvgChessboard().slice(0, -6);
@@ -49,7 +52,7 @@ export default class AlapoRules extends ChessRules {
       s.w.map(p => piece2pawn[p].toUpperCase()).join("") + "/" +
       s.w.join("").toUpperCase()
     );
-    return { fen: fen, o: {} };
+    return { fen, o: {} };
   }
 
   // Triangles are rotated from opponent viewpoint (=> suffix "_inv")
@@ -58,8 +61,10 @@ export default class AlapoRules extends ChessRules {
     return {
       'r': allSpecs['r'],
       'q': allSpecs['q'],
-      'b': Object.assign({}, allSpecs['b'],
-        {"class": "bishop" + (this.playerColor != color ? "_inv" : "")}),
+      'b': {
+        "class": "bishop" + (this.playerColor != color ? "_inv" : ""),
+        ...allSpecs['b']
+      },
       's': { //"square"
         "class": "babyrook",
         both: [
index e77bb2c..c380eaa 100644 (file)
@@ -16,6 +16,9 @@ export default class ApocalypseRules extends ChessRules {
   get hideMoves() {
     return true;
   }
+  static get HasKing() {
+    return false;
+  }
 
   pawnPromotions() {
     return ['n', 'p'];
index 1b848c9..120afdc 100644 (file)
@@ -8,7 +8,14 @@ export default class AtarigoRules extends WeiqiRules {
   static get Options() {
     let input = WeiqiRules.Options.input;
     input[0].defaut = 11;
-    return {input: input};
+    return {
+      input,
+      styles: WeiqiRules.Options.styles
+    };
+  }
+
+  static get HasKing() {
+    return false;
   }
 
   getCurrentScore(move_s) {
index 6e9fc3e..13cc55e 100644 (file)
@@ -23,7 +23,8 @@ export default class AvalamRules extends ChessRules {
           type: "checkbox",
           defaut: false
         }
-      ]
+      ],
+      styles: ["doublemove", "progressive"]
     };
   }
 
@@ -33,6 +34,9 @@ export default class AvalamRules extends ChessRules {
   get hasEnpassant() {
     return false;
   }
+  static get HasKing() {
+    return false;
+  }
 
   pieces(color, x, y) {
     const steps = [
index 50ebee0..07502aa 100644 (file)
@@ -14,6 +14,9 @@ export default class DiscoduelRules extends ChessRules {
   get hasFlags() {
     return false;
   }
+  static get HasKing() {
+    return false;
+  }
 
   genRandInitBaseFen() {
     return {
index c08f75a..b34be7b 100644 (file)
@@ -21,6 +21,7 @@ export default class GiveawayRules extends ChessRules {
       input: C.Options.input.filter(i => i.variable == "pawnfall"),
       styles: [
         "atomic", "cannibal", "cylinder", "dark",
+        "doublemove", "progressive",
         "madrasi", "rifle", "teleport", "zen"
       ]
     };
@@ -29,6 +30,9 @@ export default class GiveawayRules extends ChessRules {
   get hasFlags() {
     return this.options["mode"] == "losers";
   }
+  static get HasKing() {
+    return this.options["mode"] == "losers";
+  }
 
   pawnPromotions() {
     let res = ['q', 'r', 'n', 'b'];
index 05200be..22dee0d 100644 (file)
@@ -38,6 +38,9 @@ export default class HexRules extends AbstractClickFillRules {
   get clickOnly() {
     return true;
   }
+  static get HasKing() {
+    return false;
+  }
 
   doClick(coords) {
     if (
index b40ebab..b50ca0b 100644 (file)
@@ -2,58 +2,65 @@ import ChessRules from "/js/base_rules.js";
 
 export default class SleepyRules extends ChessRules {
 
+  static get Options() {
+    return {
+      select: C.Options.select,
+      input: C.Options.input,
+      styles: ["balance", "capture", "cylinder", "dark", "rifle", "zen"]
+    };
+  }
+
+  setOtherVariables(fenParsed) {
+    super.setOtherVariables(fenParsed);
+    this.states = JSON.parse(fenParsed.states);
+  }
+
+  getPartFen(o) {
+    return {
+      "states": (o.init ? '0'.repeat(64) : JSON.stringify(this.states)),
+      ...super.getPartFen(o)
+    };
+  }
+
   pieces(color, x, y) {
-    let res = super.pieces(color, x, y);
-    res['s'] = {"class": "sleepy-pawn", moveas: "p"};
-    res['u'] = {"class": "sleepy-rook", moveas: "r"};
-    res['o'] = {"class": "sleepy-knight", moveas: "n"};
-    res['c'] = {"class": "sleepy-bishop", moveas: "b"};
-    res['t'] = {"class": "sleepy-queen", moveas: "q"};
-    return res;
+    return {
+      's': {"class": "sleepy-pawn"},
+      'u': {"class": "sleepy-rook"},
+      'o': {"class": "sleepy-knight"},
+      'c': {"class": "sleepy-bishop"},
+      't': {"class": "sleepy-queen"},
+      ...super.pieces(color, x, y)
+    };
   }
 
-  static get V_PIECES() {
+  static get M_PIECES() {
     return ['p', 'r', 'n', 'b', 'q'];
   }
   static get S_PIECES() {
     return ['s', 'u', 'o', 'c', 't'];
   }
 
-  // Forbid sleepy pieces to capture
-  canTake([x1, y1], [x2, y2]) {
-    return (
-      this.getColor(x1, y1) !== this.getColor(x2, y2) &&
-      (['k'].concat(V.V_PIECES)).includes(this.getPiece(x1, y1))
-    );
+  getPotentialMovesOf(piece, [x, y]) {
+    if (V.S_PIECES.includes(piece))
+      return [];
+    return super.getPotentialMovesOf(piece, [x, y]);
   }
 
-
-  //TODO:
-
-
-  pawnPostProcess(moves, color, oppCols) {
-    let res = super.pawnPostProcess(moves, color, oppCols);
-    if (res.length > 0 && res[0].vanish[0].p == 's') {
-      // Fix promotions of non-violent pawns (if any)
-      res.forEach(m => {
-        if (m.appear[0].p != 's')
-          m.appear[0].p = V.NV_PIECES[V.V_PIECES.indexOf(m.appear[0].p)];
-      });
-    }
-    return res;
+  getStateIndex(coords) {
+    return this.size.y * coords.x + coords.y;
   }
 
   prePlay(move) {
     super.prePlay(move);
-    // NOTE: drop moves already taken into account in base prePlay()
-    if (move.vanish.length == 2 && move.appear.length == 1) {
-      const normal = V.V_PIECES.includes(move.vanish[1].p);
-      const pIdx =
-        (normal ? V.V_PIECES : V.NV_PIECES).indexOf(move.vanish[1].p);
-      const resPiece = (normal ? V.NV_PIECES : V.V_PIECES)[pIdx];
-      super.updateReserve(C.GetOppTurn(this.turn), resPiece,
-        this.reserve[C.GetOppTurn(this.turn)][resPiece] + 1);
-    }
+    // 1) Wake up observed pieces
+    // TODO: findDestSquares(attackOnly = true) with changing piece color on board
+    // Loop on found squares + if index in S_PIECES then change
+    // 2) Update sleepy status
+    const indices = [move.start, move.end].map(this.getStateIndex);
+    this.states[indices[1]] = this.states[indices[0]] + 1;
+    this.states[indices[0]] = 0;
+    if (this.states[indices[1]] >= 3)
+      move.appear[0].p = V.S_PIECES[V.M_PIECES.indexOf(move.appear[0].p)];
   }
 
 };
index 6f6b1d8..f9ca469 100644 (file)
@@ -20,7 +20,8 @@ export default class WeiqiRules extends ChessRules {
           type: "checkbox",
           defaut: false
         }
-      ]
+      ],
+      styles: ["doublemove", "progressive"]
     };
   }
 
@@ -33,6 +34,9 @@ export default class WeiqiRules extends ChessRules {
   get clickOnly() {
     return true;
   }
+  static get HasKing() {
+    return false;
+  }
 
   getSvgChessboard() {
     const flipped = (this.playerColor == 'b');