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