// 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() {
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"])
}
}
}
- 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
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)
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 &&
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);
play(move) {
this.prePlay(move);
if (this.hasEnpassant)
- this.epSquare = this.getEpSquare(move);
+ this.setEpSquare_s(move);
this.playOnBoard(move);
this.postPlay(move);
}
{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'},
}
}
- 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) {
'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('') +
-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];
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
}
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;
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:
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
- };
- }
-
};
--- /dev/null
+<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.
+</p>
+
+<p>
+ For example from initial position,
+ 1.e4 d5 2.Qh5 Bxg4 would take the queen en passant.
+</p>
+
+<p class="author">Andy Kurnia (1998).</p>
+
+<p>
+ See also
+ <a href="https://www.chessvariants.com/difftaking.dir/enpassant.html">En Passant chess</a>
+ on chessvariants.com.
+</p>
--- /dev/null
+@import url("/css/base_pieces.css");
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) {