Commit | Line | Data |
---|---|---|
68e19a44 | 1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
0c3fe8a6 | 2 | |
6808d7a1 | 3 | export const VariantRules = class CheckeredRules extends ChessRules { |
6808d7a1 | 4 | static board2fen(b) { |
dac39588 | 5 | const checkered_codes = { |
6808d7a1 BA |
6 | p: "s", |
7 | q: "t", | |
8 | r: "u", | |
9 | b: "c", | |
10 | n: "o" | |
dac39588 | 11 | }; |
6808d7a1 | 12 | if (b[0] == "c") return checkered_codes[b[1]]; |
dac39588 BA |
13 | return ChessRules.board2fen(b); |
14 | } | |
15 | ||
6808d7a1 | 16 | static fen2board(f) { |
dac39588 BA |
17 | // Tolerate upper-case versions of checkered pieces (why not?) |
18 | const checkered_pieces = { | |
6808d7a1 BA |
19 | s: "p", |
20 | S: "p", | |
21 | t: "q", | |
22 | T: "q", | |
23 | u: "r", | |
24 | U: "r", | |
25 | c: "b", | |
26 | C: "b", | |
27 | o: "n", | |
28 | O: "n" | |
dac39588 BA |
29 | }; |
30 | if (Object.keys(checkered_pieces).includes(f)) | |
6808d7a1 | 31 | return "c" + checkered_pieces[f]; |
dac39588 BA |
32 | return ChessRules.fen2board(f); |
33 | } | |
34 | ||
6808d7a1 BA |
35 | static get PIECES() { |
36 | return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]); | |
dac39588 | 37 | } |
7931e479 | 38 | |
241bf8f2 | 39 | getPpath(b) { |
d1be8046 | 40 | return (b[0] == "c" ? "Checkered/" : "") + b; |
241bf8f2 BA |
41 | } |
42 | ||
6808d7a1 | 43 | setOtherVariables(fen) { |
03cff0f7 BA |
44 | super.setOtherVariables(fen); |
45 | // Local stack of non-capturing checkered moves: | |
46 | this.cmoves = []; | |
3a2a7b5f | 47 | const cmove = V.ParseFen(fen).cmove; |
6808d7a1 BA |
48 | if (cmove == "-") this.cmoves.push(null); |
49 | else { | |
03cff0f7 | 50 | this.cmoves.push({ |
6808d7a1 BA |
51 | start: ChessRules.SquareToCoords(cmove.substr(0, 2)), |
52 | end: ChessRules.SquareToCoords(cmove.substr(2)) | |
03cff0f7 BA |
53 | }); |
54 | } | |
55 | } | |
56 | ||
6808d7a1 BA |
57 | static IsGoodFen(fen) { |
58 | if (!ChessRules.IsGoodFen(fen)) return false; | |
03cff0f7 | 59 | const fenParts = fen.split(" "); |
6808d7a1 | 60 | if (fenParts.length != 6) return false; |
03cff0f7 BA |
61 | if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/)) |
62 | return false; | |
63 | return true; | |
64 | } | |
65 | ||
6808d7a1 | 66 | static IsGoodFlags(flags) { |
dac39588 | 67 | // 4 for castle + 16 for pawns |
3a2a7b5f | 68 | return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/); |
dac39588 BA |
69 | } |
70 | ||
6808d7a1 | 71 | setFlags(fenflags) { |
dac39588 | 72 | super.setFlags(fenflags); //castleFlags |
6808d7a1 BA |
73 | this.pawnFlags = { |
74 | w: [...Array(8).fill(true)], //pawns can move 2 squares? | |
75 | b: [...Array(8).fill(true)] | |
dac39588 | 76 | }; |
3a2a7b5f | 77 | const flags = fenflags.substr(4); //skip first 4 letters, for castle |
6808d7a1 BA |
78 | for (let c of ["w", "b"]) { |
79 | for (let i = 0; i < 8; i++) | |
80 | this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1"; | |
dac39588 BA |
81 | } |
82 | } | |
83 | ||
6808d7a1 | 84 | aggregateFlags() { |
dac39588 BA |
85 | return [this.castleFlags, this.pawnFlags]; |
86 | } | |
87 | ||
6808d7a1 | 88 | disaggregateFlags(flags) { |
dac39588 BA |
89 | this.castleFlags = flags[0]; |
90 | this.pawnFlags = flags[1]; | |
91 | } | |
1d184b4c | 92 | |
e727fe31 | 93 | getEpSquare(moveOrSquare) { |
bbf66837 | 94 | if (typeof moveOrSquare !== "object" || moveOrSquare.appear[0].c != 'c') |
e727fe31 BA |
95 | return super.getEpSquare(moveOrSquare); |
96 | // Checkered move: no en-passant | |
97 | return undefined; | |
98 | } | |
99 | ||
6808d7a1 BA |
100 | getCmove(move) { |
101 | if (move.appear[0].c == "c" && move.vanish.length == 1) | |
102 | return { start: move.start, end: move.end }; | |
03cff0f7 BA |
103 | return null; |
104 | } | |
105 | ||
6808d7a1 BA |
106 | canTake([x1, y1], [x2, y2]) { |
107 | const color1 = this.getColor(x1, y1); | |
108 | const color2 = this.getColor(x2, y2); | |
dac39588 | 109 | // Checkered aren't captured |
6808d7a1 BA |
110 | return ( |
111 | color1 != color2 && | |
112 | color2 != "c" && | |
113 | (color1 != "c" || color2 != this.turn) | |
114 | ); | |
dac39588 BA |
115 | } |
116 | ||
117 | // Post-processing: apply "checkerization" of standard moves | |
6808d7a1 BA |
118 | getPotentialMovesFrom([x, y]) { |
119 | let standardMoves = super.getPotentialMovesFrom([x, y]); | |
dac39588 | 120 | const lastRank = this.turn == "w" ? 0 : 7; |
71ef1664 BA |
121 | // King has to be treated differently (for castles) |
122 | if (this.getPiece(x, y) == V.KING) return standardMoves; | |
dac39588 BA |
123 | let moves = []; |
124 | standardMoves.forEach(m => { | |
6808d7a1 BA |
125 | if (m.vanish[0].p == V.PAWN) { |
126 | if ( | |
127 | Math.abs(m.end.x - m.start.x) == 2 && | |
128 | !this.pawnFlags[this.turn][m.start.y] | |
129 | ) | |
dac39588 | 130 | return; //skip forbidden 2-squares jumps |
6808d7a1 BA |
131 | if ( |
132 | this.board[m.end.x][m.end.y] == V.EMPTY && | |
133 | m.vanish.length == 2 && | |
134 | this.getColor(m.start.x, m.start.y) == "c" | |
135 | ) { | |
dac39588 BA |
136 | return; //checkered pawns cannot take en-passant |
137 | } | |
138 | } | |
6808d7a1 | 139 | if (m.vanish.length == 1) moves.push(m); |
241bf8f2 | 140 | // No capture |
6808d7a1 | 141 | else { |
dac39588 BA |
142 | // A capture occured (m.vanish.length == 2) |
143 | m.appear[0].c = "c"; | |
144 | moves.push(m); | |
6808d7a1 BA |
145 | if ( |
146 | m.appear[0].p != m.vanish[1].p && //avoid promotions (already treated): | |
147 | (m.vanish[0].p != V.PAWN || m.end.x != lastRank) | |
148 | ) { | |
dac39588 BA |
149 | // Add transformation into captured piece |
150 | let m2 = JSON.parse(JSON.stringify(m)); | |
151 | m2.appear[0].p = m.vanish[1].p; | |
152 | moves.push(m2); | |
153 | } | |
154 | } | |
155 | }); | |
156 | return moves; | |
157 | } | |
158 | ||
e727fe31 BA |
159 | getPotentialPawnMoves([x, y]) { |
160 | const color = this.turn; | |
161 | let moves = []; | |
162 | const [sizeX, sizeY] = [V.size.x, V.size.y]; | |
163 | const shiftX = color == "w" ? -1 : 1; | |
164 | const startRank = color == "w" ? sizeX - 2 : 1; | |
165 | const lastRank = color == "w" ? 0 : sizeX - 1; | |
166 | const pawnColor = this.getColor(x, y); //can be checkered | |
167 | ||
168 | const finalPieces = | |
169 | x + shiftX == lastRank | |
170 | ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] | |
171 | : [V.PAWN]; | |
172 | if (this.board[x + shiftX][y] == V.EMPTY) { | |
173 | // One square forward | |
174 | for (let piece of finalPieces) { | |
175 | moves.push( | |
176 | this.getBasicMove([x, y], [x + shiftX, y], { | |
177 | c: pawnColor, | |
178 | p: piece | |
179 | }) | |
180 | ); | |
181 | } | |
182 | if ( | |
183 | x == startRank && | |
184 | this.board[x + 2 * shiftX][y] == V.EMPTY | |
185 | ) { | |
186 | // Two squares jump | |
187 | moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y])); | |
188 | } | |
189 | } | |
190 | // Captures | |
191 | for (let shiftY of [-1, 1]) { | |
192 | if ( | |
193 | y + shiftY >= 0 && | |
194 | y + shiftY < sizeY && | |
195 | this.board[x + shiftX][y + shiftY] != V.EMPTY && | |
196 | this.canTake([x, y], [x + shiftX, y + shiftY]) | |
197 | ) { | |
198 | for (let piece of finalPieces) { | |
199 | moves.push( | |
200 | this.getBasicMove([x, y], [x + shiftX, y + shiftY], { | |
201 | c: pawnColor, | |
202 | p: piece | |
203 | }) | |
204 | ); | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | // En passant | |
210 | const Lep = this.epSquares.length; | |
211 | const epSquare = this.epSquares[Lep - 1]; //always at least one element | |
212 | if ( | |
213 | !!epSquare && | |
214 | epSquare.x == x + shiftX && | |
215 | Math.abs(epSquare.y - y) == 1 | |
216 | ) { | |
217 | let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]); | |
218 | enpassantMove.vanish.push({ | |
219 | x: x, | |
220 | y: epSquare.y, | |
221 | p: "p", | |
222 | c: this.getColor(x, epSquare.y) | |
223 | }); | |
224 | moves.push(enpassantMove); | |
225 | } | |
226 | ||
227 | return moves; | |
228 | } | |
229 | ||
68e19a44 BA |
230 | // Same as in base_rules but with an array given to isAttacked: |
231 | getCastleMoves([x, y]) { | |
232 | const c = this.getColor(x, y); | |
233 | if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c]) | |
234 | return []; //x isn't first rank, or king has moved (shortcut) | |
235 | ||
236 | // Castling ? | |
237 | const oppCol = V.GetOppCol(c); | |
238 | let moves = []; | |
239 | let i = 0; | |
240 | // King, then rook: | |
241 | const finalSquares = [ | |
242 | [2, 3], | |
243 | [V.size.y - 2, V.size.y - 3] | |
244 | ]; | |
245 | castlingCheck: for ( | |
246 | let castleSide = 0; | |
247 | castleSide < 2; | |
248 | castleSide++ //large, then small | |
249 | ) { | |
250 | if (this.castleFlags[c][castleSide] >= V.size.y) continue; | |
251 | // If this code is reached, rooks and king are on initial position | |
252 | ||
253 | // Nothing on the path of the king ? (and no checks) | |
254 | const finDist = finalSquares[castleSide][0] - y; | |
255 | let step = finDist / Math.max(1, Math.abs(finDist)); | |
256 | i = y; | |
257 | do { | |
258 | if ( | |
259 | this.isAttacked([x, i], [oppCol]) || | |
260 | (this.board[x][i] != V.EMPTY && | |
261 | // NOTE: next check is enough, because of chessboard constraints | |
262 | (this.getColor(x, i) != c || | |
263 | ![V.KING, V.ROOK].includes(this.getPiece(x, i)))) | |
264 | ) { | |
265 | continue castlingCheck; | |
266 | } | |
267 | i += step; | |
268 | } while (i != finalSquares[castleSide][0]); | |
269 | ||
270 | // Nothing on the path to the rook? | |
271 | step = castleSide == 0 ? -1 : 1; | |
272 | const rookPos = this.castleFlags[c][castleSide]; | |
273 | for (i = y + step; i != rookPos; i += step) { | |
274 | if (this.board[x][i] != V.EMPTY) continue castlingCheck; | |
275 | } | |
276 | ||
277 | // Nothing on final squares, except maybe king and castling rook? | |
278 | for (i = 0; i < 2; i++) { | |
279 | if ( | |
280 | this.board[x][finalSquares[castleSide][i]] != V.EMPTY && | |
281 | this.getPiece(x, finalSquares[castleSide][i]) != V.KING && | |
282 | finalSquares[castleSide][i] != rookPos | |
283 | ) { | |
284 | continue castlingCheck; | |
285 | } | |
286 | } | |
287 | ||
288 | // If this code is reached, castle is valid | |
289 | moves.push( | |
290 | new Move({ | |
291 | appear: [ | |
292 | new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }), | |
293 | new PiPo({ x: x, y: finalSquares[castleSide][1], p: V.ROOK, c: c }) | |
294 | ], | |
295 | vanish: [ | |
296 | new PiPo({ x: x, y: y, p: V.KING, c: c }), | |
297 | new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c }) | |
298 | ], | |
299 | end: | |
300 | Math.abs(y - rookPos) <= 2 | |
301 | ? { x: x, y: rookPos } | |
302 | : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } | |
303 | }) | |
304 | ); | |
305 | } | |
306 | ||
307 | return moves; | |
308 | } | |
309 | ||
6808d7a1 BA |
310 | canIplay(side, [x, y]) { |
311 | return side == this.turn && [side, "c"].includes(this.getColor(x, y)); | |
dac39588 BA |
312 | } |
313 | ||
314 | // Does m2 un-do m1 ? (to disallow undoing checkered moves) | |
6808d7a1 BA |
315 | oppositeMoves(m1, m2) { |
316 | return ( | |
241bf8f2 | 317 | m1 && |
6808d7a1 BA |
318 | m2.appear[0].c == "c" && |
319 | m2.appear.length == 1 && | |
320 | m2.vanish.length == 1 && | |
321 | m1.start.x == m2.end.x && | |
322 | m1.end.x == m2.start.x && | |
323 | m1.start.y == m2.end.y && | |
324 | m1.end.y == m2.start.y | |
325 | ); | |
dac39588 BA |
326 | } |
327 | ||
6808d7a1 BA |
328 | filterValid(moves) { |
329 | if (moves.length == 0) return []; | |
dac39588 | 330 | const color = this.turn; |
241bf8f2 | 331 | const L = this.cmoves.length; //at least 1: init from FEN |
dac39588 | 332 | return moves.filter(m => { |
6808d7a1 | 333 | if (this.oppositeMoves(this.cmoves[L - 1], m)) return false; |
dac39588 BA |
334 | this.play(m); |
335 | const res = !this.underCheck(color); | |
336 | this.undo(m); | |
337 | return res; | |
338 | }); | |
339 | } | |
340 | ||
d1be8046 BA |
341 | getAllValidMoves() { |
342 | const oppCol = V.GetOppCol(this.turn); | |
343 | let potentialMoves = []; | |
344 | for (let i = 0; i < V.size.x; i++) { | |
345 | for (let j = 0; j < V.size.y; j++) { | |
346 | // NOTE: just testing == color isn't enough because of checkred pieces | |
347 | if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) { | |
348 | Array.prototype.push.apply( | |
349 | potentialMoves, | |
350 | this.getPotentialMovesFrom([i, j]) | |
351 | ); | |
352 | } | |
353 | } | |
354 | } | |
355 | return this.filterValid(potentialMoves); | |
356 | } | |
357 | ||
358 | atLeastOneMove() { | |
359 | const oppCol = V.GetOppCol(this.turn); | |
360 | for (let i = 0; i < V.size.x; i++) { | |
361 | for (let j = 0; j < V.size.y; j++) { | |
b83a675a | 362 | // NOTE: just testing == color isn't enough because of checkered pieces |
d1be8046 BA |
363 | if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) { |
364 | const moves = this.getPotentialMovesFrom([i, j]); | |
365 | if (moves.length > 0) { | |
366 | for (let k = 0; k < moves.length; k++) { | |
367 | if (this.filterValid([moves[k]]).length > 0) return true; | |
368 | } | |
369 | } | |
370 | } | |
371 | } | |
372 | } | |
373 | return false; | |
374 | } | |
375 | ||
68e19a44 BA |
376 | // colors: array, generally 'w' and 'c' or 'b' and 'c' |
377 | isAttacked(sq, colors) { | |
378 | return ( | |
379 | this.isAttackedByPawn(sq, colors) || | |
380 | this.isAttackedByRook(sq, colors) || | |
381 | this.isAttackedByKnight(sq, colors) || | |
382 | this.isAttackedByBishop(sq, colors) || | |
383 | this.isAttackedByQueen(sq, colors) || | |
384 | this.isAttackedByKing(sq, colors) | |
385 | ); | |
386 | } | |
387 | ||
6808d7a1 BA |
388 | isAttackedByPawn([x, y], colors) { |
389 | for (let c of colors) { | |
68e19a44 | 390 | const color = (c == "c" ? this.turn : c); |
6808d7a1 BA |
391 | let pawnShift = color == "w" ? 1 : -1; |
392 | if (x + pawnShift >= 0 && x + pawnShift < 8) { | |
393 | for (let i of [-1, 1]) { | |
394 | if ( | |
395 | y + i >= 0 && | |
396 | y + i < 8 && | |
397 | this.getPiece(x + pawnShift, y + i) == V.PAWN && | |
398 | this.getColor(x + pawnShift, y + i) == c | |
399 | ) { | |
dac39588 BA |
400 | return true; |
401 | } | |
402 | } | |
403 | } | |
404 | } | |
405 | return false; | |
406 | } | |
407 | ||
68e19a44 BA |
408 | isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) { |
409 | for (let step of steps) { | |
410 | let rx = x + step[0], | |
411 | ry = y + step[1]; | |
412 | while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) { | |
413 | rx += step[0]; | |
414 | ry += step[1]; | |
415 | } | |
416 | if ( | |
417 | V.OnBoard(rx, ry) && | |
418 | this.getPiece(rx, ry) === piece && | |
419 | colors.includes(this.getColor(rx, ry)) | |
420 | ) { | |
421 | return true; | |
422 | } | |
423 | } | |
424 | return false; | |
425 | } | |
426 | ||
427 | isAttackedByRook(sq, colors) { | |
428 | return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]); | |
429 | } | |
430 | ||
431 | isAttackedByKnight(sq, colors) { | |
432 | return this.isAttackedBySlideNJump( | |
433 | sq, | |
434 | colors, | |
435 | V.KNIGHT, | |
436 | V.steps[V.KNIGHT], | |
437 | "oneStep" | |
438 | ); | |
439 | } | |
440 | ||
441 | isAttackedByBishop(sq, colors) { | |
442 | return this.isAttackedBySlideNJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]); | |
443 | } | |
444 | ||
445 | isAttackedByQueen(sq, colors) { | |
446 | return this.isAttackedBySlideNJump( | |
447 | sq, | |
448 | colors, | |
449 | V.QUEEN, | |
450 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
451 | ); | |
452 | } | |
453 | ||
454 | isAttackedByKing(sq, colors) { | |
455 | return this.isAttackedBySlideNJump( | |
456 | sq, | |
457 | colors, | |
458 | V.KING, | |
459 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), | |
460 | "oneStep" | |
461 | ); | |
462 | } | |
463 | ||
6808d7a1 BA |
464 | underCheck(color) { |
465 | return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]); | |
dac39588 BA |
466 | } |
467 | ||
6808d7a1 | 468 | getCheckSquares(color) { |
dac39588 BA |
469 | // Artifically change turn, for checkered pawns |
470 | this.turn = V.GetOppCol(color); | |
6808d7a1 BA |
471 | const kingAttacked = this.isAttacked(this.kingPos[color], [ |
472 | V.GetOppCol(color), | |
473 | "c" | |
474 | ]); | |
dac39588 BA |
475 | let res = kingAttacked |
476 | ? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate! | |
477 | : []; | |
478 | this.turn = color; | |
479 | return res; | |
480 | } | |
481 | ||
3a2a7b5f BA |
482 | postPlay(move) { |
483 | super.postPlay(move); | |
dac39588 | 484 | // Does this move turn off a 2-squares pawn flag? |
3a2a7b5f | 485 | if ([1, 6].includes(move.start.x) && move.vanish[0].p == V.PAWN) |
6808d7a1 | 486 | this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false; |
3a2a7b5f BA |
487 | this.cmoves.push(this.getCmove(move)); |
488 | } | |
489 | ||
490 | postUndo(move) { | |
491 | super.postUndo(move); | |
492 | this.cmoves.pop(); | |
dac39588 BA |
493 | } |
494 | ||
6808d7a1 BA |
495 | getCurrentScore() { |
496 | if (this.atLeastOneMove()) | |
497 | // game not over | |
0c3fe8a6 BA |
498 | return "*"; |
499 | ||
500 | const color = this.turn; | |
dac39588 BA |
501 | // Artifically change turn, for checkered pawns |
502 | this.turn = V.GetOppCol(this.turn); | |
6808d7a1 BA |
503 | const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]) |
504 | ? color == "w" | |
505 | ? "0-1" | |
506 | : "1-0" | |
dac39588 BA |
507 | : "1/2"; |
508 | this.turn = V.GetOppCol(this.turn); | |
509 | return res; | |
510 | } | |
511 | ||
6808d7a1 | 512 | evalPosition() { |
dac39588 | 513 | let evaluation = 0; |
d1be8046 | 514 | // Just count material for now, considering checkered neutral (...) |
6808d7a1 BA |
515 | for (let i = 0; i < V.size.x; i++) { |
516 | for (let j = 0; j < V.size.y; j++) { | |
517 | if (this.board[i][j] != V.EMPTY) { | |
518 | const sqColor = this.getColor(i, j); | |
d1be8046 BA |
519 | if (["w","b"].includes(sqColor)) { |
520 | const sign = sqColor == "w" ? 1 : -1; | |
521 | evaluation += sign * V.VALUES[this.getPiece(i, j)]; | |
522 | } | |
dac39588 BA |
523 | } |
524 | } | |
525 | } | |
526 | return evaluation; | |
527 | } | |
528 | ||
7ba4a5bc | 529 | static GenRandInitFen(randomness) { |
3a2a7b5f | 530 | // Add 16 pawns flags + empty cmove: |
7ba4a5bc | 531 | return ChessRules.GenRandInitFen(randomness) |
3a2a7b5f | 532 | .slice(0, -2) + "1111111111111111 - -"; |
dac39588 | 533 | } |
1d184b4c | 534 | |
6808d7a1 BA |
535 | static ParseFen(fen) { |
536 | return Object.assign({}, ChessRules.ParseFen(fen), { | |
537 | cmove: fen.split(" ")[5] | |
538 | }); | |
03cff0f7 BA |
539 | } |
540 | ||
6808d7a1 | 541 | getFen() { |
03cff0f7 | 542 | const L = this.cmoves.length; |
6808d7a1 | 543 | const cmoveFen = !this.cmoves[L - 1] |
03cff0f7 | 544 | ? "-" |
6808d7a1 BA |
545 | : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) + |
546 | ChessRules.CoordsToSquare(this.cmoves[L - 1].end); | |
03cff0f7 BA |
547 | return super.getFen() + " " + cmoveFen; |
548 | } | |
549 | ||
6808d7a1 | 550 | getFlagsFen() { |
dac39588 BA |
551 | let fen = super.getFlagsFen(); |
552 | // Add pawns flags | |
6808d7a1 BA |
553 | for (let c of ["w", "b"]) { |
554 | for (let i = 0; i < 8; i++) fen += this.pawnFlags[c][i] ? "1" : "0"; | |
dac39588 BA |
555 | } |
556 | return fen; | |
557 | } | |
1d184b4c | 558 | |
b83a675a BA |
559 | static get SEARCH_DEPTH() { |
560 | return 2; | |
561 | } | |
562 | ||
6808d7a1 BA |
563 | getNotation(move) { |
564 | if (move.appear.length == 2) { | |
dac39588 | 565 | // Castle |
6808d7a1 BA |
566 | if (move.end.y < move.start.y) return "0-0-0"; |
567 | return "0-0"; | |
dac39588 BA |
568 | } |
569 | ||
570 | // Translate final square | |
571 | const finalSquare = V.CoordsToSquare(move.end); | |
572 | ||
573 | const piece = this.getPiece(move.start.x, move.start.y); | |
6808d7a1 | 574 | if (piece == V.PAWN) { |
dac39588 BA |
575 | // Pawn move |
576 | let notation = ""; | |
6808d7a1 | 577 | if (move.vanish.length > 1) { |
dac39588 BA |
578 | // Capture |
579 | const startColumn = V.CoordToColumn(move.start.y); | |
6808d7a1 BA |
580 | notation = |
581 | startColumn + | |
582 | "x" + | |
583 | finalSquare + | |
584 | "=" + | |
585 | move.appear[0].p.toUpperCase(); | |
586 | } //no capture | |
587 | else { | |
dac39588 | 588 | notation = finalSquare; |
6808d7a1 BA |
589 | if (move.appear.length > 0 && piece != move.appear[0].p) |
590 | //promotion | |
dac39588 BA |
591 | notation += "=" + move.appear[0].p.toUpperCase(); |
592 | } | |
593 | return notation; | |
594 | } | |
6808d7a1 BA |
595 | // Piece movement |
596 | return ( | |
597 | piece.toUpperCase() + | |
598 | (move.vanish.length > 1 ? "x" : "") + | |
599 | finalSquare + | |
600 | (move.vanish.length > 1 ? "=" + move.appear[0].p.toUpperCase() : "") | |
601 | ); | |
dac39588 | 602 | } |
6808d7a1 | 603 | }; |