Emergo: big step forward main
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 30 May 2026 23:07:35 +0000 (01:07 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 30 May 2026 23:07:35 +0000 (01:07 +0200)
README.md
TODO
initialize.sh
js/base_rules.js
pieces/Emergo/.gitignore [deleted file]
pieces/Emergo/generateSVG.py [deleted file]
variants/Avalam/generateSVG.py [moved from pieces/Avalam/generateSVG.py with 100% similarity]
variants/Emergo/class.js
variants/Emergo/rules.html [new file with mode: 0644]
variants/Emergo/style.css

index 8ac79c3..a62b739 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,22 +1,22 @@
 # xogo.casa
 
-Simplified version of old vchess.club, to focus on the essential : the game.
+Simplified version of old vchess.club, to focus on the essential.
 
 ## Requirements (dev)
 
-PHP + Python + Node.js + npm.
+PHP + Node.js + npm.
+
 ```npm i -g nodemon```
 
 ## Usage
 
-Initialisation (done once): retrieve 'binaries'
-(files binary or not which never change).
+Initialisation (done only once):
 
 ```./initialize.sh```
 
-Rename and edit the parameters.js.dist file:
+Edit the parameters.js file:
 
-```cp js/parameters.js.dist js/parameters.js```
+```vim js/parameters.js```
 
 Finally:
 
diff --git a/TODO b/TODO
index 7114f80..81f9dfb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,3 @@
-Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element with 2 SVG or SVG + number
-==> plus simple : deux classes, images superposées (?)
-
 https://fr.wikipedia.org/wiki/Unlur
 
 DuckChess + crazyduck --> move it at random ? Capturing ? Hmm.
index a7fd40d..5b9300f 100755 (executable)
@@ -5,4 +5,3 @@ wget https://xogo.casa/extras.zip && unzip extras.zip
 cp js/parameters.js.dist js/parameters.js
 npm i
 cd pieces/Avalam && python generateSVG.py && cd ../..
-cd pieces/Emergo && python generateSVG.py && cd ../..
index 1a7543c..e5900af 100644 (file)
@@ -505,6 +505,9 @@ export default class ChessRules {
       (oldV,newV) => oldV + (this.reserve[c][newV] > 0 ? 1 : 0), 0);
   }
 
