Add Giveaway. Preparing for Chakart a little more
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 4 Jun 2022 14:47:12 +0000 (16:47 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 4 Jun 2022 14:47:12 +0000 (16:47 +0200)
app.js
base_rules.js
variants.js
variants/Absorption/class.js
variants/Atomic/class.js
variants/Chakart/class.js
variants/Giveaway/class.js [new file with mode: 0644]
variants/Giveaway/rules.html [new file with mode: 0644]
variants/Giveaway/style.css [new file with mode: 0644]

diff --git a/app.js b/app.js
index 79aa6e0..09d7815 100644 (file)
--- a/app.js
+++ b/app.js
@@ -454,7 +454,7 @@ function notifyMe(code) {
 
 let curMoves = [],
     lastFen;
-const afterPlay = (move) => {
+const afterPlay = (move_s) => {
   const callbackAfterSend = () => {
     curMoves = [];
     const result = vr.getCurrentScore(move);
@@ -466,7 +466,12 @@ const afterPlay = (move) => {
     }
   };
   // Pack into one moves array, then send
-  curMoves.push(move);
+  if (Array.isArray(move_s))
+    // Array of simple moves (e.g. Chakart)
+    Array.prototype.push.apply(curMoves, move_s);
+  else
+    // Usual case
+    curMoves.push(move_s);
   if (vr.turn != playerColor) {
     toggleTurnIndicator(false);
     send("newmove",
index 30e9ee5..dac7bb4 100644 (file)
@@ -175,8 +175,6 @@ export default class ChessRules {
 
   // Setup the initial random-or-not (asymmetric-or-not) position
   genRandInitFen(seed) {
-    Random.setSeed(seed);
-
     let fen, flags = "0707";
     if (!this.options.randomness)
       // Deterministic:
@@ -184,6 +182,7 @@ export default class ChessRules {
 
     else {
       // Randomize
+      Random.setSeed(seed);
       let pieces = {w: new Array(8), b: new Array(8)};
       flags = "";
       // Shuffle pieces on first (and last rank if randomness == 2)
index 3fced3d..13dac98 100644 (file)
@@ -62,6 +62,7 @@ const variants = [
 //  {name: 'Fugue', desc: 'Baroque Music'},
 //  {name: 'Fullcavalry', desc: 'Lancers everywhere', disp: 'Full Cavalry'},
 //  {name: 'Fusion', desc: 'Fusion pieces (v1)'},
+  {name: 'Giveaway', desc: 'Lose all pieces'},
 //  {name: 'Gomoku', desc: 'Align five stones'},
 //  {name: 'Grand', desc: 'Big board'},
 //  {name: 'Grasshopper', desc: 'Long jumps over pieces'},
@@ -86,7 +87,6 @@ const variants = [
 //  {name: 'Konane', desc: 'Hawaiian Checkers'},
 //  {name: 'Koopa', desc: 'Stun & kick pieces'},
 //  {name: 'Koth', desc: 'King of the Hill', disp:'King of the Hill'},
-//  {name: 'Losers', desc: 'Get strong at self-mate'},
 //  {name: 'Madhouse', desc: 'Rearrange enemy pieces'},
   {name: 'Madrasi', desc: 'Paralyzed pieces'},
 //  {name: 'Magnetic', desc: 'Laws of attraction'},
@@ -137,7 +137,6 @@ const variants = [
 //  {name: 'Spartan', desc: 'Spartan versus Persians'},
 //  {name: 'Squatter', desc: 'Squat last rank'},
 //  {name: 'Stealthbomb', desc: 'Beware the bomb'},
-//  {name: 'Suicide', desc: 'Lose all pieces'},
 //  {name: 'Suction', desc: 'Attract opposite king'},
 //  {name: 'Swap', desc: 'Dangerous captures'},
 //  {name: 'Switching', desc: "Exchange pieces' positions"},
index 3e8d391..66278db 100644 (file)
@@ -5,6 +5,7 @@ export default class AbsorptionRules extends ChessRules {
   static get Options() {
     return {
       select: C.Options.select,
+      input: C.Options.input,
       styles: [
         "balance",
         "capture",
index cc339ff..5c73de2 100644 (file)
@@ -13,14 +13,8 @@ export default class AtomicRules extends ChessRules {
           variable: "rempawn",
           type: "checkbox",
           defaut: false
-        },
-        {
-          label: "Falling pawn",
-          variable: "pawnfall",
-          type: "checkbox",
-          defaut: false
         }
-      ],
+      ].concat(C.Options.input.filter(i => i.variable == "pawnfall")),
       styles: C.Options.styles.filter(s => s != "atomic")
     };
   }
index 69a2e4c..3cc4a1e 100644 (file)
@@ -1,16 +1,13 @@
 import ChessRules from "/base_rules";
 import GiveawayRules from "/variants/Giveaway";
+import { ArrayFun } from "/utils/array.js";
+import { Random } from "/utils/alea.js";
+import PiPo from "/utils/PiPo.js";
+import Move from "/utils/Move.js";
 
 // TODO + display bonus messages
 // + animation + multi-moves for bananas/bombs/mushrooms
 
-
-
-import { ArrayFun } from "/utils/array";
-import { randInt } from "/utils/alea";
-import PiPo from "/utils/PiPo.js";
-import Move from "/utils/Move.js";
-
 export class ChakartRules extends ChessRules {
 
   static get Options() {
@@ -30,9 +27,10 @@ export class ChakartRules extends ChessRules {
     };
   }
 
-  static get PawnSpecs() {
-    return SuicideRules.PawnSpecs;
+  get pawnPromotions() {
+    return ['q', 'r', 'n', 'b', 'k'];
   }
+
   get hasCastle() {
     return false;
   }
@@ -80,7 +78,7 @@ export class ChakartRules extends ChessRules {
     return 'm';
   }
 
-  static fen2board(f) {
+  fen2board(f) {
     return (
       f.charCodeAt() <= 90
         ? "w" + f.toLowerCase()
@@ -88,89 +86,13 @@ export class ChakartRules extends ChessRules {
     );
   }
 
-  static get PIECES() {
-    return (
-      ChessRules.PIECES.concat(
-      Object.keys(V.IMMOBILIZE_DECODE)).concat(
-      [V.BANANA, V.BOMB, V.EGG, V.MUSHROOM, V.INVISIBLE_QUEEN])
-    );
-  }
-
-  getPpath(b) {
-    let prefix = "";
-    if (
-      b[0] == 'a' ||
-      b[1] == V.INVISIBLE_QUEEN ||
-      Object.keys(V.IMMOBILIZE_DECODE).includes(b[1])
-    ) {
-      prefix = "Chakart/";
-    }
-    return prefix + b;
-  }
-
-  getPPpath(m) {
-    if (!!m.promoteInto) return m.promoteInto;
-    if (m.appear.length == 0 && m.vanish.length == 1)
-      // King 'remote shell capture', on an adjacent square:
-      return this.getPpath(m.vanish[0].c + m.vanish[0].p);
-    let piece = m.appear[0].p;
-    if (Object.keys(V.IMMOBILIZE_DECODE).includes(piece))
-      // Promotion by capture into immobilized piece: do not reveal!
-      piece = V.IMMOBILIZE_DECODE[piece];
-    return this.getPpath(m.appear[0].c + piece);
-  }
-
-  static ParseFen(fen) {
-    const fenParts = fen.split(" ");
-    return Object.assign(
-      ChessRules.ParseFen(fen),
-      { captured: fenParts[4] }
-    );
-  }
-
-  static IsGoodFen(fen) {
-    if (!ChessRules.IsGoodFen(fen)) return false;
-    const captured = V.ParseFen(fen).captured;
-    if (!captured || !captured.match(/^[0-9]{12,12}$/)) return false;
-    return true;
-  }
-
-  // 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, '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], 10);
-          if (isNaN(num)) return false;
-          sumElts += num;
-        }
-      }
-      if (sumElts != V.size.y) return false;
-    }
-    if (kings['k'] + kings['l'] == 0 || kings['K'] + kings['L'] == 0)
-      return false;
-    return true;
-  }
-
-  static IsGoodFlags(flags) {
-    // 4 for Peach + Mario w, b
-    return !!flags.match(/^[01]{4,4}$/);
-  }
-
   setFlags(fenflags) {
     // King can send shell? Queen can be invisible?
     this.powerFlags = {
-      w: { 'k': false, 'q': false },
-      b: { 'k': false, 'q': false }
+      w: {k: false, q: false},
+      b: {k: false, q: false}
     };
-    for (let c of ["w", "b"]) {
+    for (let c of ['w', 'b']) {
       for (let p of ['k', 'q']) {
         this.powerFlags[c][p] =
           fenflags.charAt((c == "w" ? 0 : 2) + (p == 'k' ? 0 : 1)) == "1";
@@ -190,50 +112,28 @@ export class ChakartRules extends ChessRules {
     return super.getFen() + " " + this.getCapturedFen();
   }
 
-  getFenForRepeat() {
-    return super.getFenForRepeat() + "_" + this.getCapturedFen();
-  }
-
   getCapturedFen() {
-    let counts = [...Array(12).fill(0)];
-    let i = 0;
-    for (let p of V.RESERVE_PIECES) {
-      counts[i] = this.captured["w"][p];
-      counts[6 + i] = this.captured["b"][p];
-      i++;
-    }
-    return counts.join("");
+    const res = ['w', 'b'].map(c => {
+      Object.values(this.captured[c])
+    });
+    return res[0].concat(res[1]).join("");
   }
 
-  scanKings() {}
-
-  setOtherVariables(fen) {
-    super.setOtherVariables(fen);
+  setOtherVariables(fenParsed) {
+    super.setOtherVariables(fenParsed);
     // Initialize captured pieces' counts from FEN
-    const captured =
-      V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10));
+    const allCapts = fenParsed.captured.split("").map(x => parseInt(x, 10));
+    const pieces = ['p', 'r', 'n', 'b', 'q', 'k'];
     this.captured = {
-      w: {
-        [V.PAWN]: captured[0],
-        [V.ROOK]: captured[1],
-        [V.KNIGHT]: captured[2],
-        [V.BISHOP]: captured[3],
-        [V.QUEEN]: captured[4],
-        [V.KING]: captured[5]
-      },
-      b: {
-        [V.PAWN]: captured[6],
-        [V.ROOK]: captured[7],
-        [V.KNIGHT]: captured[8],
-        [V.BISHOP]: captured[9],
-        [V.QUEEN]: captured[10],
-        [V.KING]: captured[11]
-      }
+      w: Array.toObject(pieces, allCapts.slice(0, 6)),
+      b: Array.toObject(pieces, allCapts.slice(6, 12))
     };
     this.effects = [];
-    this.subTurn = 1;
   }
 
+
+  // TODO from here ::::::::
+
   getFlagsFen() {
     let fen = "";
     // Add power flags
@@ -242,20 +142,6 @@ export class ChakartRules extends ChessRules {
     return fen;
   }
 
-  getColor(i, j) {
-    if (i >= V.size.x) return i == V.size.x ? "w" : "b";
-    return this.board[i][j].charAt(0);
-  }
-
-  getPiece(i, j) {
-    if (i >= V.size.x) return V.RESERVE_PIECES[j];
-    return this.board[i][j].charAt(1);
-  }
-
-  getReservePpath(index, color) {
-    return color + V.RESERVE_PIECES[index];
-  }
-
   static get RESERVE_PIECES() {
     return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
   }
@@ -1167,7 +1053,7 @@ export class ChakartRules extends ChessRules {
   }
 
   genRandInitFen(seed) {
-    const gr = new GiveawayRules({}, true);
+    const gr = new GiveawayRules({mode: "suicide"}, true);
     return (
       gr.genRandInitFen(seed).slice(0, -1) +
       // Add Peach + Mario flags + capture counts
diff --git a/variants/Giveaway/class.js b/variants/Giveaway/class.js
new file mode 100644 (file)
index 0000000..77f5c74
--- /dev/null
@@ -0,0 +1,96 @@
+import ChessRules from "/base_rules.js";
+import { ArrayFun } from "/utils/array.js";
+import { Random } from "/utils/alea.js";
+
+export default class GiveawayRules extends ChessRules {
+
+  static get Options() {
+    return {
+      select: [
+        {
+          label: "Mode",
+          variable: "mode",
+          defaut: "suicide",
+          options: [
+            {label: "Suicide", value: "suicide"},
+            {label: "Losers", value: "losers"}
+          ]
+        }
+      ].concat(C.Options.select),
+      input: C.Options.input.filter(i => i.variable == "pawnfall"),
+      styles: [
+        "atomic", "cannibal", "cylinder", "dark",
+        "madrasi", "rifle", "teleport", "zen"
+      ]
+    };
+  }
+
+  get hasFlags() {
+    return this.options["mode"] == "losers";
+  }
+
+  get pawnPromotions() {
+    let res = ['q', 'r', 'n', 'b'];
+    if (this.options["mode"] == "suicide")
+      res.push('k');
+    return res;
+  }
+
+  genRandInitFen(seed) {
+    if (this.options["randomness"] == 0) {
+      return (
+        'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 {"enpassant":"-"}'
+      );
+    }
+
+    Random.setSeed(seed);
+    let pieces = { w: new Array(8), b: new Array(8) };
+    for (let c of ["w", "b"]) {
+      if (c == 'b' && this.options["randomness"] == 1) {
+        pieces['b'] = pieces['w'];
+        break;
+      }
+
+      // Get random squares for every piece, totally freely
+      let positions = Random.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]];
+            break;
+          }
+        }
+      }
+      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 {"enpassant":"-"}'
+    );
+  }
+
+  constructor(o) {
+    o.options["capture"] = true;
+    super(o);
+  }
+
+  underCheck([x, y], oppCol) {
+    if (this.options["mode"] == "suicide")
+      return false;
+    return super.underCheck([x, y], oppCol);
+  }
+
+  getCurrentScore() {
+    if (this.atLeastOneMove()) return "*";
+    // No valid move: the side who cannot move wins
+    return (this.turn == "w" ? "1-0" : "0-1");
+  }
+
+};
diff --git a/variants/Giveaway/rules.html b/variants/Giveaway/rules.html
new file mode 100644 (file)
index 0000000..920b382
--- /dev/null
@@ -0,0 +1 @@
+<p>Win by losing all your material, or get stalemated.</p>
diff --git a/variants/Giveaway/style.css b/variants/Giveaway/style.css
new file mode 100644 (file)
index 0000000..a3550bc
--- /dev/null
@@ -0,0 +1 @@
+@import url("/base_pieces.css");