]> git.sesse.net Git - remoteglot/blobdiff - www/js/chess.js
Chess.js bugfixes.
[remoteglot] / www / js / chess.js
index f274628273e33c6d964f3274bf524046ed5e0e61..531a08fec291ee6f5fb430d7031ef7ff055c95f1 100644 (file)
@@ -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',
@@ -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;
       }
     }
 
@@ -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 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) {
+    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 */
+        if (piece === 'n') break;
+      }
+    }
+
+    return moves;
+  }
+
   function ascii() {
     var s = '   +------------------------+\n';
     for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {