Fix Antiking 1 notation
[vchess.git] / client / src / variants / Recycle.js
CommitLineData
78d64531
BA
1import { ChessRules, PiPo, Move } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
3
4export const VariantRules = class RecycleRules extends ChessRules {
5 static IsGoodFen(fen) {
6 if (!ChessRules.IsGoodFen(fen)) return false;
7 const fenParsed = V.ParseFen(fen);
8 // 5) Check reserves
9 if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{10,10}$/))
10 return false;
11 return true;
12 }
13
14 static ParseFen(fen) {
15 const fenParts = fen.split(" ");
16 return Object.assign(ChessRules.ParseFen(fen), {
90e814b6 17 reserve: fenParts[5]
78d64531
BA
18 });
19 }
20
e727fe31 21 getEpSquare(moveOrSquare) {
bbf66837 22 if (typeof moveOrSquare !== "object" || moveOrSquare.vanish.length > 0)
e727fe31
BA
23 return super.getEpSquare(moveOrSquare);
24 // Landing move: no en-passant
25 return undefined;
26 }
27
7ba4a5bc
BA
28 static GenRandInitFen(randomness) {
29 return ChessRules.GenRandInitFen(randomness) + " 0000000000";
78d64531
BA
30 }
31
32 getFen() {
90e814b6 33 return super.getFen() + " " + this.getReserveFen();
78d64531
BA
34 }
35
f9c36b2d 36 getFenForRepeat() {
90e814b6 37 return super.getFenForRepeat() + "_" + this.getReserveFen();
f9c36b2d
BA
38 }
39
78d64531
BA
40 getReserveFen() {
41 let counts = new Array(10);
42 for (
43 let i = 0;
44 i < V.PIECES.length - 1;
45 i++ //-1: no king reserve
46 ) {
47 counts[i] = this.reserve["w"][V.PIECES[i]];
48 counts[5 + i] = this.reserve["b"][V.PIECES[i]];
49 }
50 return counts.join("");
51 }
52
53 setOtherVariables(fen) {
54 super.setOtherVariables(fen);
55 const fenParsed = V.ParseFen(fen);
56 // Also init reserves (used by the interface to show landable pieces)
57 this.reserve = {
58 w: {
59 [V.PAWN]: parseInt(fenParsed.reserve[0]),
60 [V.ROOK]: parseInt(fenParsed.reserve[1]),
61 [V.KNIGHT]: parseInt(fenParsed.reserve[2]),
62 [V.BISHOP]: parseInt(fenParsed.reserve[3]),
63 [V.QUEEN]: parseInt(fenParsed.reserve[4])
64 },
65 b: {
66 [V.PAWN]: parseInt(fenParsed.reserve[5]),
67 [V.ROOK]: parseInt(fenParsed.reserve[6]),
68 [V.KNIGHT]: parseInt(fenParsed.reserve[7]),
69 [V.BISHOP]: parseInt(fenParsed.reserve[8]),
70 [V.QUEEN]: parseInt(fenParsed.reserve[9])
71 }
72 };
73 }
74
75 getColor(i, j) {
76 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
77 return this.board[i][j].charAt(0);
78 }
79
80 getPiece(i, j) {
81 if (i >= V.size.x) return V.RESERVE_PIECES[j];
82 return this.board[i][j].charAt(1);
83 }
84
85 // Used by the interface:
241bf8f2 86 getReservePpath(index, color) {
78d64531
BA
87 return color + V.RESERVE_PIECES[index];
88 }
89
90 // Ordering on reserve pieces
91 static get RESERVE_PIECES() {
92 return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
93 }
94
95 getReserveMoves([x, y]) {
96 const color = this.turn;
97 const p = V.RESERVE_PIECES[y];
98 if (this.reserve[color][p] == 0) return [];
99 let moves = [];
100 const pawnShift = p == V.PAWN ? 1 : 0;
101 for (let i = pawnShift; i < V.size.x - pawnShift; i++) {
102 for (let j = 0; j < V.size.y; j++) {
103 if (this.board[i][j] == V.EMPTY) {
104 let mv = new Move({
105 appear: [
106 new PiPo({
107 x: i,
108 y: j,
109 c: color,
110 p: p
111 })
112 ],
113 vanish: [],
114 start: { x: x, y: y }, //a bit artificial...
115 end: { x: i, y: j }
116 });
117 moves.push(mv);
118 }
119 }
120 }
121 return moves;
122 }
123
124 getPotentialMovesFrom([x, y]) {
125 if (x >= V.size.x) {
126 // Reserves, outside of board: x == sizeX(+1)
127 return this.getReserveMoves([x, y]);
128 }
129 // Standard moves
130 return super.getPotentialMovesFrom([x, y]);
131 }
132
133 getPotentialPawnMoves([x, y]) {
134 const color = this.turn;
135 let moves = [];
136 const [sizeX, sizeY] = [V.size.x, V.size.y];
137 const shiftX = color == "w" ? -1 : 1;
138 const startRank = color == "w" ? sizeX - 2 : 1;
139 const lastRank = color == "w" ? 0 : sizeX - 1;
140
141 // One square forward
142 if (this.board[x + shiftX][y] == V.EMPTY) {
143 moves.push(
144 this.getBasicMove([x, y], [x + shiftX, y])
145 );
146 // Next condition because pawns on 1st rank can generally jump
147 if (
148 x == startRank &&
149 this.board[x + 2 * shiftX][y] == V.EMPTY
150 ) {
151 // Two squares jump
152 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
153 }
154 }
155 // Captures
156 for (let shiftY of [-1, 1]) {
157 if (
158 y + shiftY >= 0 &&
159 y + shiftY < sizeY &&
160 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
161 this.canTake([x, y], [x + shiftX, y + shiftY])
162 ) {
163 moves.push(
164 this.getBasicMove([x, y], [x + shiftX, y + shiftY])
165 );
166 }
167 }
168
169 // En passant
170 const Lep = this.epSquares.length;
171 const epSquare = this.epSquares[Lep - 1]; //always at least one element
172 if (
173 !!epSquare &&
174 epSquare.x == x + shiftX &&
175 Math.abs(epSquare.y - y) == 1
176 ) {
177 let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
178 enpassantMove.vanish.push({
179 x: x,
180 y: epSquare.y,
181 p: "p",
182 c: this.getColor(x, epSquare.y)
183 });
184 moves.push(enpassantMove);
185 }
186
187 // Post-processing: remove falling pawns
188 if (x + shiftX == lastRank) {
189 moves.forEach(m => {
190 m.appear.pop();
191 });
192 }
193
194 return moves;
195 }
196
197 getAllValidMoves() {
198 let moves = super.getAllValidMoves();
199 const color = this.turn;
200 for (let i = 0; i < V.RESERVE_PIECES.length; i++)
201 moves = moves.concat(
202 this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
203 );
204 return this.filterValid(moves);
205 }
206
207 atLeastOneMove() {
208 if (!super.atLeastOneMove()) {
209 // Search one reserve move
210 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
211 let moves = this.filterValid(
212 this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
213 );
214 if (moves.length > 0) return true;
215 }
216 return false;
217 }
218 return true;
219 }
220
221 canTake([x1, y1], [x2, y2]) {
222 // Self-captures allowed, except for the king:
223 return this.getPiece(x2, y2) != V.KING;
224 }
225
3a2a7b5f
BA
226 postPlay(move) {
227 super.postPlay(move);
78d64531 228 if (move.vanish.length == 2 && move.appear.length == 2) return; //skip castle
3a2a7b5f
BA
229 const color = move.appear[0].c;
230 if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--;
231 else if (move.vanish.length == 2 && move.vanish[1].c == color)
78d64531
BA
232 // Self-capture
233 this.reserve[color][move.vanish[1].p]++;
78d64531
BA
234 }
235
3a2a7b5f
BA
236 postUndo(move) {
237 super.postUndo(move);
78d64531
BA
238 if (move.vanish.length == 2 && move.appear.length == 2) return;
239 const color = this.turn;
3a2a7b5f
BA
240 if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]++;
241 else if (move.vanish.length == 2 && move.vanish[1].c == color)
78d64531 242 this.reserve[color][move.vanish[1].p]--;
78d64531
BA
243 }
244
245 static get SEARCH_DEPTH() {
246 return 2;
247 }
248
249 evalPosition() {
250 let evaluation = super.evalPosition();
251 // Add reserves:
252 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
253 const p = V.RESERVE_PIECES[i];
254 evaluation += this.reserve["w"][p] * V.VALUES[p];
255 evaluation -= this.reserve["b"][p] * V.VALUES[p];
256 }
257 return evaluation;
258 }
259
260 getNotation(move) {
261 const finalSquare = V.CoordsToSquare(move.end);
262 if (move.vanish.length > 0) {
263 if (move.appear.length > 0) {
264 // Standard move
265 return super.getNotation(move);
266 } else {
267 // Pawn fallen: capturing or not
268 let res = "";
269 if (move.vanish.length == 2)
270 res += V.CoordToColumn(move.start.y) + "x";
271 return res + finalSquare;
272 }
273 }
274 // Rebirth:
275 const piece =
276 move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
277 return piece + "@" + V.CoordsToSquare(move.end);
278 }
279};