k: [-17, -16, -15, 1, 17, 16, 15, -1]
};
- var ATTACKS = [
- 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20, 0,
- 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0,
- 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0,
- 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0,
- 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
- 24,24,24,24,24,24,56, 0, 56,24,24,24,24,24,24, 0,
- 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0,
- 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0,
- 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0,
- 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0,
- 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20
- ];
-
- var RAYS = [
- 17, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 15, 0,
- 0, 17, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 15, 0, 0,
- 0, 0, 17, 0, 0, 0, 0, 16, 0, 0, 0, 0, 15, 0, 0, 0,
- 0, 0, 0, 17, 0, 0, 0, 16, 0, 0, 0, 15, 0, 0, 0, 0,
- 0, 0, 0, 0, 17, 0, 0, 16, 0, 0, 15, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 17, 0, 16, 0, 15, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 17, 16, 15, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1,-1, -1, -1, -1, 0,
- 0, 0, 0, 0, 0, 0,-15,-16,-17, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,-15, 0,-16, 0,-17, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0,-15, 0, 0,-16, 0, 0,-17, 0, 0, 0, 0, 0,
- 0, 0, 0,-15, 0, 0, 0,-16, 0, 0, 0,-17, 0, 0, 0, 0,
- 0, 0,-15, 0, 0, 0, 0,-16, 0, 0, 0, 0,-17, 0, 0, 0,
- 0,-15, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0,-17, 0, 0,
- -15, 0, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0, 0,-17
- ];
-
- var SHIFTS = { p: 0, n: 1, b: 2, r: 3, q: 4, k: 5 };
-
var FLAGS = {
NORMAL: 'n',
CAPTURE: 'c',
var i, flag;
if (white_frc_columns !== null) {
for (i = 0; i < white_frc_columns.length; ++i) {
- var sq = SQUARES.a1 + (white_frc_columns[0].charCodeAt(0) - "A".charCodeAt(0));
+ var sq = SQUARES.a1 + (white_frc_columns[i].charCodeAt(0) - "A".charCodeAt(0));
flag = sq < kings[WHITE] ? BITS.QSIDE_CASTLE : BITS.KSIDE_CASTLE;
castling.w |= flag;
rooks[WHITE].push({square: sq, flag: flag});
var black_frc_columns = tokens[2].match(/[a-h]/g);
if (black_frc_columns !== null) {
for (i = 0; i < black_frc_columns.length; ++i) {
- var sq = SQUARES.a1 + (black_frc_columns[0].charCodeAt(0) - "A".charCodeAt(0));
+ var sq = SQUARES.a8 + (black_frc_columns[i].charCodeAt(0) - "a".charCodeAt(0));
flag = sq < kings[BLACK] ? BITS.QSIDE_CASTLE : BITS.KSIDE_CASTLE;
castling.b |= flag;
rooks[BLACK].push({square: sq, flag: flag});
}
}
+ return possibly_filter_moves(moves, us, legal);
+ }
+
+ function possibly_filter_moves(moves, us, legal) {
/* return all pseudo-legal moves (this includes moves that allow the king
* to be captured)
*/
}
function attacked(color, square) {
- for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
- /* did we run off the end of the board */
- if (i & 0x88) { i += 7; continue; }
+ // Check for attacks by the king.
+ if (Math.abs(rank(kings[color]) - rank(square)) <= 1 &&
+ Math.abs(file(kings[color]) - file(square)) <= 1) {
+ return true;
+ }
- /* if empty square or wrong color */
- if (board[i] == null || board[i].color !== color) continue;
+ // Check for attacks by knights.
+ for (const offset of PIECE_OFFSETS[KNIGHT]) {
+ let knight_sq = square + offset;
+ if (knight_sq & 0x88) continue;
- var piece = board[i];
- var difference = i - square;
- var index = difference + 119;
+ if (board[knight_sq] != null &&
+ board[knight_sq].type === KNIGHT &&
+ board[knight_sq].color === color) {
+ return true;
+ }
+ }
- if (ATTACKS[index] & (1 << SHIFTS[piece.type])) {
- if (piece.type === PAWN) {
- if (difference > 0) {
- if (piece.color === WHITE) return true;
- } else {
- if (piece.color === BLACK) return true;
+ // Check for attacks by pawns.
+ const p1sq = square - PAWN_OFFSETS[color][2];
+ const p2sq = square - PAWN_OFFSETS[color][3];
+ if (!(p1sq & 0x88) &&
+ board[p1sq] != null &&
+ board[p1sq].type === PAWN &&
+ board[p1sq].color === color) {
+ return true;
+ }
+ if (!(p2sq & 0x88) &&
+ board[p2sq] != null &&
+ board[p2sq].type === PAWN &&
+ board[p2sq].color === color) {
+ return true;
+ }
+
+ // Check for attacks by rooks (where queens count as rooks).
+ for (const offset of PIECE_OFFSETS[ROOK]) {
+ let rook_sq = square;
+ while (true) {
+ rook_sq += offset;
+ if (rook_sq & 0x88) break;
+
+ if (board[rook_sq] != null) {
+ if ((board[rook_sq].type === ROOK || board[rook_sq].type === QUEEN) &&
+ board[rook_sq].color === color) {
+ return true;
}
- continue;
+ break;
}
+ }
+ }
- /* if the piece is a knight or a king */
- if (piece.type === 'n' || piece.type === 'k') return true;
-
- var offset = RAYS[index];
- var j = i + offset;
+ // And similarly for attacks by bishops (where queens count as bishops).
+ for (const offset of PIECE_OFFSETS[BISHOP]) {
+ let bishop_sq = square;
+ while (true) {
+ bishop_sq += offset;
+ if (bishop_sq & 0x88) break;
- var blocked = false;
- while (j !== square) {
- if (board[j] != null) { blocked = true; break; }
- j += offset;
+ if (board[bishop_sq] != null) {
+ if ((board[bishop_sq].type === BISHOP || board[bishop_sq].type === QUEEN) &&
+ board[bishop_sq].color === color) {
+ return true;
+ }
+ break;
}
-
- if (!blocked) return true;
}
}
if (move.flags & BITS.KSIDE_CASTLE) {
var castling_to = move.to - 1;
var castling_from = move.rook_sq;
- board[castling_to] = board[castling_from];
- if(castling_from !== move.to)
+ board[castling_to] = {type: ROOK, color: us};
+ if(castling_from !== move.to && castling_from !== castling_to)
board[castling_from] = null;
} else if (move.flags & BITS.QSIDE_CASTLE) {
var castling_to = move.to + 1;
var castling_from = move.rook_sq;
- board[castling_to] = board[castling_from];
- if(castling_from !== move.to)
+ board[castling_to] = {type: ROOK, color: us};
+ if(castling_from !== move.to && castling_from !== castling_to)
board[castling_from] = null;
}
castling_from = move.to + 1;
}
- board[castling_to] = board[castling_from];
- if(castling_from !== move.from)
+ board[castling_to] = {type: ROOK, color: us};
+ if(castling_from !== move.from && castling_from !== castling_to)
board[castling_from] = null;
}
/* this function is used to uniquely identify ambiguous moves */
function get_disambiguator(move, sloppy) {
- var moves = generate_moves({legal: !sloppy});
-
var from = move.from;
var to = move.to;
var piece = move.piece;
+ if (piece === 'p' || piece === 'k') {
+ // Pawn or king moves are never ambiguous.
+ return '';
+ }
+
+ let moves = find_attacking_moves(move.to, piece, move.color, sloppy);
+
var ambiguities = 0;
var same_rank = 0;
var same_file = 0;
return '';
}
+ // Find all moves featuring the given piece attacking the given square
+ // (using symmetry of all non-pawn-or-castle moves, we simply generate
+ // moves backwards). Does not support kings or pawns. Assumes there's
+ // not already a piece of our own color on the destination square.
+ function find_attacking_moves(to, piece, us, sloppy) {
+ let moves = [];
+
+ function add_move(board, moves, from, to, flags, rook_sq) {
+ moves.push(build_move(board, from, to, flags, undefined, rook_sq));
+ }
+ for (let offset of PIECE_OFFSETS[piece]) {
+ var square = to;
+
+ while (true) {
+ square += offset;
+ if (square & 0x88) break;
+
+ if (board[square] != null) {
+ if (board[square].color !== us) break;
+ if (board[to] == null) {
+ add_move(board, moves, square, to, BITS.NORMAL);
+ } else {
+ add_move(board, moves, square, to, BITS.CAPTURE);
+ }
+ break;
+ }
+
+ /* break if knight */
+ if (piece === 'n') break;
+ }
+ }
+
+ return possibly_filter_moves(moves, us, !sloppy);
+ }
+
function ascii() {
var s = ' +------------------------+\n';
for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {