X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=www%2Fjs%2Fchess.js;h=3c97b646e03c07565556ed54209c78b7dc99ee85;hb=d6ba2d130757a967d26638702e561feb48f0178e;hp=6d6623e4d90005e7b5616346ca920391a4963d17;hpb=01b20c4f22ef463dec8262498848d3f7236b34ef;p=remoteglot diff --git a/www/js/chess.js b/www/js/chess.js index 6d6623e..3c97b64 100644 --- a/www/js/chess.js +++ b/www/js/chess.js @@ -68,44 +68,6 @@ var Chess = function(fen) { 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', @@ -242,7 +204,7 @@ var Chess = function(fen) { 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}); @@ -270,7 +232,7 @@ var Chess = function(fen) { 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}); @@ -708,6 +670,10 @@ var Chess = function(fen) { } } + 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) */ @@ -820,40 +786,71 @@ var Chess = function(fen) { } 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; } } @@ -997,14 +994,14 @@ var Chess = function(fen) { 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; } @@ -1104,8 +1101,8 @@ var Chess = function(fen) { 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; } @@ -1114,12 +1111,24 @@ var Chess = function(fen) { /* 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); + if (moves.length <= 1) { + // There can be no ambiguity, so don't bother checking legality + // (we assume the move has already been found legal). + return ''; + } + + moves = possibly_filter_moves(moves, move.color, !sloppy); + var ambiguities = 0; var same_rank = 0; var same_file = 0; @@ -1167,6 +1176,42 @@ var Chess = function(fen) { return ''; } + // Find all pseudolegal moves featuring the given piece moving to + // the given square (using symmetry of all non-pawn-or-castle moves, + // we simply generate moves backwards). Does not support pawns. + // Assumes there's not already a piece of our own color + // on the destination square. + function find_attacking_moves(to, piece, us) { + 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 || board[square].type !== piece) 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 or king */ + if (piece === 'n' || piece === 'k') break; + } + } + + return moves; + } + function ascii() { var s = ' +------------------------+\n'; for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { @@ -1214,7 +1259,23 @@ var Chess = function(fen) { } } - var moves = generate_moves(); + let moves; + let piece_matches = clean_move.match(/^([NBRQK])x?([a-h][1-8])$/); + if (piece_matches) { + // Only look for moves by the given piece to the given square. + let to = SQUARES[piece_matches[2]]; + if (board[to] != null && board[to].color === turn) { + // Cannot capture our own piece. + return null; + } + moves = find_attacking_moves(to, piece_matches[1].toLowerCase(), turn); + // Legal moves only. + moves = possibly_filter_moves(moves, turn, true); + } else { + // Fallback (also used for pawns): Any (legal) moves. + moves = generate_moves(); + } + for (var i = 0, len = moves.length; i < len; i++) { // try the strict parser first, then the sloppy parser if requested // by the user