1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
3 export class EnpassantRules
extends ChessRules
{
5 static IsGoodEnpassant(enpassant
) {
6 if (enpassant
!= "-") {
7 const squares
= enpassant
.split(",");
8 if (squares
.length
> 2) return false;
9 for (let sq
of squares
) {
10 const ep
= V
.SquareToCoords(sq
);
11 if (isNaN(ep
.x
) || !V
.OnBoard(ep
)) return false;
18 return (b
[1] == V
.KNIGHT
? "Enpassant/" : "") + b
;
21 getEpSquare(moveOrSquare
) {
22 if (!moveOrSquare
) return undefined;
23 if (typeof moveOrSquare
=== "string") {
24 const square
= moveOrSquare
;
25 if (square
== "-") return undefined;
26 // Expand init + dest squares into a full path:
27 const init
= V
.SquareToCoords(square
.substr(0, 2));
29 if (square
.length
== 2) return newPath
;
30 const dest
= V
.SquareToCoords(square
.substr(2));
31 const delta
= ['x', 'y'].map(i
=> Math
.abs(dest
[i
] - init
[i
]));
32 // Check if it's a knight(rider) movement:
34 if (delta
[0] > 0 && delta
[1] > 0 && delta
[0] != delta
[1]) {
36 const minShift
= Math
.min(delta
[0], delta
[1]);
37 step
[0] = (dest
.x
- init
.x
) / minShift
;
38 step
[1] = (dest
.y
- init
.y
) / minShift
;
41 step
= ['x', 'y'].map((i
, idx
) => {
42 return (dest
[i
] - init
[i
]) / delta
[idx
] || 0
45 let x
= init
.x
+ step
[0],
47 while (x
!= dest
.x
|| y
!= dest
.y
) {
48 newPath
.push({ x: x
, y: y
});
55 // Argument is a move: all intermediate squares are en-passant candidates,
56 // except if the moving piece is a king.
57 const move = moveOrSquare
;
58 const piece
= move.appear
[0].p
;
59 if (piece
== V
.KING
||
61 Math
.abs(move.end
.x
-move.start
.x
) <= 1 &&
62 Math
.abs(move.end
.y
-move.start
.y
) <= 1
67 const delta
= [move.end
.x
-move.start
.x
, move.end
.y
-move.start
.y
];
69 if (piece
== V
.KNIGHT
) {
70 const divisor
= Math
.min(Math
.abs(delta
[0]), Math
.abs(delta
[1]));
71 step
= [delta
[0]/divisor
|| 0, delta
[1]/divisor
|| 0];
74 delta
[0]/Math
.abs(delta
[0]) || 0,
75 delta
[1]/Math
.abs(delta
[1]) || 0
80 let [x
,y
] = [move.start
.x
+step
[0],move.start
.y
+step
[1]];
81 x
!= move.end
.x
|| y
!= move.end
.y
;
82 x
+= step
[0], y
+= step
[1]
84 res
.push({ x: x
, y: y
});
86 // Add final square to know which piece is taken en passant:
92 const L
= this.epSquares
.length
;
93 if (!this.epSquares
[L
- 1]) return "-"; //no en-passant
94 const epsq
= this.epSquares
[L
- 1];
95 if (epsq
.length
<= 2) return epsq
.map(V
.CoordsToSquare
).join("");
96 // Condensate path: just need initial and final squares:
97 return V
.CoordsToSquare(epsq
[0]) + V
.CoordsToSquare(epsq
[epsq
.length
- 1]);
100 getPotentialMovesFrom([x
, y
]) {
101 let moves
= super.getPotentialMovesFrom([x
,y
]);
102 // Add en-passant captures from this square:
103 const L
= this.epSquares
.length
;
104 if (!this.epSquares
[L
- 1]) return moves
;
105 const squares
= this.epSquares
[L
- 1];
106 const S
= squares
.length
;
107 // Object describing the removed opponent's piece:
108 const pipoV
= new PiPo({
111 c: V
.GetOppCol(this.turn
),
112 p: this.getPiece(squares
[S
-1].x
, squares
[S
-1].y
)
114 // Check if existing non-capturing moves could also capture en passant
117 m
.appear
[0].p
!= V
.PAWN
&& //special pawn case is handled elsewhere
118 m
.vanish
.length
<= 1 &&
119 [...Array(S
-1).keys()].some(i
=> {
120 return m
.end
.x
== squares
[i
].x
&& m
.end
.y
== squares
[i
].y
;
123 m
.vanish
.push(pipoV
);
126 // Special case of the king knight's movement:
127 if (this.getPiece(x
, y
) == V
.KING
) {
128 V
.steps
[V
.KNIGHT
].forEach(step
=> {
129 const endX
= x
+ step
[0];
130 const endY
= y
+ step
[1];
132 V
.OnBoard(endX
, endY
) &&
133 [...Array(S
-1).keys()].some(i
=> {
134 return endX
== squares
[i
].x
&& endY
== squares
[i
].y
;
137 let enpassantMove
= this.getBasicMove([x
, y
], [endX
, endY
]);
138 enpassantMove
.vanish
.push(pipoV
);
139 moves
.push(enpassantMove
);
146 getEnpassantCaptures([x
, y
], shiftX
) {
147 const Lep
= this.epSquares
.length
;
148 const squares
= this.epSquares
[Lep
- 1];
151 const S
= squares
.length
;
152 const taken
= squares
[S
-1];
153 const pipoV
= new PiPo({
156 p: this.getPiece(taken
.x
, taken
.y
),
157 c: this.getColor(taken
.x
, taken
.y
)
159 [...Array(S
-1).keys()].forEach(i
=> {
160 const sq
= squares
[i
];
161 if (sq
.x
== x
+ shiftX
&& Math
.abs(sq
.y
- y
) == 1) {
162 let enpassantMove
= this.getBasicMove([x
, y
], [sq
.x
, sq
.y
]);
163 enpassantMove
.vanish
.push(pipoV
);
164 moves
.push(enpassantMove
);
171 // Remove the "onestep" condition: knight promote to knightrider:
172 getPotentialKnightMoves(sq
) {
173 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
]);
177 const filteredMoves
= super.filterValid(moves
);
178 // If at least one full move made, everything is allowed:
179 if (this.movesCount
>= 2)
180 return filteredMoves
;
181 // Else, forbid captures:
182 return filteredMoves
.filter(m
=> m
.vanish
.length
== 1);
185 isAttackedByKnight(sq
, color
) {
186 return this.isAttackedBySlideNJump(
194 static get SEARCH_DEPTH() {
198 static get VALUES() {