Add EnPassant variant draft
[vchess.git] / client / src / variants / Enpassant.js
CommitLineData
7d8bf63e
BA
1import { ChessRules, PiPo, Move } from "@/base_rules";
2
3export const VariantRules = class EnpassantRules extends ChessRules {
4
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;
11 }
12 }
13 return true;
14 }
15
16 getEpSquare(moveOrSquare) {
17 if (!moveOrSquare) return undefined;
18 if (typeof moveOrSquare === "string") {
19 const square = moveOrSquare;
20 if (square == "-") return undefined;
21 let res = [];
22 square.split(",").forEach(sq => {
23 res.push(V.SquareToCoords(sq));
24 });
25 return res;
26 }
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 ||
32 (
33 Math.abs(move.end.x-move.start.x) <= 1 &&
34 Math.abs(move.end.y-move.start.y) <= 1
35 )
36 ) {
37 return undefined;
38 }
39 const delta = [move.end.x-move.start.x, move.end.y-move.start.y];
40 let step = undefined;
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];
44 } else {
45 step = [delta[0]/Math.abs(delta[0]) || 0, delta[1]/Math.abs(delta[1]) || 0];
46 }
47 let res = [];
48 for (
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]
52 ) {
53 res.push({x:x, y:y});
54 }
55 // Add final square to know which piece is taken en passant:
56 res.push(move.end);
57 return res;
58 }
59
60 getEnpassantFen() {
61 const L = this.epSquares.length;
62 if (!this.epSquares[L - 1]) return "-"; //no en-passant
63 let res = "";
64 this.epSquares[L - 1].forEach(sq => {
65 res += V.CoordsToSquare(sq) + ",";
66 });
67 return res.slice(0, -1); //remove last comma
68 }
69
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;
74 let moves = [];
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
81
82 // NOTE: next condition is generally true (no pawn on last rank)
83 if (x + shiftX >= 0 && x + shiftX < sizeX) {
84 const finalPieces =
85 x + shiftX == lastRank
86 ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
87 : [V.PAWN];
88 // One square forward
89 if (this.board[x + shiftX][y] == V.EMPTY) {
90 for (let piece of finalPieces) {
91 moves.push(
92 this.getBasicMove([x, y], [x + shiftX, y], {
93 c: pawnColor,
94 p: piece
95 })
96 );
97 }
98 // Next condition because pawns on 1st rank can generally jump
99 if (
100 [startRank, firstRank].includes(x) &&
101 this.board[x + 2 * shiftX][y] == V.EMPTY
102 ) {
103 // Two squares jump
104 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
105 }
106 }
107 // Captures
108 for (let shiftY of [-1, 1]) {
109 if (
110 y + shiftY >= 0 &&
111 y + shiftY < sizeY &&
112 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
113 this.canTake([x, y], [x + shiftX, y + shiftY])
114 ) {
115 for (let piece of finalPieces) {
116 moves.push(
117 this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
118 c: pawnColor,
119 p: piece
120 })
121 );
122 }
123 }
124 }
125 }
126
127 // En passant
128 const Lep = this.epSquares.length;
129 const squares = this.epSquares[Lep - 1];
130 if (!!squares) {
131 const S = squares.length;
132 const taken = squares[S-1];
133 const pipoV = new PiPo({
134 x: taken.x,
135 y: taken.y,
136 p: this.getPiece(taken.x, taken.y),
137 c: this.getColor(taken.x, taken.y)
138 });
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);
145 }
146 });
147 }
148
149 return moves;
150 }
151
152 // Remove the "onestep" condition: knight promote to knightrider:
153
154 getPotentialKnightMoves(sq) {
155 return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT]);
156 }
157
158 isAttackedByKnight(sq, colors) {
159 return this.isAttackedBySlideNJump(
160 sq,
161 colors,
162 V.KNIGHT,
163 V.steps[V.KNIGHT]
164 );
165 }
166
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({
176 x: squares[S-1].x,
177 y: squares[S-1].y,
178 c: V.GetOppCol(this.turn),
179 p: this.getPiece(squares[S-1].x, squares[S-1].y)
180 });
181 // Check if existing non-capturing moves could also capture en passant
182 moves.forEach(m => {
183 if (
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;
188 })
189 ) {
190 m.vanish.push(pipoV);
191 }
192 });
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];
198 if (
199 V.OnBoard(endX, endY) &&
200 [...Array(S-1).keys()].some(i => {
201 return endX == squares[i].x && endY == squares[i].y;
202 })
203 ) {
204 let enpassantMove = this.getBasicMove([x, y], [endX, endY]);
205 enpassantMove.vanish.push(pipoV);
206 moves.push(enpassantMove);
207 }
208 });
209 }
210 return moves;
211 }
212
213 static get VALUES() {
214 return {
215 p: 1,
216 r: 5,
217 n: 4,
218 b: 3,
219 q: 9,
220 k: 1000
221 };
222 }
223};