From: Benjamin Auder Date: Thu, 18 Jun 2026 08:49:23 +0000 (+0200) Subject: Start Enpassant variant X-Git-Url: https://git.auder.net/doc/html/css/app_dev.php/figure/rpsls.js?a=commitdiff_plain;ds=sidebyside;p=xogo.git Start Enpassant variant --- diff --git a/js/base_rules.js b/js/base_rules.js index 749b65c..3a960aa 100644 --- a/js/base_rules.js +++ b/js/base_rules.js @@ -329,9 +329,9 @@ export default class ChessRules { // Enpassant part of the FEN string getEnpassantFen() { - if (!this.epSquare) + if (!this.epSquare_s) return "-"; - return C.CoordsToSquare(this.epSquare); + return this.epSquare_s.map(C.CoordsToSquare).join(','); } static get NewGameReserves() { @@ -424,7 +424,7 @@ export default class ChessRules { if (this.hasFlags) this.setFlags(fenParsed.flags); if (this.hasEnpassant) - this.epSquare = this.getEpSquare(fenParsed.enpassant); + this.epSquare_s = this.readEpSquare_s(fenParsed.enpassant); if (this.hasReserve && !this.isDiagram) this.initReserves(fenParsed.reserve); if (this.options["crazyhouse"]) @@ -1103,27 +1103,34 @@ export default class ChessRules { } } } - if (this.epSquare) + if (this.epSquare_s) this.enlightEnpassant(); } // Include square of the en-passant capturing square: enlightEnpassant() { + this.epSquare_s.forEach(sq => { + if (this.findEpAttack(sq, this.playerColor)) + this.enlightened[sq.x][sq.y] = true; + }) + } + + findEpAttack(sq, color) { // NOTE: shortcut, pawn has only one attack type, doesn't depend on square // TODO: (0, 0) is wrong, would need to place an attacker here... const steps = this.pieceDef('p', this.playerColor, 0, 0).attack[0].steps; for (let step of steps) { - const x = this.epSquare.x - step[0], //NOTE: epSquare.x not on edge - y = this.getY(this.epSquare.y - step[1]); + const x = sq.x - step[0], //NOTE: (ep) sq.x not on edge + y = this.getY(sq.y - step[1]); if ( this.onBoard(x, y) && - this.getColor(x, y) == this.playerColor && + this.getColor(x, y) == color && this.getPieceType(x, y) == "p" ) { - this.enlightened[x][this.epSquare.y] = true; - break; + return true; } } + return false; } // Apply diff this.enlightened --> oldEnlightened on board @@ -1560,8 +1567,8 @@ export default class ChessRules { return []; const piece = this.getPieceType(x, y); let moves = this.getPotentialMovesOf(piece, [x, y]); - if (piece == "p" && this.hasEnpassant && this.epSquare) - Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y])); + if (this.hasEnpassant && !!this.epSquare_s) { + moves = [...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) @@ -2005,21 +2012,25 @@ export default class ChessRules { return mv; } - // En-passant square, if any - getEpSquare(moveOrSquare) { - if (typeof moveOrSquare === "string") { - const square = moveOrSquare; - if (square == "-") - return undefined; - return C.SquareToCoords(square); - } - // Argument is a move: - const move = moveOrSquare; + // En-passant square from FEN string, if any + readEpSquare_s(fenSquare_s) { + if (fenSquare_s == "-") + return undefined; + let res = fenSquare_s.split(',').map(C.SquareToCoords); + if (res.length == 1) //most common case + return res[0]; + return res; + } + + // Extract potential en-passant square from just played move + setEpSquare_s(move) { + this.epSquare = undefined; const s = move.start, e = move.end; + const gap = Math.abs(e.x - s.x); if ( s.y == e.y && - Math.abs(s.x - e.x) == 2 && + gap >= 2 && // Next conditions for variants like Atomic or Rifle, Recycle... ( move.appear.length > 0 && @@ -2031,16 +2042,18 @@ export default class ChessRules { this.getPieceType(0, 0, move.vanish[0].p) == 'p' ) ) { - return { + const step = (e.x - s.x) / gap; + this.epSquare = { x: (s.x + e.x) / 2, y: s.y }; } - return undefined; //default } // Special case of en-passant captures: treated separately - getEnpassantCaptures([x, y]) { + getEnpassantCaptures(piece, [x, y]) { + if (piece != 'p') + return []; const color = this.getColor(x, y); const shiftX = (color == 'w' ? -1 : 1); const oppCols = this.getOppCols(color); @@ -2382,7 +2395,7 @@ export default class ChessRules { play(move) { this.prePlay(move); if (this.hasEnpassant) - this.epSquare = this.getEpSquare(move); + this.setEpSquare_s(move); this.playOnBoard(move); this.postPlay(move); } diff --git a/js/variants.js b/js/variants.js index f135626..7315dac 100644 --- a/js/variants.js +++ b/js/variants.js @@ -51,9 +51,9 @@ const variants = [ {name: 'Eightpieces', desc: 'Each piece is unique', disp: '8 Pieces'}, {name: 'Emergo', desc: 'Stacking Checkers variant'}, {name: 'Empire', desc: 'Empire versus Kingdom'}, -// {name: 'Enpassant', desc: 'Capture en passant', disp: 'En-passant'}, -// {name: 'Evolution', desc: 'Faster development'}, -// {name: 'Extinction', desc: 'Capture all of a kind'}, + {name: 'Enpassant', desc: 'Capture en passant', disp: 'En-passant'}, + {name: 'Evolution', desc: 'Faster development'}, + {name: 'Extinction', desc: 'Capture all of a kind'}, // {name: 'Fanorona', desc: 'Malagasy Draughts'}, // {name: 'Football', desc: 'Score a goal'}, // {name: 'Forward', desc: 'Moving forward'}, diff --git a/variants/Checkered/class.js b/variants/Checkered/class.js index 793ed7e..3e6237d 100644 --- a/variants/Checkered/class.js +++ b/variants/Checkered/class.js @@ -191,16 +191,15 @@ export default class CheckeredRules extends ChessRules { } } - getEpSquare(moveOrSquare) { - // At stage 2, all pawns can be captured en-passant + setEpSquare_s(move) { if ( + // At stage 2, all pawns can be captured en-passant this.stage == 2 || - typeof moveOrSquare !== "object" || - (moveOrSquare.appear.length > 0 && moveOrSquare.appear[0].c != 'c') - ) - return super.getEpSquare(moveOrSquare); - // Checkered or switch move: no en-passant - return undefined; + // Checkered or switch move ==> no en-passant: + (move.appear.length > 0 && move.appear[0].c != 'c') + ) { + super.setEpSquare_s(move); + } } getCmove(move) { diff --git a/variants/Empire/class.js b/variants/Empire/class.js index 64b2156..6dd3dc3 100644 --- a/variants/Empire/class.js +++ b/variants/Empire/class.js @@ -21,7 +21,8 @@ export default class EmpireRules extends ChessRules { 'K': 'K' }; - const bf = super.genRandInitBaseFen(); + let bf = super.genRandInitBaseFen(); + bf.o.flags = "88" + bf.o.flags.substr(2); return { fen: bf.fen.substr(0, 24) + "PPPSSPPP/8/" + bf.fen.substr(35, 8).split('').map(p => piecesMap[p]).join('') + diff --git a/variants/Enpassant/class.js b/variants/Enpassant/class.js index d6eaef5..dd2dfd6 100644 --- a/variants/Enpassant/class.js +++ b/variants/Enpassant/class.js @@ -1,33 +1,32 @@ -import { ChessRules, PiPo, Move } from "@/js/base_rules"; +import ChessRules from "/js/base_rules.js"; -export class EnpassantRules extends ChessRules { +export default class EnpassantRules extends ChessRules { - static IsGoodEnpassant(enpassant) { - if (enpassant != "-") { - const squares = enpassant.split(","); - if (squares.length > 2) return false; - for (let sq of squares) { - const ep = V.SquareToCoords(sq); - if (isNaN(ep.x) || !V.OnBoard(ep)) return false; - } - } - return true; + static get Options() { + return { + select: C.Options.select, + input: C.Options.input, + styles: C.Options.styles.filter(s => s != "zen") + }; } - getPpath(b) { - return (b[1] == V.KNIGHT ? "Enpassant/" : "") + b; + pieceDef(piece, color, x, y) { + let res = super.pieceDef(piece, color, x, y); + if (piece == 'n') + res.both[0].range = 8; //"infinite" + return res; } - getEpSquare(moveOrSquare) { - if (!moveOrSquare) return undefined; - if (typeof moveOrSquare === "string") { - const square = moveOrSquare; - if (square == "-") return undefined; + // TODO: comma separated (convention ?! yes..) + readEpSquare_s(squares) { + if (squares == "-") + return undefined; // Expand init + dest squares into a full path: - const init = V.SquareToCoords(square.substr(0, 2)); + const init = C.SquareToCoords(square.substr(0, 2)); let newPath = [init]; - if (square.length == 2) return newPath; - const dest = V.SquareToCoords(square.substr(2)); + 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]; @@ -36,7 +35,8 @@ export class EnpassantRules extends ChessRules { const minShift = Math.min(delta[0], delta[1]); step[0] = (dest.x - init.x) / minShift; step[1] = (dest.y - init.y) / minShift; - } else { + } + else { // "Sliders" step = ['x', 'y'].map((i, idx) => { return (dest[i] - init[i]) / delta[idx] || 0 @@ -51,7 +51,9 @@ export class EnpassantRules extends ChessRules { } newPath.push(dest); return newPath; - } + } + + setEpSquare_s(move) { // Argument is a move: all intermediate squares are en-passant candidates, // except if the moving piece is a king. const move = moveOrSquare; @@ -168,11 +170,6 @@ export class EnpassantRules extends ChessRules { return moves; } - // Remove the "onestep" condition: knight promote to knightrider: - getPotentialKnightMoves(sq) { - return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT]); - } - filterValid(moves) { const filteredMoves = super.filterValid(moves); // If at least one full move made, everything is allowed: @@ -182,28 +179,4 @@ export class EnpassantRules extends ChessRules { return filteredMoves.filter(m => m.vanish.length == 1); } - isAttackedByKnight(sq, color) { - return this.isAttackedBySlideNJump( - sq, - color, - V.KNIGHT, - V.steps[V.KNIGHT] - ); - } - - static get SEARCH_DEPTH() { - return 2; - } - - static get VALUES() { - return { - p: 1, - r: 5, - n: 4, - b: 3, - q: 9, - k: 1000 - }; - } - }; diff --git a/variants/Enpassant/rules.html b/variants/Enpassant/rules.html new file mode 100644 index 0000000..e3ca56e --- /dev/null +++ b/variants/Enpassant/rules.html @@ -0,0 +1,17 @@ +

+ 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. +

+ +

+ For example from initial position, + 1.e4 d5 2.Qh5 Bxg4 would take the queen en passant. +

+ +

Andy Kurnia (1998).

+ +

+ See also + En Passant chess + on chessvariants.com. +

diff --git a/variants/Enpassant/style.css b/variants/Enpassant/style.css new file mode 100644 index 0000000..95e35b2 --- /dev/null +++ b/variants/Enpassant/style.css @@ -0,0 +1 @@ +@import url("/css/base_pieces.css"); diff --git a/variants/Refusal/class.js b/variants/Refusal/class.js index e57a360..0edf0da 100644 --- a/variants/Refusal/class.js +++ b/variants/Refusal/class.js @@ -83,10 +83,9 @@ export default class RefusalRules extends ChessRules { return super.getPotentialMovesFrom([x, y]); } - getEpSquare(move) { + setEpSquare_s(move) { if (!move.refusal) - return super.getEpSquare(move); - return null; + super.setEpSquare_s(move); } filterValid(moves) {