Commit | Line | Data |
---|---|---|
af34341d BA |
1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
2 | ||
eb2d61de | 3 | export class CheckeredRules extends ChessRules { |
7e8a7ea1 | 4 | |
4313762d BA |
5 | static get Options() { |
6 | return Object.assign( | |
7 | {}, | |
8 | ChessRules.Options, | |
9 | { | |
10 | check: [ | |
11 | { | |
12 | label: "With switch", | |
13 | defaut: true, | |
14 | variable: "switch" | |
15 | } | |
16 | ] | |
17 | } | |
18 | ); | |
19 | } | |
20 | ||
21 | static AbbreviateOptions(opts) { | |
22 | return (!opts["switch"] ? "NS" : ""); | |
23 | } | |
24 | ||
af34341d BA |
25 | static board2fen(b) { |
26 | const checkered_codes = { | |
27 | p: "s", | |
28 | q: "t", | |
29 | r: "u", | |
30 | b: "c", | |
31 | n: "o" | |
32 | }; | |
33 | if (b[0] == "c") return checkered_codes[b[1]]; | |
34 | return ChessRules.board2fen(b); | |
35 | } | |
36 | ||
37 | static fen2board(f) { | |
38 | // Tolerate upper-case versions of checkered pieces (why not?) | |
39 | const checkered_pieces = { | |
40 | s: "p", | |
41 | S: "p", | |
42 | t: "q", | |
43 | T: "q", | |
44 | u: "r", | |
45 | U: "r", | |
46 | c: "b", | |
47 | C: "b", | |
48 | o: "n", | |
49 | O: "n" | |
50 | }; | |
51 | if (Object.keys(checkered_pieces).includes(f)) | |
52 | return "c" + checkered_pieces[f]; | |
53 | return ChessRules.fen2board(f); | |
54 | } | |
55 | ||
56 | static get PIECES() { | |
57 | return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]); | |
58 | } | |
59 | ||
60 | getPpath(b) { | |
61 | return (b[0] == "c" ? "Checkered/" : "") + b; | |
62 | } | |
63 | ||
64 | setOtherVariables(fen) { | |
65 | super.setOtherVariables(fen); | |
66 | // Local stack of non-capturing checkered moves: | |
67 | this.cmoves = []; | |
68 | const cmove = V.ParseFen(fen).cmove; | |
69 | if (cmove == "-") this.cmoves.push(null); | |
70 | else { | |
71 | this.cmoves.push({ | |
72 | start: ChessRules.SquareToCoords(cmove.substr(0, 2)), | |
73 | end: ChessRules.SquareToCoords(cmove.substr(2)) | |
74 | }); | |
75 | } | |
76 | // Stage 1: as Checkered2. Stage 2: checkered pieces are autonomous | |
77 | const stageInfo = V.ParseFen(fen).stage; | |
e50a8025 | 78 | this.stage = parseInt(stageInfo[0], 10); |
eb2d61de | 79 | this.canSwitch = (this.stage == 1 && stageInfo[1] != '-'); |
af34341d BA |
80 | this.sideCheckered = (this.stage == 2 ? stageInfo[1] : undefined); |
81 | } | |
82 | ||
83 | static IsGoodFen(fen) { | |
84 | if (!ChessRules.IsGoodFen(fen)) return false; | |
85 | const fenParts = fen.split(" "); | |
86 | if (fenParts.length != 7) return false; | |
87 | if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/)) | |
88 | return false; | |
eb2d61de | 89 | if (!fenParts[6].match(/^[12][wb-]?$/)) return false; |
af34341d BA |
90 | return true; |
91 | } | |
92 | ||
93 | static IsGoodFlags(flags) { | |
94 | // 4 for castle + 16 for pawns | |
95 | return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/); | |
96 | } | |
97 | ||
98 | setFlags(fenflags) { | |
99 | super.setFlags(fenflags); //castleFlags | |
100 | this.pawnFlags = { | |
101 | w: [...Array(8)], //pawns can move 2 squares? | |
102 | b: [...Array(8)] | |
103 | }; | |
104 | const flags = fenflags.substr(4); //skip first 4 letters, for castle | |
105 | for (let c of ["w", "b"]) { | |
106 | for (let i = 0; i < 8; i++) | |
107 | this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1"; | |
108 | } | |
109 | } | |
110 | ||
111 | aggregateFlags() { | |
112 | return [this.castleFlags, this.pawnFlags]; | |
113 | } | |
114 | ||
115 | disaggregateFlags(flags) { | |
116 | this.castleFlags = flags[0]; | |
117 | this.pawnFlags = flags[1]; | |
118 | } | |
119 | ||
120 | getEpSquare(moveOrSquare) { | |
121 | // At stage 2, all pawns can be captured en-passant | |
122 | if ( | |
123 | this.stage == 2 || | |
124 | typeof moveOrSquare !== "object" || | |
125 | (moveOrSquare.appear.length > 0 && moveOrSquare.appear[0].c != 'c') | |
126 | ) | |
127 | return super.getEpSquare(moveOrSquare); | |
128 | // Checkered or switch move: no en-passant | |
129 | return undefined; | |
130 | } | |
131 | ||
132 | getCmove(move) { | |
133 | // No checkered move to undo at stage 2: | |
134 | if (this.stage == 1 && move.vanish.length == 1 && move.appear[0].c == "c") | |
135 | return { start: move.start, end: move.end }; | |
136 | return null; | |
137 | } | |
138 | ||
139 | canTake([x1, y1], [x2, y2]) { | |
140 | const color1 = this.getColor(x1, y1); | |
141 | const color2 = this.getColor(x2, y2); | |
142 | if (this.stage == 2) { | |
143 | // Black & White <-- takes --> Checkered | |
144 | const color1 = this.getColor(x1, y1); | |
145 | const color2 = this.getColor(x2, y2); | |
146 | return color1 != color2 && [color1, color2].includes('c'); | |
147 | } | |
148 | // Checkered aren't captured | |
149 | return ( | |
150 | color1 != color2 && | |
151 | color2 != "c" && | |
152 | (color1 != "c" || color2 != this.turn) | |
153 | ); | |
154 | } | |
155 | ||
165530a5 | 156 | getPotentialMovesFrom([x, y], noswitch) { |
af34341d BA |
157 | let standardMoves = super.getPotentialMovesFrom([x, y]); |
158 | if (this.stage == 1) { | |
159 | const color = this.turn; | |
160 | // Post-processing: apply "checkerization" of standard moves | |
161 | const lastRank = (color == "w" ? 0 : 7); | |
162 | let moves = []; | |
163 | // King is treated differently: it never turn checkered | |
164 | if (this.getPiece(x, y) == V.KING) { | |
165 | // If at least one checkered piece, allow switching: | |
165530a5 | 166 | if ( |
eb2d61de | 167 | this.canSwitch && !noswitch && |
165530a5 BA |
168 | this.board.some(b => b.some(cell => cell[0] == 'c')) |
169 | ) { | |
af34341d BA |
170 | const oppCol = V.GetOppCol(color); |
171 | moves.push( | |
172 | new Move({ | |
173 | start: { x: x, y: y }, | |
174 | end: { x: this.kingPos[oppCol][0], y: this.kingPos[oppCol][1] }, | |
175 | appear: [], | |
176 | vanish: [] | |
177 | }) | |
178 | ); | |
179 | } | |
180 | return standardMoves.concat(moves); | |
181 | } | |
182 | standardMoves.forEach(m => { | |
183 | if (m.vanish[0].p == V.PAWN) { | |
184 | if ( | |
185 | Math.abs(m.end.x - m.start.x) == 2 && | |
186 | !this.pawnFlags[this.turn][m.start.y] | |
187 | ) { | |
188 | return; //skip forbidden 2-squares jumps | |
189 | } | |
190 | if ( | |
191 | this.board[m.end.x][m.end.y] == V.EMPTY && | |
192 | m.vanish.length == 2 && | |
193 | this.getColor(m.start.x, m.start.y) == "c" | |
194 | ) { | |
195 | return; //checkered pawns cannot take en-passant | |
196 | } | |
197 | } | |
198 | if (m.vanish.length == 1) | |
199 | // No capture | |
200 | moves.push(m); | |
201 | else { | |
202 | // A capture occured (m.vanish.length == 2) | |
203 | m.appear[0].c = "c"; | |
204 | moves.push(m); | |
205 | if ( | |
206 | // Avoid promotions (already treated): | |
207 | m.appear[0].p != m.vanish[1].p && | |
208 | (m.vanish[0].p != V.PAWN || m.end.x != lastRank) | |
209 | ) { | |
210 | // Add transformation into captured piece | |
211 | let m2 = JSON.parse(JSON.stringify(m)); | |
212 | m2.appear[0].p = m.vanish[1].p; | |
213 | moves.push(m2); | |
214 | } | |
215 | } | |
216 | }); | |
217 | return moves; | |
218 | } | |
219 | return standardMoves; | |
220 | } | |
221 | ||
222 | getPotentialPawnMoves([x, y]) { | |
223 | const color = this.getColor(x, y); | |
224 | if (this.stage == 2) { | |
225 | const saveTurn = this.turn; | |
226 | if (this.sideCheckered == this.turn) { | |
227 | // Cannot change PawnSpecs.bidirectional, so cheat a little: | |
228 | this.turn = 'w'; | |
229 | const wMoves = super.getPotentialPawnMoves([x, y]); | |
230 | this.turn = 'b'; | |
231 | const bMoves = super.getPotentialPawnMoves([x, y]); | |
232 | this.turn = saveTurn; | |
233 | return wMoves.concat(bMoves); | |
234 | } | |
235 | // Playing with both colors: | |
236 | this.turn = color; | |
237 | const moves = super.getPotentialPawnMoves([x, y]); | |
238 | this.turn = saveTurn; | |
239 | return moves; | |
240 | } | |
241 | let moves = super.getPotentialPawnMoves([x, y]); | |
242 | // Post-process: set right color for checkered moves | |
243 | if (color == 'c') { | |
244 | moves.forEach(m => { | |
245 | m.appear[0].c = 'c'; //may be done twice if capture | |
246 | m.vanish[0].c = 'c'; | |
247 | }); | |
248 | } | |
249 | return moves; | |
250 | } | |
251 | ||
252 | canIplay(side, [x, y]) { | |
253 | if (this.stage == 2) { | |
254 | const color = this.getColor(x, y); | |
255 | return ( | |
256 | this.turn == this.sideCheckered | |
257 | ? color == 'c' | |
258 | : ['w', 'b'].includes(color) | |
259 | ); | |
260 | } | |
261 | return side == this.turn && [side, "c"].includes(this.getColor(x, y)); | |
262 | } | |
263 | ||
264 | // Does m2 un-do m1 ? (to disallow undoing checkered moves) | |
265 | oppositeMoves(m1, m2) { | |
266 | return ( | |
267 | !!m1 && | |
268 | m2.appear[0].c == "c" && | |
269 | m2.appear.length == 1 && | |
270 | m2.vanish.length == 1 && | |
271 | m1.start.x == m2.end.x && | |
272 | m1.end.x == m2.start.x && | |
273 | m1.start.y == m2.end.y && | |
274 | m1.end.y == m2.start.y | |
275 | ); | |
276 | } | |
277 | ||
278 | filterValid(moves) { | |
279 | if (moves.length == 0) return []; | |
280 | const color = this.turn; | |
281 | const oppCol = V.GetOppCol(color); | |
282 | const L = this.cmoves.length; //at least 1: init from FEN | |
283 | const stage = this.stage; //may change if switch | |
284 | return moves.filter(m => { | |
285 | // Checkered cannot be under check (no king) | |
286 | if (stage == 2 && this.sideCheckered == color) return true; | |
287 | this.play(m); | |
288 | let res = true; | |
289 | if (stage == 1) { | |
290 | if (m.appear.length == 0 && m.vanish.length == 0) { | |
291 | // Special "switch" move: kings must not be attacked by checkered. | |
292 | // Not checking for oppositeMoves here: checkered are autonomous | |
293 | res = ( | |
294 | !this.isAttacked(this.kingPos['w'], ['c']) && | |
295 | !this.isAttacked(this.kingPos['b'], ['c']) && | |
296 | this.getAllPotentialMoves().length > 0 | |
297 | ); | |
298 | } | |
299 | else res = !this.oppositeMoves(this.cmoves[L - 1], m); | |
300 | } | |
301 | if (res && m.appear.length > 0) res = !this.underCheck(color); | |
302 | // At stage 2, side with B & W can be undercheck with both kings: | |
303 | if (res && stage == 2) res = !this.underCheck(oppCol); | |
304 | this.undo(m); | |
305 | return res; | |
306 | }); | |
307 | } | |
308 | ||
309 | getAllPotentialMoves() { | |
310 | const color = this.turn; | |
311 | const oppCol = V.GetOppCol(color); | |
312 | let potentialMoves = []; | |
313 | for (let i = 0; i < V.size.x; i++) { | |
314 | for (let j = 0; j < V.size.y; j++) { | |
315 | const colIJ = this.getColor(i, j); | |
316 | if ( | |
317 | this.board[i][j] != V.EMPTY && | |
318 | ( | |
319 | (this.stage == 1 && colIJ != oppCol) || | |
320 | (this.stage == 2 && | |
321 | ( | |
322 | (this.sideCheckered == color && colIJ == 'c') || | |
323 | (this.sideCheckered != color && ['w', 'b'].includes(colIJ)) | |
324 | ) | |
325 | ) | |
326 | ) | |
327 | ) { | |
328 | Array.prototype.push.apply( | |
329 | potentialMoves, | |
330 | this.getPotentialMovesFrom([i, j]) | |
331 | ); | |
332 | } | |
333 | } | |
334 | } | |
335 | return potentialMoves; | |
336 | } | |
337 | ||
338 | atLeastOneMove() { | |
339 | const color = this.turn; | |
340 | const oppCol = V.GetOppCol(color); | |
341 | for (let i = 0; i < V.size.x; i++) { | |
342 | for (let j = 0; j < V.size.y; j++) { | |
343 | const colIJ = this.getColor(i, j); | |
344 | if ( | |
345 | this.board[i][j] != V.EMPTY && | |
346 | ( | |
347 | (this.stage == 1 && colIJ != oppCol) || | |
348 | (this.stage == 2 && | |
349 | ( | |
350 | (this.sideCheckered == color && colIJ == 'c') || | |
351 | (this.sideCheckered != color && ['w', 'b'].includes(colIJ)) | |
352 | ) | |
353 | ) | |
354 | ) | |
355 | ) { | |
165530a5 | 356 | const moves = this.getPotentialMovesFrom([i, j], "noswitch"); |
af34341d BA |
357 | if (moves.length > 0) { |
358 | for (let k = 0; k < moves.length; k++) | |
359 | if (this.filterValid([moves[k]]).length > 0) return true; | |
360 | } | |
361 | } | |
362 | } | |
363 | } | |
364 | return false; | |
365 | } | |
366 | ||
367 | // colors: array, 'w' and 'c' or 'b' and 'c' at stage 1, | |
368 | // just 'c' (or unused) at stage 2 | |
369 | isAttacked(sq, colors) { | |
370 | if (!Array.isArray(colors)) colors = [colors]; | |
371 | return ( | |
372 | this.isAttackedByPawn(sq, colors) || | |
373 | this.isAttackedByRook(sq, colors) || | |
374 | this.isAttackedByKnight(sq, colors) || | |
375 | this.isAttackedByBishop(sq, colors) || | |
376 | this.isAttackedByQueen(sq, colors) || | |
377 | this.isAttackedByKing(sq, colors) | |
378 | ); | |
379 | } | |
380 | ||
381 | isAttackedByPawn([x, y], colors) { | |
382 | for (let c of colors) { | |
383 | let shifts = []; | |
384 | if (this.stage == 1) { | |
385 | const color = (c == "c" ? this.turn : c); | |
386 | shifts = [color == "w" ? 1 : -1]; | |
387 | } | |
388 | else { | |
389 | // Stage 2: checkered pawns are bidirectional | |
390 | if (c == 'c') shifts = [-1, 1]; | |
391 | else shifts = [c == "w" ? 1 : -1]; | |
392 | } | |
393 | for (let pawnShift of shifts) { | |
394 | if (x + pawnShift >= 0 && x + pawnShift < 8) { | |
395 | for (let i of [-1, 1]) { | |
396 | if ( | |
397 | y + i >= 0 && | |
398 | y + i < 8 && | |
399 | this.getPiece(x + pawnShift, y + i) == V.PAWN && | |
400 | this.getColor(x + pawnShift, y + i) == c | |
401 | ) { | |
402 | return true; | |
403 | } | |
404 | } | |
405 | } | |
406 | } | |
407 | } | |
408 | return false; | |
409 | } | |
410 | ||
411 | isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) { | |
412 | for (let step of steps) { | |
413 | let rx = x + step[0], | |
414 | ry = y + step[1]; | |
415 | while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) { | |
416 | rx += step[0]; | |
417 | ry += step[1]; | |
418 | } | |
419 | if ( | |
420 | V.OnBoard(rx, ry) && | |
421 | this.getPiece(rx, ry) === piece && | |
422 | colors.includes(this.getColor(rx, ry)) | |
423 | ) { | |
424 | return true; | |
425 | } | |
426 | } | |
427 | return false; | |
428 | } | |
429 | ||
430 | isAttackedByRook(sq, colors) { | |
431 | return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]); | |
432 | } | |
433 | ||
434 | isAttackedByKnight(sq, colors) { | |
435 | return this.isAttackedBySlideNJump( | |
4313762d | 436 | sq, colors, V.KNIGHT, V.steps[V.KNIGHT], "oneStep"); |
af34341d BA |
437 | } |
438 | ||
439 | isAttackedByBishop(sq, colors) { | |
440 | return this.isAttackedBySlideNJump( | |
441 | sq, colors, V.BISHOP, V.steps[V.BISHOP]); | |
442 | } | |
443 | ||
444 | isAttackedByQueen(sq, colors) { | |
445 | return this.isAttackedBySlideNJump( | |
4313762d | 446 | sq, colors, V.QUEEN, V.steps[V.ROOK].concat(V.steps[V.BISHOP])); |
af34341d BA |
447 | } |
448 | ||
449 | isAttackedByKing(sq, colors) { | |
450 | return this.isAttackedBySlideNJump( | |
4313762d BA |
451 | sq, colors, V.KING, |
452 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep" | |
af34341d BA |
453 | ); |
454 | } | |
455 | ||
456 | underCheck(color) { | |
457 | if (this.stage == 1) | |
458 | return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]); | |
459 | if (color == this.sideCheckered) return false; | |
460 | return ( | |
461 | this.isAttacked(this.kingPos['w'], ["c"]) || | |
462 | this.isAttacked(this.kingPos['b'], ["c"]) | |
463 | ); | |
464 | } | |
465 | ||
466 | getCheckSquares() { | |
467 | const color = this.turn; | |
468 | if (this.stage == 1) { | |
469 | // Artifically change turn, for checkered pawns | |
470 | this.turn = V.GetOppCol(color); | |
471 | const kingAttacked = | |
472 | this.isAttacked( | |
473 | this.kingPos[color], | |
474 | [V.GetOppCol(color), "c"] | |
475 | ); | |
476 | let res = kingAttacked | |
477 | ? [JSON.parse(JSON.stringify(this.kingPos[color]))] | |
478 | : []; | |
479 | this.turn = color; | |
480 | return res; | |
481 | } | |
482 | if (this.sideCheckered == color) return []; | |
483 | let res = []; | |
484 | for (let c of ['w', 'b']) { | |
485 | if (this.isAttacked(this.kingPos[c], ['c'])) | |
486 | res.push(JSON.parse(JSON.stringify(this.kingPos[c]))); | |
487 | } | |
488 | return res; | |
489 | } | |
490 | ||
491 | play(move) { | |
492 | move.flags = JSON.stringify(this.aggregateFlags()); | |
493 | this.epSquares.push(this.getEpSquare(move)); | |
494 | V.PlayOnBoard(this.board, move); | |
495 | if (move.appear.length > 0 || move.vanish.length > 0) | |
496 | { | |
497 | this.turn = V.GetOppCol(this.turn); | |
498 | this.movesCount++; | |
499 | } | |
500 | this.postPlay(move); | |
501 | } | |
502 | ||
af34341d BA |
503 | postPlay(move) { |
504 | if (move.appear.length == 0 && move.vanish.length == 0) { | |
505 | this.stage = 2; | |
506 | this.sideCheckered = this.turn; | |
507 | } | |
508 | else { | |
509 | const c = move.vanish[0].c; | |
510 | const piece = move.vanish[0].p; | |
511 | if (piece == V.KING) { | |
512 | this.kingPos[c][0] = move.appear[0].x; | |
513 | this.kingPos[c][1] = move.appear[0].y; | |
514 | } | |
a9e1202b | 515 | super.updateCastleFlags(move, piece); |
5d74fcea BA |
516 | if ( |
517 | [1, 6].includes(move.start.x) && | |
518 | move.vanish[0].p == V.PAWN && | |
519 | Math.abs(move.end.x - move.start.x) == 2 | |
520 | ) { | |
521 | // This move turns off a 2-squares pawn flag | |
af34341d | 522 | this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false; |
5d74fcea | 523 | } |
af34341d BA |
524 | } |
525 | this.cmoves.push(this.getCmove(move)); | |
526 | } | |
527 | ||
528 | undo(move) { | |
529 | this.epSquares.pop(); | |
530 | this.disaggregateFlags(JSON.parse(move.flags)); | |
531 | V.UndoOnBoard(this.board, move); | |
532 | if (move.appear.length > 0 || move.vanish.length > 0) | |
533 | { | |
534 | this.turn = V.GetOppCol(this.turn); | |
535 | this.movesCount--; | |
536 | } | |
537 | this.postUndo(move); | |
538 | } | |
539 | ||
540 | postUndo(move) { | |
541 | if (move.appear.length == 0 && move.vanish.length == 0) this.stage = 1; | |
542 | else super.postUndo(move); | |
543 | this.cmoves.pop(); | |
544 | } | |
545 | ||
546 | getCurrentScore() { | |
547 | const color = this.turn; | |
548 | if (this.stage == 1) { | |
549 | if (this.atLeastOneMove()) return "*"; | |
550 | // Artifically change turn, for checkered pawns | |
551 | this.turn = V.GetOppCol(this.turn); | |
552 | const res = | |
553 | this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]) | |
554 | ? color == "w" | |
555 | ? "0-1" | |
556 | : "1-0" | |
557 | : "1/2"; | |
558 | this.turn = V.GetOppCol(this.turn); | |
559 | return res; | |
560 | } | |
561 | // Stage == 2: | |
562 | if (this.sideCheckered == this.turn) { | |
563 | // Check if remaining checkered pieces: if none, I lost | |
564 | if (this.board.some(b => b.some(cell => cell[0] == 'c'))) { | |
565 | if (!this.atLeastOneMove()) return "1/2"; | |
566 | return "*"; | |
567 | } | |
568 | return color == 'w' ? "0-1" : "1-0"; | |
569 | } | |
570 | if (this.atLeastOneMove()) return "*"; | |
571 | let res = this.isAttacked(this.kingPos['w'], ["c"]); | |
572 | if (!res) res = this.isAttacked(this.kingPos['b'], ["c"]); | |
573 | if (res) return color == 'w' ? "0-1" : "1-0"; | |
574 | return "1/2"; | |
575 | } | |
576 | ||
577 | evalPosition() { | |
578 | let evaluation = 0; | |
579 | // Just count material for now, considering checkered neutral at stage 1. | |
580 | const baseSign = (this.turn == 'w' ? 1 : -1); | |
581 | for (let i = 0; i < V.size.x; i++) { | |
582 | for (let j = 0; j < V.size.y; j++) { | |
583 | if (this.board[i][j] != V.EMPTY) { | |
584 | const sqColor = this.getColor(i, j); | |
585 | if (this.stage == 1) { | |
586 | if (["w", "b"].includes(sqColor)) { | |
587 | const sign = sqColor == "w" ? 1 : -1; | |
588 | evaluation += sign * V.VALUES[this.getPiece(i, j)]; | |
589 | } | |
590 | } | |
591 | else { | |
592 | const sign = | |
593 | this.sideCheckered == this.turn | |
594 | ? (sqColor == 'c' ? 1 : -1) * baseSign | |
595 | : (sqColor == 'c' ? -1 : 1) * baseSign; | |
596 | evaluation += sign * V.VALUES[this.getPiece(i, j)]; | |
597 | } | |
598 | } | |
599 | } | |
600 | } | |
601 | return evaluation; | |
602 | } | |
603 | ||
eb2d61de BA |
604 | static GenRandInitFen(options) { |
605 | const baseFen = ChessRules.GenRandInitFen(options.randomness); | |
606 | return ( | |
607 | // Add 16 pawns flags + empty cmove + stage == 1: | |
608 | baseFen.slice(0, -2) + "1111111111111111 - - 1" + | |
609 | (!options["switch"] ? '-' : "") | |
610 | ); | |
af34341d BA |
611 | } |
612 | ||
613 | static ParseFen(fen) { | |
614 | const fenParts = fen.split(" "); | |
615 | return Object.assign( | |
616 | ChessRules.ParseFen(fen), | |
617 | { | |
618 | cmove: fenParts[5], | |
619 | stage: fenParts[6] | |
620 | } | |
621 | ); | |
622 | } | |
623 | ||
624 | getCmoveFen() { | |
625 | const L = this.cmoves.length; | |
626 | return ( | |
627 | !this.cmoves[L - 1] | |
628 | ? "-" | |
629 | : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) + | |
630 | ChessRules.CoordsToSquare(this.cmoves[L - 1].end) | |
631 | ); | |
632 | } | |
633 | ||
634 | getStageFen() { | |
eb2d61de BA |
635 | if (this.stage == 1) return "1" + (!this.canSwitch ? '-' : ""); |
636 | // Stage == 2: | |
637 | return "2" + this.sideCheckered; | |
af34341d BA |
638 | } |
639 | ||
640 | getFen() { | |
641 | return ( | |
642 | super.getFen() + " " + this.getCmoveFen() + " " + this.getStageFen() | |
643 | ); | |
644 | } | |
645 | ||
646 | getFenForRepeat() { | |
647 | return ( | |
648 | super.getFenForRepeat() + "_" + | |
649 | this.getCmoveFen() + "_" + this.getStageFen() | |
650 | ); | |
651 | } | |
652 | ||
653 | getFlagsFen() { | |
654 | let fen = super.getFlagsFen(); | |
655 | // Add pawns flags | |
656 | for (let c of ["w", "b"]) | |
657 | for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0"); | |
658 | return fen; | |
659 | } | |
660 | ||
661 | static get SEARCH_DEPTH() { | |
662 | return 2; | |
663 | } | |
664 | ||
665 | getComputerMove() { | |
666 | // To simplify, prevent the bot from switching (TODO...) | |
667 | return ( | |
668 | super.getComputerMove( | |
669 | this.getAllValidMoves().filter(m => m.appear.length > 0) | |
670 | ) | |
671 | ); | |
672 | } | |
673 | ||
674 | getNotation(move) { | |
675 | if (move.appear.length == 0 && move.vanish.length == 0) return "S"; | |
676 | if (move.appear.length == 2) { | |
677 | // Castle | |
678 | if (move.end.y < move.start.y) return "0-0-0"; | |
679 | return "0-0"; | |
680 | } | |
681 | ||
682 | const finalSquare = V.CoordsToSquare(move.end); | |
683 | const piece = this.getPiece(move.start.x, move.start.y); | |
684 | let notation = ""; | |
685 | if (piece == V.PAWN) { | |
686 | if (move.vanish.length > 1) { | |
687 | const startColumn = V.CoordToColumn(move.start.y); | |
688 | notation = startColumn + "x" + finalSquare; | |
689 | } else notation = finalSquare; | |
690 | } else { | |
691 | // Piece movement | |
692 | notation = | |
693 | piece.toUpperCase() + | |
694 | (move.vanish.length > 1 ? "x" : "") + | |
695 | finalSquare; | |
696 | } | |
697 | if (move.appear[0].p != move.vanish[0].p) | |
698 | notation += "=" + move.appear[0].p.toUpperCase(); | |
699 | return notation; | |
700 | } | |
7e8a7ea1 | 701 | |
af34341d | 702 | }; |