Add unambiguous section in the PGN + some fixes + code formatting and fix typos
[vchess.git] / client / src / variants / Enpassant.js
1 import { ChessRules, PiPo, Move } from "@/base_rules";
2
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;
10 }
11 }
12 return true;
13 }
14
15 getEpSquare(moveOrSquare) {
16 if (!moveOrSquare) return undefined;
17 if (typeof moveOrSquare === "string") {
18 const square = moveOrSquare;
19 if (square == "-") return undefined;
20 let res = [];
21 square.split(",").forEach(sq => {
22 res.push(V.SquareToCoords(sq));
23 });
24 return res;
25 }
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 ||
31 (
32 Math.abs(move.end.x-move.start.x) <= 1 &&
33 Math.abs(move.end.y-move.start.y) <= 1
34 )
35 ) {
36 return undefined;
37 }
38 const delta = [move.end.x-move.start.x, move.end.y-move.start.y];
39 let step = undefined;
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];
43 } else {
44 step = [
45 delta[0]/Math.abs(delta[0]) || 0,
46 delta[1]/Math.abs(delta[1]) || 0
47 ];
48 }
49 let res = [];
50 for (
51 let [x,y] = [move.start.x+step[0],move.start.y+step[1]];
52 x != move.end.x || y != move.end.y;
53 x += step[0], y += step[1]
54 ) {
55 res.push({x:x, y:y});
56 }
57 // Add final square to know which piece is taken en passant:
58 res.push(move.end);
59 return res;
60 }
61
62 getEnpassantFen() {
63 const L = this.epSquares.length;
64 if (!this.epSquares[L - 1]) return "-"; //no en-passant
65 let res = "";
66 this.epSquares[L - 1].forEach(sq => {
67 res += V.CoordsToSquare(sq) + ",";
68 });
69 return res.slice(0, -1); //remove last comma
70 }
71
72 getPotentialMovesFrom([x, y]) {
73 let moves = super.getPotentialMovesFrom([x,y]);
74 // Add en-passant captures from this square:
75 const L = this.epSquares.length;
76 if (!this.epSquares[L - 1]) return moves;
77 const squares = this.epSquares[L - 1];
78 const S = squares.length;
79 // Object describing the removed opponent's piece:
80 const pipoV = new PiPo({
81 x: squares[S-1].x,
82 y: squares[S-1].y,
83 c: V.GetOppCol(this.turn),
84 p: this.getPiece(squares[S-1].x, squares[S-1].y)
85 });
86 // Check if existing non-capturing moves could also capture en passant
87 moves.forEach(m => {
88 if (
89 m.appear[0].p != V.PAWN && //special pawn case is handled elsewhere
90 m.vanish.length <= 1 &&
91 [...Array(S-1).keys()].some(i => {
92 return m.end.x == squares[i].x && m.end.y == squares[i].y;
93 })
94 ) {
95 m.vanish.push(pipoV);
96 }
97 });
98 // Special case of the king knight's movement:
99 if (this.getPiece(x, y) == V.KING) {
100 V.steps[V.KNIGHT].forEach(step => {
101 const endX = x + step[0];
102 const endY = y + step[1];
103 if (
104 V.OnBoard(endX, endY) &&
105 [...Array(S-1).keys()].some(i => {
106 return endX == squares[i].x && endY == squares[i].y;
107 })
108 ) {
109 let enpassantMove = this.getBasicMove([x, y], [endX, endY]);
110 enpassantMove.vanish.push(pipoV);
111 moves.push(enpassantMove);
112 }
113 });
114 }
115 return moves;
116 }
117
118 getEnpassantCaptures([x, y], shiftX) {
119 const Lep = this.epSquares.length;
120 const squares = this.epSquares[Lep - 1];
121 let moves = [];
122 if (!!squares) {
123 const S = squares.length;
124 const taken = squares[S-1];
125 const pipoV = new PiPo({
126 x: taken.x,
127 y: taken.y,
128 p: this.getPiece(taken.x, taken.y),
129 c: this.getColor(taken.x, taken.y)
130 });
131 [...Array(S-1).keys()].forEach(i => {
132 const sq = squares[i];
133 if (sq.x == x + shiftX && Math.abs(sq.y - y) == 1) {
134 let enpassantMove = this.getBasicMove([x, y], [sq.x, sq.y]);
135 enpassantMove.vanish.push(pipoV);
136 moves.push(enpassantMove);
137 }
138 });
139 }
140 return moves;
141 }
142
143 // Remove the "onestep" condition: knight promote to knightrider:
144 getPotentialKnightMoves(sq) {
145 return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT]);
146 }
147
148 filterValid(moves) {
149 const filteredMoves = super.filterValid(moves);
150 // If at least one full move made, everything is allowed:
151 if (this.movesCount >= 2)
152 return filteredMoves;
153 // Else, forbid captures:
154 return filteredMoves.filter(m => m.vanish.length == 1);
155 }
156
157 isAttackedByKnight(sq, color) {
158 return this.isAttackedBySlideNJump(
159 sq,
160 color,
161 V.KNIGHT,
162 V.steps[V.KNIGHT]
163 );
164 }
165
166 static get SEARCH_DEPTH() {
167 return 2;
168 }
169
170 static get VALUES() {
171 return {
172 p: 1,
173 r: 5,
174 n: 4,
175 b: 3,
176 q: 9,
177 k: 1000
178 };
179 }
180 };