1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
3 export class EnpassantRules
extends ChessRules
{
4 static IsGoodEnpassant(enpassant
) {
5 if (enpassant
!= "-") {
6 const squares
= enpassant
.split(",");
7 for (let sq
of squares
) {
8 const ep
= V
.SquareToCoords(sq
);
9 if (isNaN(ep
.x
) || !V
.OnBoard(ep
)) return false;
15 getEpSquare(moveOrSquare
) {
16 if (!moveOrSquare
) return undefined;
17 if (typeof moveOrSquare
=== "string") {
18 const square
= moveOrSquare
;
19 if (square
== "-") return undefined;
21 square
.split(",").forEach(sq
=> {
22 res
.push(V
.SquareToCoords(sq
));
26 // Argument is a move: all intermediate squares are en-passant candidates,
27 // except if the moving piece is a king.
28 const move = moveOrSquare
;
29 const piece
= move.appear
[0].p
;
30 if (piece
== V
.KING
||
32 Math
.abs(move.end
.x
-move.start
.x
) <= 1 &&
33 Math
.abs(move.end
.y
-move.start
.y
) <= 1
38 const delta
= [move.end
.x
-move.start
.x
, move.end
.y
-move.start
.y
];
40 if (piece
== V
.KNIGHT
) {
41 const divisor
= Math
.min(Math
.abs(delta
[0]), Math
.abs(delta
[1]));
42 step
= [delta
[0]/divisor
|| 0, delta
[1]/divisor
|| 0];
44 step
= [delta
[0]/Math
.abs(delta
[0]) || 0, delta
[1]/Math
.abs(delta
[1]) || 0];
48 let [x
,y
] = [move.start
.x
+step
[0],move.start
.y
+step
[1]];
49 x
!= move.end
.x
|| y
!= move.end
.y
;
50 x
+= step
[0], y
+= step
[1]
54 // Add final square to know which piece is taken en passant:
60 const L
= this.epSquares
.length
;
61 if (!this.epSquares
[L
- 1]) return "-"; //no en-passant
63 this.epSquares
[L
- 1].forEach(sq
=> {
64 res
+= V
.CoordsToSquare(sq
) + ",";
66 return res
.slice(0, -1); //remove last comma
69 getPotentialMovesFrom([x
, y
]) {
70 let moves
= super.getPotentialMovesFrom([x
,y
]);
71 // Add en-passant captures from this square:
72 const L
= this.epSquares
.length
;
73 if (!this.epSquares
[L
- 1]) return moves
;
74 const squares
= this.epSquares
[L
- 1];
75 const S
= squares
.length
;
76 // Object describing the removed opponent's piece:
77 const pipoV
= new PiPo({
80 c: V
.GetOppCol(this.turn
),
81 p: this.getPiece(squares
[S
-1].x
, squares
[S
-1].y
)
83 // Check if existing non-capturing moves could also capture en passant
86 m
.appear
[0].p
!= V
.PAWN
&& //special pawn case is handled elsewhere
87 m
.vanish
.length
<= 1 &&
88 [...Array(S
-1).keys()].some(i
=> {
89 return m
.end
.x
== squares
[i
].x
&& m
.end
.y
== squares
[i
].y
;
95 // Special case of the king knight's movement:
96 if (this.getPiece(x
, y
) == V
.KING
) {
97 V
.steps
[V
.KNIGHT
].forEach(step
=> {
98 const endX
= x
+ step
[0];
99 const endY
= y
+ step
[1];
101 V
.OnBoard(endX
, endY
) &&
102 [...Array(S
-1).keys()].some(i
=> {
103 return endX
== squares
[i
].x
&& endY
== squares
[i
].y
;
106 let enpassantMove
= this.getBasicMove([x
, y
], [endX
, endY
]);
107 enpassantMove
.vanish
.push(pipoV
);
108 moves
.push(enpassantMove
);
115 getEnpassantCaptures([x
, y
], shiftX
) {
116 const Lep
= this.epSquares
.length
;
117 const squares
= this.epSquares
[Lep
- 1];
120 const S
= squares
.length
;
121 const taken
= squares
[S
-1];
122 const pipoV
= new PiPo({
125 p: this.getPiece(taken
.x
, taken
.y
),
126 c: this.getColor(taken
.x
, taken
.y
)
128 [...Array(S
-1).keys()].forEach(i
=> {
129 const sq
= squares
[i
];
130 if (sq
.x
== x
+ shiftX
&& Math
.abs(sq
.y
- y
) == 1) {
131 let enpassantMove
= this.getBasicMove([x
, y
], [sq
.x
, sq
.y
]);
132 enpassantMove
.vanish
.push(pipoV
);
133 moves
.push(enpassantMove
);
140 // Remove the "onestep" condition: knight promote to knightrider:
141 getPotentialKnightMoves(sq
) {
142 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
]);
146 const filteredMoves
= super.filterValid(moves
);
147 // If at least one full move made, everything is allowed:
148 if (this.movesCount
>= 2)
149 return filteredMoves
;
150 // Else, forbid captures:
151 return filteredMoves
.filter(m
=> m
.vanish
.length
== 1);
154 isAttackedByKnight(sq
, color
) {
155 return this.isAttackedBySlideNJump(
163 static get SEARCH_DEPTH() {
167 static get VALUES() {