Finished letter E main
authorBenjamin Auder <benjamin.auder@somewhere>
Fri, 19 Jun 2026 13:11:06 +0000 (15:11 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Fri, 19 Jun 2026 13:11:06 +0000 (15:11 +0200)
12 files changed:
js/base_rules.js
pieces/black_nightrider.svg [new file with mode: 0644]
pieces/white_nightrider.svg [new file with mode: 0644]
variants/Enpassant/class.js
variants/Enpassant/rules.html
variants/Enpassant/style.css
variants/Evolution/class.js
variants/Evolution/rules.html [new file with mode: 0644]
variants/Evolution/style.css [new file with mode: 0644]
variants/Extinction/class.js
variants/Extinction/rules.html [new file with mode: 0644]
variants/Extinction/style.css [new file with mode: 0644]

index 3a960aa..532932b 100644 (file)
@@ -158,8 +158,8 @@ export default class ChessRules {
 
   // 3a --> {x:3, y:10}
   static SquareToCoords(sq) {
-    return ArrayFun.toObject(["x", "y"],
-                             [0, 1].map(i => parseInt(sq[i], 36)));
+    const [x, y] = [0, 1].map(i => parseInt(sq[i], 36));
+    return { x, y };
   }
 
   // {x:11, y:12} --> bc
@@ -452,14 +452,16 @@ export default class ChessRules {
     const counts = reserveStr.split("").map(c => parseInt(c, 36));
     const L = V.ReserveArray.length;
     this.reserve = {
-      w: ArrayFun.toObject(V.ReserveArray, counts.slice(0, L)),
-      b: ArrayFun.toObject(V.ReserveArray, counts.slice(L, 2 * L))
+      w: Object.fromEntries(V.ReserveArray.map((k, i) => [k, counts[i]])),
+      b: Object.fromEntries(V.ReserveArray.map((k, i) => [k, counts[L + i]]))
     };
   }
 
   initIspawn(ispawnStr) {
-    if (ispawnStr != "-")
-      this.ispawn = ArrayFun.toObject(ispawnStr.split(","), true);
+    if (ispawnStr != "-") {
+      this.ispawn =
+        Object.fromEntries(ispawnStr.split(",").map(k => [k, true]));
+    }
     else
       this.ispawn = {};
   }
@@ -1568,7 +1570,9 @@ export default class ChessRules {
     const piece = this.getPieceType(x, y);
     let moves = this.getPotentialMovesOf(piece, [x, y]);
     if (this.hasEnpassant && !!this.epSquare_s) {
-      moves = [...moves, this.getEnpassantCaptures(piece, [x, y])];
+      Array.prototype.push.apply(moves,
+        this.getEnpassantCaptures(piece, [x, y]));
+    }
     if (this.isKing(0, 0, piece) && this.hasCastle)
       Array.prototype.push.apply(moves, this.getCastleMoves([x, y]));
     if (!noPP)
@@ -1581,24 +1585,19 @@ export default class ChessRules {
       return [];
     const color = this.getColor(moves[0].start.x, moves[0].start.y);
     const oppCols = this.getOppCols(color);
-
     if (this.options["capture"] && this.atLeastOneCapture(color))
       moves = this.capturePostProcess(moves, oppCols);
-
     if (this.options["atomic"])
       moves = this.atomicPostProcess(moves, color, oppCols);
-
     if (
       moves.length > 0 &&
       this.getPieceType(moves[0].start.x, moves[0].start.y) == "p"
     ) {
       moves = this.pawnPostProcess(moves, color, oppCols);
     }
-
     if (this.options["cannibal"] && this.options["rifle"])
       // In this case a rifle-capture from last rank may promote a pawn
       moves = this.riflePromotePostProcess(moves, color);
-
     return moves;
   }
 
@@ -2024,7 +2023,7 @@ export default class ChessRules {
 
   // Extract potential en-passant square from just played move
   setEpSquare_s(move) {
-    this.epSquare = undefined;
+    this.epSquare_s = undefined;
     const s = move.start,
           e = move.end;
     const gap = Math.abs(e.x - s.x);
@@ -2043,37 +2042,45 @@ export default class ChessRules {
       )
     ) {
       const step = (e.x - s.x) / gap;
-      this.epSquare = {
-        x: (s.x + e.x) / 2,
-        y: s.y
-      };
+      this.epSquare_s = [
+        {
+          x: (s.x + e.x) / 2,
+          y: s.y
+        },
+        e //add endpoint (to know where captured piece is)
+      ];
     }
   }
 
-  // Special case of en-passant captures: treated separately
+  // Special case of (pawn) en-passant captures: treated separately
   getEnpassantCaptures(piece, [x, y]) {
-    if (piece != 'p')
+    if (piece != 'p' || !this.epSquare_s)
       return [];
     const color = this.getColor(x, y);
-    const shiftX = (color == 'w' ? -1 : 1);
-    const oppCols = this.getOppCols(color);
-    if (
-      this.epSquare &&
-      this.epSquare.x == x + shiftX && //NOTE: epSquare.x not on edge
-      Math.abs(this.getY(this.epSquare.y - y)) == 1 &&
-      // Doublemove (and Progressive?) guards:
-      this.board[this.epSquare.x][this.epSquare.y] == "" &&
-      oppCols.includes(this.getColor(x, this.epSquare.y))
-    ) {
-      const [epx, epy] = [this.epSquare.x, this.epSquare.y];
-      this.board[epx][epy] = this.board[x][this.epSquare.y];
-      let enpassantMove = this.getBasicMove([x, y], [epx, epy]);
-      this.board[epx][epy] = "";
-      const lastIdx = enpassantMove.vanish.length - 1; //think Rifle
-      enpassantMove.vanish[lastIdx].x = x;
-      return [enpassantMove];
-    }
-    return [];
+    const shiftX = (color == 'w' ? -1 : 1),
+          oppCols = this.getOppCols(color);
+    const L = this.epSquare_s.length;
+    const captSq = this.epSquare_s[L-1];
+    let res = [];
+    for (let i = 0; i < L-1; i++) {
+      const sq = this.epSquare_s[i];
+      if (
+        sq.x == x + shiftX && //NOTE: epSquare.x not on edge
+        Math.abs(this.getY(sq.y - y)) == 1 &&
+        // Doublemove (and Progressive?) guards:
+        this.board[sq.x][sq.y] == "" &&
+        oppCols.includes(this.getColor(captSq.x, captSq.y))
+      ) {
+        this.board[sq.x][sq.y] = this.board[captSq.x][captSq.y];
+        let enpassantMove = this.getBasicMove([x, y], [sq.x, sq.y]);
+        this.board[sq.x][sq.y] = "";
+        const lastIdx = enpassantMove.vanish.length - 1; //think Rifle
+        enpassantMove.vanish[lastIdx].x = captSq.x;
+        enpassantMove.vanish[lastIdx].y = captSq.y; //in case of (Enpassant..)
+        res.push(enpassantMove);
+      }
+    }
+    return res;
   }
 
   getCastleMoves([x, y], finalSquares, castleWith, castleFlags) {
diff --git a/pieces/black_nightrider.svg b/pieces/black_nightrider.svg
new file mode 100644 (file)
index 0000000..b38d2de
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+       Vágtató (fekete)
+       Készült: a knight-b.svg felhasználásával
+       Felhasználási feltételek: Nevezd meg! - Így add tovább! (Creative Commons)
+       Uray M. János
+       2013-2018
+-->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128" height="128" viewBox="0 0 45 45">
+       <!-- alap -->
+       <g fill="#000" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
+               <!-- test -->
+               <path d="M 21.5,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"/>
+               <!-- fej -->
+               <path d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.5,9.5 13.5,8.0 11.5,5.5 C 13.0,4.5 16.5,10 16.5,10 L 18.5,10 C 19.0,8 19.5,6 19.0,4.5 C 20.5,4.0 21.0,7 21.5,10"/>
+       </g>
+       <!-- sebességvonalak -->
+       <defs>
+               <linearGradient id="speed-grad" x1="0%" y1="0%" x2="100%" y2="0%" spreadMethod="pad">
+                       <stop offset="30%" stop-color="#000" stop-opacity="0.6"/>
+                       <stop offset="100%" stop-color="#000" stop-opacity="0"/>
+               </linearGradient>
+       </defs>
+       <g>
+               <rect fill="url(#speed-grad)" x="30.0" y="12" width="7.5" height="2"/>
+               <rect fill="url(#speed-grad)" x="34.0" y="16" width="6.5" height="2"/>
+               <rect fill="url(#speed-grad)" x="36.0" y="20" width="6.0" height="2"/>
+               <rect fill="url(#speed-grad)" x="37.0" y="25" width="5.5" height="2"/>
+               <rect fill="url(#speed-grad)" x="38.0" y="30" width="5.0" height="2"/>
+               <rect fill="url(#speed-grad)" x="38.0" y="35" width="5.0" height="2"/>
+       </g>
+       <!-- vonalak -->
+       <g fill="#FFF" stroke="#FFF" stroke-width="1.5" stroke-linejoin="round">
+               <!-- szemkötő -->
+               <path stroke="none" d="M 12.8,13 C 11.8,14 10.0,17 9.1,19 L 33,16 L 27,11 Z"/>
+               <!-- hát -->
+               <path stroke="none" d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 Z"/>
+               <!-- orr -->
+               <path d="M 9.5 25.5 A 0.5,0.5,0 1,1 8.5,25.5 A 0.5,0.5,0 1,1 9.5,25.5 Z"/>
+       </g>
+       <!-- szem -->
+       <g fill="#000" stroke="#000" stroke-width="1.5" stroke-linejoin="round">
+               <path d="M 15.8,14.75 A 0.5,1.5,60 1,1 13.2,16.25 A 0.5,1.5,60 1,1 15.8,14.75 Z"/>
+       </g>
+</svg>
diff --git a/pieces/white_nightrider.svg b/pieces/white_nightrider.svg
new file mode 100644 (file)
index 0000000..aefaefe
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+       Vágtató (fehér)
+       Készült: a knight-w.svg felhasználásával
+       Felhasználási feltételek: Nevezd meg! - Így add tovább! (Creative Commons)
+       Uray M. János
+       2013-2018
+-->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128" height="128" viewBox="0 0 45 45">
+       <!-- alap -->
+       <g fill="#FFF" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
+               <!-- test -->
+               <path d="M 21.5,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"/>
+               <!-- fej -->
+               <path d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.5,9.5 13.5,8.0 11.5,5.5 C 13.0,4.5 16.5,10 16.5,10 L 18.5,10 C 19.0,8 19.5,6 19.0,4.5 C 20.5,4.0 21.0,7 21.5,10"/>
+       </g>
+       <path fill="#000" d="M 13,13 L 9,19 L 34,16 L 28,11 Z"/>
+       <!-- orr -->
+       <g fill="#000" stroke="#000" stroke-width="1.5" stroke-linejoin="round">
+               <path d="M 9.5 25.5 A 0.5,0.5,0 1,1 8.5,25.5 A 0.5,0.5,0 1,1 9.5,25.5 Z"/>
+       </g>
+       <!-- szem -->
+       <g fill="#FFF" stroke="#FFF" stroke-width="1.5" stroke-linejoin="round">
+               <path d="M 15.8,14.75 A 0.5,1.5,60 1,1 13.2,16.25 A 0.5,1.5,60 1,1 15.8,14.75 Z"/>
+       </g>
+       <!-- sebességvonalak -->
+       <defs>
+               <linearGradient id="speed-grad" x1="0%" y1="0%" x2="100%" y2="0%" spreadMethod="pad">
+                       <stop offset="30%" stop-color="#000" stop-opacity="0.6"/>
+                       <stop offset="100%" stop-color="#000" stop-opacity="0"/>
+               </linearGradient>
+       </defs>
+       <g>
+               <rect fill="url(#speed-grad)" x="30.0" y="12" width="7.5" height="2"/>
+               <rect fill="url(#speed-grad)" x="34.0" y="16" width="6.5" height="2"/>
+               <rect fill="url(#speed-grad)" x="36.0" y="20" width="6.0" height="2"/>
+               <rect fill="url(#speed-grad)" x="37.0" y="25" width="5.5" height="2"/>
+               <rect fill="url(#speed-grad)" x="38.0" y="30" width="5.0" height="2"/>
+               <rect fill="url(#speed-grad)" x="38.0" y="35" width="5.0" height="2"/>
+       </g>
+</svg>
index dd2dfd6..d60c028 100644 (file)
@@ -1,4 +1,5 @@
 import ChessRules from "/js/base_rules.js";
+import PiPo from "/utils/PiPo.js";
 
 export default class EnpassantRules extends ChessRules {
 
@@ -12,160 +13,77 @@ export default class EnpassantRules extends ChessRules {
 
   pieceDef(piece, color, x, y) {
     let res = super.pieceDef(piece, color, x, y);
-    if (piece == 'n')
+    if (piece == 'n') {
+      res["class"] = "nightrider";
       res.both[0].range = 8; //"infinite"
+    }
     return res;
   }
 
-  // TODO: comma separated (convention ?! yes..)
-  readEpSquare_s(squares) {
-      if (squares == "-")
-        return undefined;
-      // Expand init + dest squares into a full path:
-      const init = C.SquareToCoords(square.substr(0, 2));
-      let newPath = [init];
-      if (square.length == 2)
-        return newPath;
-      const dest = C.SquareToCoords(square.substr(2));
-      const delta = ['x', 'y'].map(i => Math.abs(dest[i] - init[i]));
-      // Check if it's a knight(rider) movement:
-      let step = [0, 0];
-      if (delta[0] > 0 && delta[1] > 0 && delta[0] != delta[1]) {
-        // Knightrider
-        const minShift = Math.min(delta[0], delta[1]);
-        step[0] = (dest.x - init.x) / minShift;
-        step[1] = (dest.y - init.y) / minShift;
-      }
-      else {
-        // "Sliders"
-        step = ['x', 'y'].map((i, idx) => {
-          return (dest[i] - init[i]) / delta[idx] || 0
-        });
-      }
-      let x = init.x + step[0],
-          y = init.y + step[1];
-      while (x != dest.x || y != dest.y) {
-        newPath.push({ x: x, y: y });
-        x += step[0];
-        y += step[1];
-      }
-      newPath.push(dest);
-      return newPath;
-  }
-
   setEpSquare_s(move) {
-    // Argument is a move: all intermediate squares are en-passant candidates,
+    // All intermediate squares are en-passant candidates,
     // except if the moving piece is a king.
-    const move = moveOrSquare;
     const piece = move.appear[0].p;
-    if (piece == V.KING ||
+    if (piece == 'k' ||
       (
-        Math.abs(move.end.x-move.start.x) <= 1 &&
-        Math.abs(move.end.y-move.start.y) <= 1
+        Math.abs(move.end.x - move.start.x) <= 1 &&
+        Math.abs(move.end.y - move.start.y) <= 1
       )
     ) {
-      return undefined;
+      this.epSquare_s = undefined;
     }
-    const delta = [move.end.x-move.start.x, move.end.y-move.start.y];
-    let step = undefined;
-    if (piece == V.KNIGHT) {
-      const divisor = Math.min(Math.abs(delta[0]), Math.abs(delta[1]));
-      step = [delta[0]/divisor || 0, delta[1]/divisor || 0];
-    } else {
-      step = [
-        delta[0]/Math.abs(delta[0]) || 0,
-        delta[1]/Math.abs(delta[1]) || 0
-      ];
-    }
-    let res = [];
-    for (
-      let [x,y] = [move.start.x+step[0],move.start.y+step[1]];
-      x != move.end.x || y != move.end.y;
-      x += step[0], y += step[1]
-    ) {
-      res.push({ x: x, y: y });
+    else {
+      const delta = [move.end.x-move.start.x, move.end.y-move.start.y];
+      const divisor = [Math.abs(delta[0]), Math.abs(delta[1])].sort();
+      const idx = (divisor[0] > 0 ? 0 : 1);
+      const step = [delta[0]/divisor[idx], delta[1]/divisor[idx]];
+      this.epSquare_s = [];
+      for (
+        let [x,y] = [move.start.x + step[0], move.start.y + step[1]];
+        x != move.end.x || y != move.end.y;
+        x += step[0], y += step[1]
+      ) {
+        this.epSquare_s.push({ x: x, y: y });
+      }
+      // Add final square to know which piece is taken en passant:
+      this.epSquare_s.push(move.end);
     }
-    // Add final square to know which piece is taken en passant:
-    res.push(move.end);
-    return res;
-  }
-
-  getEnpassantFen() {
-    const L = this.epSquares.length;
-    if (!this.epSquares[L - 1]) return "-"; //no en-passant
-    const epsq = this.epSquares[L - 1];
-    if (epsq.length <= 2) return epsq.map(V.CoordsToSquare).join("");
-    // Condensate path: just need initial and final squares:
-    return V.CoordsToSquare(epsq[0]) + V.CoordsToSquare(epsq[epsq.length - 1]);
   }
 
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x,y]);
-    // Add en-passant captures from this square:
-    const L = this.epSquares.length;
-    if (!this.epSquares[L - 1]) return moves;
-    const squares = this.epSquares[L - 1];
-    const S = squares.length;
-    // Object describing the removed opponent's piece:
-    const pipoV = new PiPo({
-      x: squares[S-1].x,
-      y: squares[S-1].y,
-      c: V.GetOppCol(this.turn),
-      p: this.getPiece(squares[S-1].x, squares[S-1].y)
-    });
-    // Check if existing non-capturing moves could also capture en passant
-    moves.forEach(m => {
-      if (
-        m.appear[0].p != V.PAWN && //special pawn case is handled elsewhere
-        m.vanish.length <= 1 &&
-        [...Array(S-1).keys()].some(i => {
-          return m.end.x == squares[i].x && m.end.y == squares[i].y;
-        })
-      ) {
-        m.vanish.push(pipoV);
-      }
-    });
     // Special case of the king knight's movement:
-    if (this.getPiece(x, y) == V.KING) {
-      V.steps[V.KNIGHT].forEach(step => {
-        const endX = x + step[0];
-        const endY = y + step[1];
-        if (
-          V.OnBoard(endX, endY) &&
-          [...Array(S-1).keys()].some(i => {
-            return endX == squares[i].x && endY == squares[i].y;
-          })
-        ) {
-          let enpassantMove = this.getBasicMove([x, y], [endX, endY]);
-          enpassantMove.vanish.push(pipoV);
-          moves.push(enpassantMove);
-        }
-      });
-    }
-    return moves;
-  }
-
-  getEnpassantCaptures([x, y], shiftX) {
-    const Lep = this.epSquares.length;
-    const squares = this.epSquares[Lep - 1];
-    let moves = [];
-    if (!!squares) {
-      const S = squares.length;
-      const taken = squares[S-1];
+    if (!!this.epSquare_s) {
+      const L = this.epSquare_s.length;
       const pipoV = new PiPo({
-        x: taken.x,
-        y: taken.y,
-        p: this.getPiece(taken.x, taken.y),
-        c: this.getColor(taken.x, taken.y)
+        x: this.epSquare_s[L-1].x,
+        y: this.epSquare_s[L-1].y,
+        c: C.GetOppTurn(this.turn),
+        p: this.getPiece(this.epSquare_s[L-1].x, this.epSquare_s[L-1].y)
       });
-      [...Array(S-1).keys()].forEach(i => {
-        const sq = squares[i];
-        if (sq.x == x + shiftX && Math.abs(sq.y - y) == 1) {
-          let enpassantMove = this.getBasicMove([x, y], [sq.x, sq.y]);
-          enpassantMove.vanish.push(pipoV);
-          moves.push(enpassantMove);
+      // Check if existing non-capturing moves could also capture en passant
+      moves.forEach(m => {
+        if (
+          m.appear[0].p != 'p' && //special pawn case is handled elsewhere
+          m.vanish.length <= 1 &&
+          this.epSquare_s.some(sq => m.end.x == sq.x && m.end.y == sq.y)
+        ) {
+          m.vanish.push(pipoV);
         }
       });
+      if (this.getPiece(x, y) == 'k') {
+        super.pieceDef('n').both[0].steps.forEach(step => {
+          const [endX, endY] = [x + step[0], y + step[1]];
+          if (
+            this.onBoard(endX, endY) &&
+            this.epSquare_s.some(sq => endX == sq.x && endY == sq.y)
+          ) {
+            let enpassantMove = this.getBasicMove([x, y], [endX, endY]);
+            enpassantMove.vanish.push(pipoV);
+            moves.push(enpassantMove);
+          }
+        });
+      }
     }
     return moves;
   }
index e3ca56e..5e2fdfc 100644 (file)
@@ -1,6 +1,6 @@
 <p>
   All pieces can be captured "en passant" when they make more than one step.
-  So, for more fun, knights can make multi-steps: they turn into knightriders.
+  So, for more fun, knights can make multi-steps: they turn into (k)nightriders.
 </p>
 
 <p>
@@ -8,6 +8,8 @@
   1.e4 d5 2.Qh5 Bxg4 would take the queen en passant.
 </p>
 
+<p>Note: the king has the extra ability to capture en-passant like a knight.</p>
+
 <p class="author">Andy Kurnia (1998).</p>
 
 <p>
index 95e35b2..e72f3eb 100644 (file)
@@ -1 +1,8 @@
 @import url("/css/base_pieces.css");
+
+piece.white.nightrider {
+  background-image: url('/pieces/white_nightrider.svg');
+}
+piece.black.nightrider {
+  background-image: url('/pieces/black_nightrider.svg');
+}
index 7b5abba..9371ff2 100644 (file)
@@ -1,27 +1,27 @@
-import { ChessRules } from "@/js/base_rules";
+import ChessRules from "/js/base_rules.js";
 
-export class EvolutionRules extends ChessRules {
+export default class EvolutionRules extends ChessRules {
+
+//  static get Options() {
+//    return C.Options;
+//  }
 
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
     const c = this.getColor(x, y);
     const piece = this.getPiece(x, y);
     if (
-      [V.BISHOP, V.ROOK, V.QUEEN].includes(piece) &&
+      ['b', 'r', 'q'].includes(piece) &&
       (c == 'w' && x == 7) || (c == 'b' && x == 0)
     ) {
       // Move from first rank
       const forward = (c == 'w' ? -1 : 1);
       for (let shift of [-2, 0, 2]) {
-        if (
-          (piece == V.ROOK && shift != 0) ||
-          (piece == V.BISHOP && shift == 0)
-        ) {
+        if ((piece == 'r' && shift != 0) || (piece == 'b' && shift == 0))
           continue;
-        }
         if (
-          V.OnBoard(x+2*forward, y+shift) &&
-          this.board[x+forward][y+shift/2] != V.EMPTY &&
+          this.onBoard(x+2*forward, y+shift) &&
+          this.board[x+forward][y+shift/2] != "" &&
           this.getColor(x+2*forward, y+shift) != c
         ) {
           moves.push(this.getBasicMove([x,y], [x+2*forward,y+shift]));
diff --git a/variants/Evolution/rules.html b/variants/Evolution/rules.html
new file mode 100644 (file)
index 0000000..205b059
--- /dev/null
@@ -0,0 +1,9 @@
+<p>Long-range pieces can jump over an obstacle when they are on the first rank.</p>
+
+<p>
+  From the first rank, rook, bishops and queen can play directly on the third
+  rank, even if an obstacle stands in-between. Development is thus accelerated.
+  This also help for defense.
+</p>
+
+<p class="author">Zied Haddad (2020).</p>
diff --git a/variants/Evolution/style.css b/variants/Evolution/style.css
new file mode 100644 (file)
index 0000000..95e35b2
--- /dev/null
@@ -0,0 +1 @@
+@import url("/css/base_pieces.css");
index c8e575d..75fff2a 100644 (file)
-import { ChessRules } from "@/js/base_rules";
+import ChessRules from "/js/base_rules.js";
 
-export class ExtinctionRules extends ChessRules {
+export default class ExtinctionRules extends ChessRules {
 
-  static get PawnSpecs() {
-    return Object.assign(
-      {},
-      ChessRules.PawnSpecs,
-      { promotions: ChessRules.PawnSpecs.promotions.concat([V.KING]) }
-    );
-  }
+//  static get Options() {
+//    return C.Options;
+//  }
 
-  static IsGoodPosition(position) {
-    if (!ChessRules.IsGoodPosition(position)) return false;
-    // Also check that each piece type is present
-    const rows = position.split("/");
-    let pieces = {};
-    for (let row of rows) {
-      for (let i = 0; i < row.length; i++) {
-        if (isNaN(parseInt(row[i], 10)) && !pieces[row[i]])
-          pieces[row[i]] = true;
-      }
-    }
-    if (Object.keys(pieces).length != 12) return false;
-    return true;
-  }
-
-  setOtherVariables(fen) {
-    super.setOtherVariables(fen);
-    const pos = V.ParseFen(fen).position;
-    // NOTE: no need for safety "|| []", because each piece type is present
-    // (otherwise game is already over!)
-    this.material = {
-      w: {
-        [V.KING]: pos.match(/K/g).length,
-        [V.QUEEN]: pos.match(/Q/g).length,
-        [V.ROOK]: pos.match(/R/g).length,
-        [V.KNIGHT]: pos.match(/N/g).length,
-        [V.BISHOP]: pos.match(/B/g).length,
-        [V.PAWN]: pos.match(/P/g).length
-      },
-      b: {
-        [V.KING]: pos.match(/k/g).length,
-        [V.QUEEN]: pos.match(/q/g).length,
-        [V.ROOK]: pos.match(/r/g).length,
-        [V.KNIGHT]: pos.match(/n/g).length,
-        [V.BISHOP]: pos.match(/b/g).length,
-        [V.PAWN]: pos.match(/p/g).length
-      }
-    };
-  }
-
-  // TODO: verify this assertion
-  atLeastOneMove() {
-    return true; //always at least one possible move
+  pawnPromotions() {
+    return super.pawnPromotions().concat('k');
   }
 
   filterValid(moves) {
     return moves; //there is no check
   }
 
-  getCheckSquares() {
-    return [];
-  }
-
-  postPlay(move) {
-    super.postPlay(move);
-    // Treat the promotion case: (not the capture part)
-    if (move.appear[0].p != move.vanish[0].p) {
-      this.material[move.appear[0].c][move.appear[0].p]++;
-      this.material[move.appear[0].c][V.PAWN]--;
-    }
-    if (move.vanish.length == 2 && move.appear.length == 1)
-      //capture
-      this.material[move.vanish[1].c][move.vanish[1].p]--;
-  }
-
-  postUndo(move) {
-    super.postUndo(move);
-    if (move.appear[0].p != move.vanish[0].p) {
-      this.material[move.appear[0].c][move.appear[0].p]--;
-      this.material[move.appear[0].c][V.PAWN]++;
-    }
-    if (move.vanish.length == 2 && move.appear.length == 1)
-      this.material[move.vanish[1].c][move.vanish[1].p]++;
-  }
-
   getCurrentScore() {
-    if (this.atLeastOneMove()) {
-      // Game not over?
-      const color = this.turn;
-      if (
-        Object.keys(this.material[color]).some(p => {
-          return this.material[color][p] == 0;
-        })
-      ) {
-        return this.turn == "w" ? "0-1" : "1-0";
-      }
-      return "*";
-    }
-    return this.turn == "w" ? "0-1" : "1-0"; //NOTE: currently unreachable...
-  }
-
-  evalPosition() {
     const color = this.turn;
-    if (
-      Object.keys(this.material[color]).some(p => {
-        return this.material[color][p] == 0;
-      })
-    ) {
-      // Very negative (resp. positive)
-      // if white (reps. black) pieces set is incomplete
-      return (color == "w" ? -1 : 1) * V.INFINITY;
+    let material = { 'w': {}, 'b': {} };
+    this.board.flat().forEach(cell => {
+      if (cell != "")
+        material[cell.charAt(0)][cell.charAt(1)] = true;
+    });
+    for (const c of ['w', 'b']) {
+      if (Object.keys(material[c]).length < 6)
+        return c == 'w' ? "0-1" : "1-0";
     }
-    return super.evalPosition();
+    return "*";
   }
 
 };
diff --git a/variants/Extinction/rules.html b/variants/Extinction/rules.html
new file mode 100644 (file)
index 0000000..0a06c37
--- /dev/null
@@ -0,0 +1,8 @@
+<p>Losing all pieces of some kind means losing the game.</p>
+
+<p>
+  There are no checks: kings can be captured too
+  (and pawns can promote into kings).
+</p>
+
+<p class="author">R. Wayne Schmittberger (1985).</p>
diff --git a/variants/Extinction/style.css b/variants/Extinction/style.css
new file mode 100644 (file)
index 0000000..95e35b2
--- /dev/null
@@ -0,0 +1 @@
+@import url("/css/base_pieces.css");