+  // Placeholder for variants building SVG pieces "on demand"
+  setPieceBackground(domPiece, piece, color, x, y) {}
+
   static AddClass_es(elt, class_es) {
     if (!Array.isArray(class_es))
       class_es = [class_es];
@@ -636,9 +639,11 @@ export default class ChessRules {
     if (!r)
       r = chessboard.getBoundingClientRect();
     const pieceWidth = this.getPieceWidth(r.width);
-    const addPiece = (i, j, arrName, classes) => {
+    const addPiece = (i, j, arrName, classes, piece, color) => {
       this[arrName][i][j] = document.createElement("piece");
       C.AddClass_es(this[arrName][i][j], classes);
+      if (!!piece)
+        this.setPieceBackground(this[arrName][i][j], piece, color, i, j);
       this[arrName][i][j].style.width = pieceWidth + "px";
       this[arrName][i][j].style.height = pieceWidth + "px";
       let [ip, jp] = this.getPixelPosition(i, j, r);
@@ -661,11 +666,12 @@ export default class ChessRules {
       }
       else
         this[arrName] = ArrayFun.init(this.size.x, this.size.y, null);
-      if (arrName == "d_pieces")
+      if (arrName == "d_pieces") {
         this.marks.forEach((m) => {
           const mCoords = this.coordsFromUsual(m);
           addPiece(mCoords.x, mCoords.y, arrName, "mark");
         });
+      }
     };
     if (this.marks)
       conditionalReset("d_pieces");
@@ -676,7 +682,7 @@ export default class ChessRules {
           const color = this.getColor(i, j);
           const piece = this.getPiece(i, j);
           addPiece(i, j, "g_pieces",
-                   this.pieceDef(piece, color, i, j)["class"]);
+                   this.pieceDef(piece, color, i, j)["class"], piece, color);
           this.g_pieces[i][j].classList.add(V.GetColorClass(color));
           if (this.enlightened && !this.enlightened[i][j])
             this.g_pieces[i][j].classList.add("hidden");
@@ -744,6 +750,7 @@ export default class ChessRules {
         rcontainer.appendChild(r_cell);
         let piece = document.createElement("piece");
         C.AddClass_es(piece, this.pieceDef(p, c, c, p)["class"]);
+        this.setPieceBackground(piece, p, c, c, p);
         piece.classList.add(V.GetColorClass(c));
         piece.style.width = "100%";
         piece.style.height = "100%";
@@ -1063,6 +1070,8 @@ export default class ChessRules {
       const cdisp = moves[i].choice || moves[i].appear[0].p;
       C.AddClass_es(piece,
         this.pieceDef(cdisp, color, moves[i].end.x, moves[i].end.y)["class"]);
+      this.setPieceBackground(piece,
+        cdisp, color, moves[i].end.x, moves[i].end.y);
       piece.classList.add(V.GetColorClass(color));
       piece.style.width = "100%";
       piece.style.height = "100%";
@@ -2518,6 +2527,7 @@ export default class ChessRules {
       this.g_pieces[a.x][a.y] = document.createElement("piece");
       C.AddClass_es(this.g_pieces[a.x][a.y],
                     this.pieceDef(a.p, a.c, a.x, a.y)["class"]);
+      this.setPieceBackground(this.g_pieces[a.x][a.y], a.p, a.c, a.x, a.y);
       this.g_pieces[a.x][a.y].classList.add(V.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";
@@ -2598,6 +2608,8 @@ export default class ChessRules {
         this.pieceDef(startCode, apparentColor, start.x, start.y)["class"]);
       C.AddClass_es(movingPiece,
         this.pieceDef(drag.p, apparentColor, start.x, start.y)["class"]);
+      this.setPieceBackground(movingPiece,
+        drag.p, apparentColor, start.x, start.y);
       if (apparentColor != drag.c) {
         movingPiece.classList.remove(V.GetColorClass(apparentColor));
         movingPiece.classList.add(V.GetColorClass(drag.c));
diff --git a/pieces/Emergo/.gitignore b/pieces/Emergo/.gitignore
deleted file mode 100644 (file)
index 756b22f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*.svg
diff --git a/pieces/Emergo/generateSVG.py b/pieces/Emergo/generateSVG.py
deleted file mode 100755 (executable)
index 258af71..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/usr/bin/env python
-
-# Compose each piece SVG with numbers
-# https://travishorn.com/removing-parts-of-shapes-in-svg-b539a89e5649
-# https://developer.mozilla.org/fr/docs/Web/SVG/Tutoriel/Paths
-
-###############################
-# 1. Simple pieces (mono-color)
-###############################
-
-preamble_simple = """<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="230" height="230">"""
-
-black = '<circle cx="115" cy="115" r="100" fill="black"/>'
-white = '<circle cx="115" cy="115" r="100" fill="whitesmoke" stroke="saddlebrown"/>'
-
-digits = [
-    # 1 (unused)
-    '<path d="M130,85 v60"',
-    # 2
-    '<path d="M100,85 h30 v30 h-30 v30 h30"',
-    # 3
-    '<path d="M100,85 h30 v30 h-30 M130,115 v30 h-30"',
-    # 4
-    '<path d="M100,85 v30 h30 v30 M130,85 v30"',
-    # 5
-    '<path d="M130,85 h-30 v30 h30 v30 h-30"',
-    # 6
-    '<path d="M130,85 h-30 v60 h30 v-30 h-30"',
-    # 7
-    '<path d="M100,85 h30 v60"',
-    # 8
-    '<path d="M100,85 h30 v60 h-30 z M100,115 h30"',
-    # 9
-    '<path d="M100,135 h30 v-60 h-30 v30 h30"',
-    # 10
-    '<path d="M95,85 v60 M105,85 h30 v60 h-30 v-60"',
-    # 11
-    '<path d="M95,85 v60 M135,85 v60"',
-    # 12
-    '<path d="M95,85 v60 M105,85 h30 v30 h-30 v30 h30"'
-]
-
-final = "</svg>"
-
-for color in ["white", "black"]:
-    chrShift = 0 if color == "white" else 32
-    for number in range(12):
-        filename = chr(65 + number + chrShift) + "@.svg"
-        f = open(filename, "w")
-        f.write(preamble_simple)
-        f.write("\n")
-        f.write(white if color == "white" else black)
-        f.write("\n")
-        if number >= 1:
-            f.write(digits[number] + ' fill="none" stroke-width="5" ' + ('stroke="red"' if color == "white" else 'stroke="orange"') + '/>')
-            f.write("\n")
-        f.write(final)
-        f.close()
-
-################################
-# 1. Composite pieces (bi-color)
-################################
-
-preamble_composite = """<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="230" height="230">
-<defs>
-  <mask id="stripe">
-    <rect x="0" y="0" width="230" height="230" fill="white"/>
-    <rect x="0" y="115" width="230" height="115"/>
-  </mask>
-</defs>"""
-
-black_top = '<circle cx="115" cy="115" r="100" fill="black" mask="url(#stripe)"/>'
-white_bottom = '<circle cx="115" cy="115" r="100" fill="whitesmoke" stroke="saddlebrown"/>'
-white_top = '<circle cx="115" cy="115" r="100" fill="whitesmoke" stroke="saddlebrown" mask="url(#stripe)"/>'
-black_bottom = '<circle cx="115" cy="115" r="100" fill="black"/>'
-
-digits = {
-    "top": [
-        # 1 (unused here)
-        '<path d="M130,35 v60"',
-        # 2
-        '<path d="M100,35 h30 v30 h-30 v30 h30"',
-        # 3
-        '<path d="M100,35 h30 v30 h-30 M130,65 v30 h-30"',
-        # 4
-        '<path d="M100,35 v30 h30 v30 M130,35 v30"',
-        # 5
-        '<path d="M130,35 h-30 v30 h30 v30 h-30"',
-        # 6
-        '<path d="M130,35 h-30 v60 h30 v-30 h-30"',
-        # 7
-        '<path d="M100,35 h30 v60"',
-        # 8
-        '<path d="M100,35 h30 v60 h-30 z M100,65 h30"',
-        # 9
-        '<path d="M100,95 h30 v-60 h-30 v30 h30"',
-        # 10
-        '<path d="M90,35 v60 M100,35 h30 v60 h-30 v-60"',
-        # 11
-        '<path d="M90,35 v60 M130,35 v60"',
-        # 12
-        '<path d="M90,35 v60 M100,35 h30 v30 h-30 v30 h30"'
-    ],
-    "bottom": [
-        # 1 (unused here)
-        '<path d="M130,135 v60"',
-        # 2
-        '<path d="M100,135 h30 v30 h-30 v30 h30"',
-        # 3
-        '<path d="M100,135 h30 v30 h-30 M130,165 v30 h-30"',
-        # 4
-        '<path d="M100,135 v30 h30 v30 M130,135 v30"',
-        # 5
-        '<path d="M130,135 h-30 v30 h30 v30 h-30"',
-        # 6
-        '<path d="M130,135 h-30 v60 h30 v-30 h-30"',
-        # 7
-        '<path d="M100,135 h30 v60"',
-        # 8
-        '<path d="M100,135 h30 v60 h-30 z M100,165 h30"',
-        # 9
-        '<path d="M100,195 h30 v-60 h-30 v30 h30"',
-        # 10
-        '<path d="M90,135 v60 M100,135 h30 v60 h-30 v-60"',
-        # 11
-        '<path d="M90,135 v60 M130,135 v60"',
-        # 12
-        '<path d="M90,135 v60 M100,135 h30 v30 h-30 v30 h30"'
-    ]
-}
-
-final = "</svg>"
-
-for colorTop in ["white", "black"]:
-    chrShift = 0 if colorTop == "white" else 32
-    for top in range(12):
-        for bottom in range(12):
-            filename = chr(65 + top + chrShift) + chr(65 + bottom + chrShift) + ".svg"
-            f = open(filename, "w")
-            f.write(preamble_composite)
-            f.write("\n")
-            f.write(black_bottom if colorTop == "white" else white_bottom)
-            f.write("\n")
-            f.write(white_top if colorTop == "white" else black_top)
-            f.write("\n")
-            if top >= 1:
-                f.write(digits["top"][top] + ' fill="none" stroke-width="5" ' + ('stroke="red"' if colorTop == "white" else 'stroke="orange"') + '/>')
-                f.write("\n")
-            if bottom >= 1:
-                f.write(digits["bottom"][bottom] + ' fill="none" stroke-width="5" ' + ('stroke="red"' if colorTop == "black" else 'stroke="orange"') + '/>')
-                f.write("\n")
-            f.write(final)
-            f.close()
index 3c29f33..4084edc 100644 (file)
@@ -53,23 +53,8 @@ export default class EmergoRules extends ChessRules {
     return board;
   }
 
-  getColor(x, y) {
-    const sq = (typeof x == "string" ? x : this.board[x][y]);
-    return sq.charCodeAt(0) < 97 ? 'w' : 'b';
-  }
-
-  pieceDef(piece, color, x, y) {
-    const color = this.getColor(piece);
-    piece.charAt(0)
-    //this.board[x][y]
-    // --> TODO
-    // Moving always the same, but look differs
-    // class: classUp, class: classDOwn + composition
-  }
-
-  getPiece(x, y) {
-    // Both characters required to describe the (aggregated) "piece"
-    return this.board[x][y];
+  getSquareColorClass(x, y) {
+    return ((x+y) % 2 == 0 ? "dark-square": "light-square");
   }
 
   get size() {
@@ -88,6 +73,183 @@ export default class EmergoRules extends ChessRules {
     super.setOtherVariables(fenParsed);
     // Last capture during a turn (square + direction)
     this.lastCapture = null;
+    // Compute pieces drawings when required, and cache it:
+    this.svgEncodedPieces = {};
+  }
+
+  getColor(x, y) {
+    const sq = (typeof x == "string" ? x : this.board[x][y]);
+    return sq.charCodeAt(0) < 97 ? 'w' : 'b';
+  }
+
+  getPiece(x, y) {
+    // Both characters required to describe the (aggregated) "piece"
+    return this.board[x][y];
+  }
+
+  pieceDef(piece, color, x, y) {
+    // Captures handled in getPotentialMoves directly
+    return {
+      moves: [{ steps: [ [-1, -1], [-1, 1], [1, -1], [1, 1] ] }],
+      "class": "emergo-piece",
+    };
+  }
+
+  static GetSvgNumber(n, position) {
+    switch (position) {
+    case "full": {
+      switch (n) {
+      case 1: //unused
+        return '<path d="M130,85 v60"';
+      case 2:
+        return '<path d="M100,85 h30 v30 h-30 v30 h30"';
+      case 3:
+        return '<path d="M100,85 h30 v30 h-30 M130,115 v30 h-30"';
+      case 4:
+        return '<path d="M100,85 v30 h30 v30 M130,85 v30"';
+      case 5:
+        return '<path d="M130,85 h-30 v30 h30 v30 h-30"';
+      case 6:
+        return '<path d="M130,85 h-30 v60 h30 v-30 h-30"';
+      case 7:
+        return '<path d="M100,85 h30 v60"';
+      case 8:
+        return '<path d="M100,85 h30 v60 h-30 z M100,115 h30"';
+      case 9:
+        return '<path d="M100,135 h30 v-60 h-30 v30 h30"';
+      case 10:
+        return '<path d="M95,85 v60 M105,85 h30 v60 h-30 v-60"';
+      case 11:
+        return '<path d="M95,85 v60 M135,85 v60"';
+      case 12:
+        return '<path d="M95,85 v60 M105,85 h30 v30 h-30 v30 h30"';
+      }
+    }
+    case "top": {
+      switch (n) {
+      case 1: //unused here
+        return '<path d="M130,35 v60"';
+      case 2:
+        return '<path d="M100,35 h30 v30 h-30 v30 h30"';
+      case 3:
+        return '<path d="M100,35 h30 v30 h-30 M130,65 v30 h-30"';
+      case 4:
+        return '<path d="M100,35 v30 h30 v30 M130,35 v30"';
+      case 5:
+        return '<path d="M130,35 h-30 v30 h30 v30 h-30"';
+      case 6:
+        return '<path d="M130,35 h-30 v60 h30 v-30 h-30"';
+      case 7:
+        return '<path d="M100,35 h30 v60"';
+      case 8:
+        return '<path d="M100,35 h30 v60 h-30 z M100,65 h30"';
+      case 9:
+        return '<path d="M100,95 h30 v-60 h-30 v30 h30"';
+      case 10:
+        return '<path d="M90,35 v60 M100,35 h30 v60 h-30 v-60"';
+      case 11:
+        return '<path d="M90,35 v60 M130,35 v60"';
+      case 12:
+        return '<path d="M90,35 v60 M100,35 h30 v30 h-30 v30 h30"';
+      }
+    }
+    case "bottom": {
+      switch (n) {
+      case 1: //unused here
+        return '<path d="M130,135 v60"';
+      case 2:
+        return '<path d="M100,135 h30 v30 h-30 v30 h30"';
+      case 3:
+        return '<path d="M100,135 h30 v30 h-30 M130,165 v30 h-30"';
+      case 4:
+        return '<path d="M100,135 v30 h30 v30 M130,135 v30"';
+      case 5:
+        return '<path d="M130,135 h-30 v30 h30 v30 h-30"';
+      case 6:
+        return '<path d="M130,135 h-30 v60 h30 v-30 h-30"';
+      case 7:
+        return '<path d="M100,135 h30 v60"';
+      case 8:
+        return '<path d="M100,135 h30 v60 h-30 z M100,165 h30"';
+      case 9:
+        return '<path d="M100,195 h30 v-60 h-30 v30 h30"';
+      case 10:
+        return '<path d="M90,135 v60 M100,135 h30 v60 h-30 v-60"';
+      case 11:
+        return '<path d="M90,135 v60 M130,135 v60"';
+      case 12:
+        return '<path d="M90,135 v60 M100,135 h30 v30 h-30 v30 h30"';
+      }
+    }
+    return ""; //never reached
+  }
+
+  // Compute piece drawing
+  getSvgEncodedPiece(piece, color) {
+    if (this.svgPieces[piece])
+      return this.svgPieces[piece];
+
+    let rawSvg = `
+      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 230 230"
+           width="100%" height="100%">`;
+    if (piece.charAt(1) == '@') {
+      const number = ...
+      rawSvg += `
+        <circle cx="115" cy="115" r="100"
+                fill="${color=='w' ? 'whitesmoke' : 'black'}"
+                ${color=='w' ? 'stroke="saddlebrown"' : ''}
+        />`;
+      if (number > 1) {
+        rawSvg += V.GetSvgNumber(number, "full") + `
+                    fill="none" stroke-width="5"
+                    stroke="${color=='w' ? 'red' : 'orange'}"
+                  />`;
+      }
+    }
+    else { //composite
+      const numTop = ...,
+            numBottom = ...;
+      const isTopWhite = 
+      rawSvg += `<defs>
+        <mask id="stripe">
+          <rect width="230" height="230" fill="white"/>
+          <rect y="115" width="230" height="115"/>
+        </mask>
+      </defs>
+      <circle cx="115" cy="115" r="100"
+              fill="${color=='b' ? 'whitesmoke' : 'black'}"
+              ${color=='b' ? 'stroke="saddlebrown"' : ''}
+      />
+      <circle cx="115" cy="115" r="100"
+              fill="${color=='w' ? 'whitesmoke' : 'black'}"
+              ${color=='w' ? 'stroke="saddlebrown"' : ''} mask="url(#stripe)"
+      />`;
+      if (numTop > 1) {
+        rawSvg += V.GetSvgNumber(numTop, "top") + `
+                    fill="none" stroke-width="5"
+                    stroke="${color=='w' ? 'red' : 'orange'}"
+                  />`
+      }
+      if (numBottom > 1) {
+        rawSvg += V.GetSvgNumber(numBottom, "bottom") + `
+                    fill="none" stroke-width="5"
+                    stroke="${color=='b' ? 'red' : 'orange'}"
+                  />`;
+      }
+    }
+    rawSvg += "</svg>";
+
+    // On encode le SVG pour qu'il soit lisible dans un "url()" CSS
+    const svgBase64 = btoa(unescape(encodeURIComponent(rawSvg)));
+    this.svgEncodedPieces[piece] = svgBase64;
+    return svgBase64;
+  }
+
+  // Draw piece
+  setPieceBackground(domPiece, piece, color, x, y) {
+    const svgBase64 = this.getSvgEncodedPiece(piece, color);
+    domPiece.style.backgroundImage =
+      `url('data:image/svg+xml;base64,${svgBase64}')`;
   }
 
   atLeastOneCaptureFrom([x, y], color, forbiddenStep) {
@@ -260,13 +422,6 @@ export default class EmergoRules extends ChessRules {
     return mv;
   }
 
-  getSquareColorClass(x, y) {
-    return ((x+y) % 2 == 0 ? "dark-square": "light-square");
-  }
-
-
-
-
   getDropMovesFrom([c, p]) {
     const color = c;
     if (!this.reserve[color] || this.atLeastOneCapture(color))
diff --git a/variants/Emergo/rules.html b/variants/Emergo/rules.html
new file mode 100644 (file)
index 0000000..c65158e
--- /dev/null
@@ -0,0 +1 @@
+<p>TODO</p>
index 9650899..1cc5116 100644 (file)
@@ -1,35 +1,5 @@
-piece.white.stack {
-  background-image: url('/pieces/Emergo/white_.svg');
-}
-
-
-
-
-piece.white.stack2 {
-  background-image: url('/pieces/Avalam/white_stack2.svg');
-}
-piece.white.stack3 {
-  background-image: url('/pieces/Avalam/white_stack3.svg');
-}
-piece.white.stack4 {
-  background-image: url('/pieces/Avalam/white_stack4.svg');
-}
-piece.white.stack5 {
-  background-image: url('/pieces/Avalam/white_stack5.svg');
-}
-
-piece.black.stack {
-  background-image: url('/pieces/Avalam/black_stack.svg');
-}
-piece.black.stack2 {
-  background-image: url('/pieces/Avalam/black_stack2.svg');
-}
-piece.black.stack3 {
-  background-image: url('/pieces/Avalam/black_stack3.svg');
-}
-piece.black.stack4 {
-  background-image: url('/pieces/Avalam/black_stack4.svg');
-}
-piece.black.stack5 {
-  background-image: url('/pieces/Avalam/black_stack5.svg');
+.emergo-piece {
+  background-image: var(--piece-img);
+  /*background-size: contain;
+  background-repeat: no-repeat;*/
 }