+ getCastleMoves([x, y]) {
+ const c = this.getColor(x, y);
+ const oppCol = V.GetOppCol(c);
+ let moves = [];
+ const finalSquares = [ [2, 3], [6, 5] ];
+ castlingCheck: for (let castleSide = 0; castleSide < 2; castleSide++) {
+ if (this.castleFlags[c][castleSide] >= 8) continue;
+ const rookPos = this.castleFlags[c][castleSide];
+
+ // Nothing on the path of the king ?
+ const finDist = finalSquares[castleSide][0] - y;
+ let step = finDist / Math.max(1, Math.abs(finDist));
+ let i = y;
+ let kingSquares = [y];
+ do {
+ if (
+ (
+ this.board[x][i] != V.EMPTY &&
+ (this.getColor(x, i) != c || ![y, rookPos].includes(i))
+ )
+ ) {
+ continue castlingCheck;
+ }
+ i += step;
+ kingSquares.push(i);
+ } while (i != finalSquares[castleSide][0]);
+ // No checks on the path of the king ?
+ if (this.isAttacked(kingSquares, oppCol)) continue castlingCheck;
+
+ // Nothing on the path to the rook?
+ step = castleSide == 0 ? -1 : 1;
+ for (i = y + step; i != rookPos; i += step) {
+ if (this.board[x][i] != V.EMPTY) continue castlingCheck;
+ }
+
+ // Nothing on final squares, except maybe king and castling rook?
+ for (i = 0; i < 2; i++) {
+ if (
+ finalSquares[castleSide][i] != rookPos &&
+ this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
+ (
+ finalSquares[castleSide][i] != y ||
+ this.getColor(x, finalSquares[castleSide][i]) != c
+ )
+ ) {
+ continue castlingCheck;
+ }
+ }
+
+ moves.push(
+ new Move({
+ appear: [
+ new PiPo({
+ x: x,
+ y: finalSquares[castleSide][0],
+ p: V.KING,
+ c: c
+ }),
+ new PiPo({
+ x: x,
+ y: finalSquares[castleSide][1],
+ p: V.ROOK,
+ c: c
+ })
+ ],
+ vanish: [
+ // King might be initially disguised (Titan...)
+ new PiPo({ x: x, y: y, p: V.KING, c: c }),
+ new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
+ ],
+ end:
+ Math.abs(y - rookPos) <= 2
+ ? { x: x, y: rookPos }
+ : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
+ })
+ );
+ }
+
+ return moves;
+ }
+
+ isAttacked_aux(files, color, positions, fromSquare, released) {
+ // "positions" = array of FENs to detect infinite loops. Example:
+ // r1q1k2r/p1Pb1ppp/5n2/1f1p4/AV5P/P1eDP3/3B1PP1/R3K1NR,
+ // Bxd2 Bxc3 Bxb4 Bxc3 Bxb4 etc.
+ const newPos = { fen: super.getBaseFen(), piece: released };
+ if (positions.some(p => p.piece == newPos.piece && p.fen == newPos.fen))
+ // Start of an infinite loop: exit
+ return false;
+ positions.push(newPos);
+ const rank = (color == 'w' ? 0 : 7);
+ const moves = this.getPotentialMovesFrom(fromSquare);
+ if (moves.some(m => m.end.x == rank && files.includes(m.end.y)))
+ // Found an attack!
+ return true;
+ for (let m of moves) {
+ if (!!m.released) {
+ // Turn won't change since !!m.released
+ this.play(m);
+ const res = this.isAttacked_aux(
+ files, color, positions, [m.end.x, m.end.y], m.released);
+ this.undo(m);
+ if (res) return true;
+ }
+ }