A few bugs fixes
[vchess.git] / client / src / variants / Clorange.js
... / ...
CommitLineData
1import { ChessRules, PiPo, Move } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
3
4export class ClorangeRules 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]{20,20}$/))
10 return false;
11 return true;
12 }
13
14 static ParseFen(fen) {
15 const fenParts = fen.split(" ");
16 return Object.assign(
17 ChessRules.ParseFen(fen),
18 { reserve: fenParts[5] }
19 );
20 }
21
22 static GenRandInitFen(randomness) {
23 // Capturing and non-capturing reserves:
24 return ChessRules.GenRandInitFen(randomness) + " 00000000000000000000";
25 }
26
27 getFen() {
28 return super.getFen() + " " + this.getReserveFen();
29 }
30
31 getFenForRepeat() {
32 return super.getFenForRepeat() + "_" + this.getReserveFen();
33 }
34
35 getReserveFen() {
36 return (
37 Object.keys(this.reserve).map(
38 c => Object.values(this.reserve[c]).join("")).join("")
39 );
40 }
41
42 getEpSquare(moveOrSquare) {
43 if (!moveOrSquare) return undefined;
44 if (typeof moveOrSquare === "string") {
45 const square = moveOrSquare;
46 if (square == "-") return undefined;
47 return V.SquareToCoords(square);
48 }
49 const move = moveOrSquare;
50 const s = move.start,
51 e = move.end;
52 if (
53 s.y == e.y &&
54 Math.abs(s.x - e.x) == 2 &&
55 move.vanish.length > 0 && ['p', 's'].includes(move.vanish[0].p)
56 ) {
57 return {
58 x: (s.x + e.x) / 2,
59 y: s.y
60 };
61 }
62 return undefined;
63 }
64
65 setOtherVariables(fen) {
66 super.setOtherVariables(fen);
67 // Also init reserves (used by the interface to show landable pieces)
68 const reserve =
69 V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
70 this.reserve = {
71 w: {
72 'p': reserve[0],
73 'r': reserve[1],
74 'n': reserve[2],
75 'b': reserve[3],
76 'q': reserve[4],
77 's': reserve[5],
78 'u': reserve[6],
79 'o': reserve[7],
80 'c': reserve[8],
81 't': reserve[9]
82 },
83 b: {
84 'p': reserve[10],
85 'r': reserve[11],
86 'n': reserve[12],
87 'b': reserve[13],
88 'q': reserve[14],
89 's': reserve[15],
90 'u': reserve[16],
91 'o': reserve[17],
92 'c': reserve[18],
93 't': reserve[19]
94 }
95 };
96 }
97
98 getColor(i, j) {
99 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
100 return this.board[i][j].charAt(0);
101 }
102
103 getPiece(i, j) {
104 if (i >= V.size.x) return V.RESERVE_PIECES[j];
105 return this.board[i][j].charAt(1);
106 }
107
108 getPpath(b) {
109 return (V.NON_VIOLENT.includes(b[1]) ? "Clorange/" : "") + b;
110 }
111
112 getReservePpath(index, color) {
113 const prefix =
114 (V.NON_VIOLENT.includes(V.RESERVE_PIECES[index]) ? "Clorange/" : "");
115 return prefix + color + V.RESERVE_PIECES[index];
116 }
117
118 static get NON_VIOLENT() {
119 return ['s', 'u', 'o', 'c', 't'];
120 }
121
122 static get PIECES() {
123 return ChessRules.PIECES.concat(V.NON_VIOLENT);
124 }
125
126 // Ordering on reserve pieces
127 static get RESERVE_PIECES() {
128 return V.PIECES.filter(p => p != 'k');
129 }
130
131 getReserveMoves([x, y]) {
132 const color = this.turn;
133 const p = V.RESERVE_PIECES[y];
134 if (this.reserve[color][p] == 0) return [];
135 let moves = [];
136 let rank1 = 0;
137 let rank2 = V.size.x - 1;
138 if (['p', 's'].includes(p)) {
139 if (color == 'w') rank1++;
140 else rank2--;
141 }
142 for (let i = rank1; i <= rank2; i++) {
143 for (let j = 0; j < V.size.y; j++) {
144 if (this.board[i][j] == V.EMPTY) {
145 let mv = new Move({
146 appear: [
147 new PiPo({
148 x: i,
149 y: j,
150 c: color,
151 p: p
152 })
153 ],
154 vanish: [],
155 start: { x: x, y: y }, //a bit artificial...
156 end: { x: i, y: j }
157 });
158 moves.push(mv);
159 }
160 }
161 }
162 return moves;
163 }
164
165 getPotentialMovesFrom([x, y]) {
166 if (x >= V.size.x)
167 // Reserves, outside of board: x == sizeX(+1)
168 return this.getReserveMoves([x, y]);
169 // Standard moves
170 switch (this.getPiece(x, y)) {
171 case 's': return this.getPotentialPawnMoves([x, y]);
172 case 'u': return super.getPotentialRookMoves([x, y]);
173 case 'o': return super.getPotentialKnightMoves([x, y]);
174 case 'c': return super.getPotentialBishopMoves([x, y]);
175 case 't': return super.getPotentialQueenMoves([x, y]);
176 default: return super.getPotentialMovesFrom([x, y]);
177 }
178 return []; //never reached
179 }
180
181 getPotentialPawnMoves(sq) {
182 let moves = super.getPotentialPawnMoves(sq);
183 if (moves.length > 0 && moves[0].vanish[0].p == 's') {
184 // Remove captures for non-violent pawns:
185 moves = moves.filter(m => m.vanish.length == 1);
186 moves.forEach(m => {
187 if (m.appear[0].p != 's') {
188 // Promotion pieces should be non-violent as well:
189 const pIdx = ChessRules.PIECES.findIndex(p => p == m.appear[0].p)
190 m.appear[0].p = V.NON_VIOLENT[pIdx];
191 }
192 });
193 }
194 return moves;
195 }
196
197 getSlideNJumpMoves([x, y], steps, oneStep) {
198 let moves = [];
199 const canTake = ChessRules.PIECES.includes(this.getPiece(x, y));
200 outerLoop: for (let step of steps) {
201 let i = x + step[0];
202 let j = y + step[1];
203 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
204 moves.push(this.getBasicMove([x, y], [i, j]));
205 if (oneStep) continue outerLoop;
206 i += step[0];
207 j += step[1];
208 }
209 if (V.OnBoard(i, j) && canTake && this.canTake([x, y], [i, j]))
210 moves.push(this.getBasicMove([x, y], [i, j]));
211 }
212 return moves;
213 }
214
215 getAllValidMoves() {
216 let moves = super.getAllPotentialMoves();
217 const color = this.turn;
218 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
219 moves = moves.concat(
220 this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
221 );
222 }
223 return this.filterValid(moves);
224 }
225
226 atLeastOneMove() {
227 if (!super.atLeastOneMove()) {
228 // Search one reserve move
229 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
230 let moves = this.filterValid(
231 this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
232 );
233 if (moves.length > 0) return true;
234 }
235 return false;
236 }
237 return true;
238 }
239
240 prePlay(move) {
241 super.prePlay(move);
242 // Skip castle:
243 if (move.vanish.length == 2 && move.appear.length == 2) return;
244 const color = this.turn;
245 if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--;
246 else if (move.vanish.length == 2) {
247 // Capture
248 const normal = ChessRules.PIECES.includes(move.vanish[1].p);
249 const pIdx =
250 normal
251 ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p)
252 : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p);
253 const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx];
254 this.reserve[move.vanish[1].c][rPiece]++;
255 }
256 }
257
258 postUndo(move) {
259 super.postUndo(move);
260 if (move.vanish.length == 2 && move.appear.length == 2) return;
261 const color = this.turn;
262 if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]++;
263 else if (move.vanish.length == 2) {
264 const normal = ChessRules.PIECES.includes(move.vanish[1].p);
265 const pIdx =
266 normal
267 ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p)
268 : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p);
269 const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx];
270 this.reserve[move.vanish[1].c][rPiece]--;
271 }
272 }
273
274 static get SEARCH_DEPTH() {
275 return 2;
276 }
277
278 static get VALUES() {
279 return Object.assign(
280 {
281 s: 0.75,
282 u: 4,
283 o: 2,
284 c: 2,
285 t: 7
286 },
287 ChessRules.VALUES
288 );
289 }
290
291 evalPosition() {
292 let evaluation = super.evalPosition();
293 // Add reserves:
294 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
295 const p = V.RESERVE_PIECES[i];
296 evaluation += this.reserve["w"][p] * V.VALUES[p];
297 evaluation -= this.reserve["b"][p] * V.VALUES[p];
298 }
299 return evaluation;
300 }
301
302 getNotation(move) {
303 const finalSquare = V.CoordsToSquare(move.end);
304 if (move.vanish.length > 0) {
305 // Standard move (maybe with non-violent piece)
306 let notation = super.getNotation(move);
307 if (move.vanish[0].p == 's' && move.appear[0].p != 's')
308 // Fix non-violent promotions:
309 notation += "=" + move.appear[0].p.toUpperCase();
310 return notation;
311 }
312 // Rebirth:
313 const piece =
314 move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
315 return piece + "@" + V.CoordsToSquare(move.end);
316 }
317};