50f9738704f9e99bf07e30e2af44affab5fd0725
1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
3 export const VariantRules
= class EnpassantRules
extends ChessRules
{
5 static IsGoodEnpassant(enpassant
) {
6 if (enpassant
!= "-") {
7 const squares
= enpassant
.split(",");
8 for (let sq
of squares
) {
9 const ep
= V
.SquareToCoords(sq
);
10 if (isNaN(ep
.x
) || !V
.OnBoard(ep
)) return false;
16 getEpSquare(moveOrSquare
) {
17 if (!moveOrSquare
) return undefined;
18 if (typeof moveOrSquare
=== "string") {
19 const square
= moveOrSquare
;
20 if (square
== "-") return undefined;
22 square
.split(",").forEach(sq
=> {
23 res
.push(V
.SquareToCoords(sq
));
27 // Argument is a move: all intermediate squares are en-passant candidates,
28 // except if the moving piece is a king.
29 const move = moveOrSquare
;
30 const piece
= move.appear
[0].p
;
31 if (piece
== V
.KING
||
33 Math
.abs(move.end
.x
-move.start
.x
) <= 1 &&
34 Math
.abs(move.end
.y
-move.start
.y
) <= 1
39 const delta
= [move.end
.x
-move.start
.x
, move.end
.y
-move.start
.y
];
41 if (piece
== V
.KNIGHT
) {
42 const divisor
= Math
.min(Math
.abs(delta
[0]), Math
.abs(delta
[1]));
43 step
= [delta
[0]/divisor
|| 0, delta
[1]/divisor
|| 0];
45 step
= [delta
[0]/Math
.abs(delta
[0]) || 0, delta
[1]/Math
.abs(delta
[1]) || 0];
49 let [x
,y
] = [move.start
.x
+step
[0],move.start
.y
+step
[1]];
50 x
!= move.end
.x
|| y
!= move.end
.y
;
51 x
+= step
[0], y
+= step
[1]
55 // Add final square to know which piece is taken en passant:
61 const L
= this.epSquares
.length
;
62 if (!this.epSquares
[L
- 1]) return "-"; //no en-passant
64 this.epSquares
[L
- 1].forEach(sq
=> {
65 res
+= V
.CoordsToSquare(sq
) + ",";
67 return res
.slice(0, -1); //remove last comma
70 // TODO: this getPotentialPawnMovesFrom() is mostly duplicated:
71 // it could be split in "capture", "promotion", "enpassant"...
72 getPotentialPawnMoves([x
, y
]) {
73 const color
= this.turn
;
75 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
76 const shiftX
= color
== "w" ? -1 : 1;
77 const firstRank
= color
== "w" ? sizeX
- 1 : 0;
78 const startRank
= color
== "w" ? sizeX
- 2 : 1;
79 const lastRank
= color
== "w" ? 0 : sizeX
- 1;
80 const pawnColor
= this.getColor(x
, y
); //can be different for checkered
82 // NOTE: next condition is generally true (no pawn on last rank)
83 if (x
+ shiftX
>= 0 && x
+ shiftX
< sizeX
) {
85 x
+ shiftX
== lastRank
86 ? [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
]
89 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
) {
90 for (let piece
of finalPieces
) {
92 this.getBasicMove([x
, y
], [x
+ shiftX
, y
], {
98 // Next condition because pawns on 1st rank can generally jump
100 [startRank
, firstRank
].includes(x
) &&
101 this.board
[x
+ 2 * shiftX
][y
] == V
.EMPTY
104 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shiftX
, y
]));
108 for (let shiftY
of [-1, 1]) {
111 y
+ shiftY
< sizeY
&&
112 this.board
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
113 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
115 for (let piece
of finalPieces
) {
117 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
128 const Lep
= this.epSquares
.length
;
129 const squares
= this.epSquares
[Lep
- 1];
131 const S
= squares
.length
;
132 const taken
= squares
[S
-1];
133 const pipoV
= new PiPo({
136 p: this.getPiece(taken
.x
, taken
.y
),
137 c: this.getColor(taken
.x
, taken
.y
)
139 [...Array(S
-1).keys()].forEach(i
=> {
140 const sq
= squares
[i
];
141 if (sq
.x
== x
+ shiftX
&& Math
.abs(sq
.y
- y
) == 1) {
142 let enpassantMove
= this.getBasicMove([x
, y
], [sq
.x
, sq
.y
]);
143 enpassantMove
.vanish
.push(pipoV
);
144 moves
.push(enpassantMove
);
152 // Remove the "onestep" condition: knight promote to knightrider:
154 getPotentialKnightMoves(sq
) {
155 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
]);
158 isAttackedByKnight(sq
, colors
) {
159 return this.isAttackedBySlideNJump(
167 getPotentialMovesFrom([x
, y
]) {
168 let moves
= super.getPotentialMovesFrom([x
,y
]);
169 // Add en-passant captures from this square:
170 const L
= this.epSquares
.length
;
171 if (!this.epSquares
[L
- 1]) return moves
;
172 const squares
= this.epSquares
[L
- 1];
173 const S
= squares
.length
;
174 // Object describing the removed opponent's piece:
175 const pipoV
= new PiPo({
178 c: V
.GetOppCol(this.turn
),
179 p: this.getPiece(squares
[S
-1].x
, squares
[S
-1].y
)
181 // Check if existing non-capturing moves could also capture en passant
184 m
.appear
[0].p
!= V
.PAWN
&& //special pawn case is handled elsewhere
185 m
.vanish
.length
<= 1 &&
186 [...Array(S
-1).keys()].some(i
=> {
187 return m
.end
.x
== squares
[i
].x
&& m
.end
.y
== squares
[i
].y
;
190 m
.vanish
.push(pipoV
);
193 // Special case of the king knight's movement:
194 if (this.getPiece(x
, y
) == V
.KING
) {
195 V
.steps
[V
.KNIGHT
].forEach(step
=> {
196 const endX
= x
+ step
[0];
197 const endY
= y
+ step
[1];
199 V
.OnBoard(endX
, endY
) &&
200 [...Array(S
-1).keys()].some(i
=> {
201 return endX
== squares
[i
].x
&& endY
== squares
[i
].y
;
204 let enpassantMove
= this.getBasicMove([x
, y
], [endX
, endY
]);
205 enpassantMove
.vanish
.push(pipoV
);
206 moves
.push(enpassantMove
);
213 static get VALUES() {