generate_moves method

List<Move> generate_moves([
  1. Map? options
])

Generates moves for the current position. Can be all moves, legal moves only (=> 'legal' option set to true).

Implementation

List<Move> generate_moves([Map? options]) {
  void add_move(List<Piece?> board, List<Move> moves, from, to, flags) {
    /* if pawn promotion */
    if (board[from]!.type == PAWN &&
        (rank(to) == RANK_8 || rank(to) == RANK_1)) {
      const pieces = [QUEEN, ROOK, BISHOP, KNIGHT];
      for (var i = 0, len = pieces.length; i < len; i++) {
        moves.add(build_move(board, from, to, flags, pieces[i]));
      }
    } else {
      moves.add(build_move(board, from, to, flags));
    }
  }

  final moves = <Move>[];
  final us = turn;
  final them = swap_color(us);
  final second_rank = ColorMap<int>(0);
  second_rank[BLACK] = RANK_7;
  second_rank[WHITE] = RANK_2;

  var first_sq = SQUARES_A8;
  var last_sq = SQUARES_H1;
  var single_square = false;

  /* do we want legal moves? */
  final legal = (options != null && options.containsKey('legal'))
      ? options['legal']
      : true;

  /* are we generating moves for a single square? */
  if (options != null && options.containsKey('square')) {
    if (SQUARES.containsKey(options['square'])) {
      first_sq = last_sq = SQUARES[options['square']];
      single_square = true;
    } else {
      /* invalid square */
      return [];
    }
  }

  for (var i = first_sq; i <= last_sq; i++) {
    /* did we run off the end of the board */
    if ((i & 0x88) != 0) {
      i += 7;
      continue;
    }

    final piece = board[i];
    if (piece == null || piece.color != us) {
      continue;
    }

    if (piece.type == PAWN) {
      /* single square, non-capturing */
      final square = i + PAWN_OFFSETS[us]![0];
      if (board[square] == null) {
        add_move(board, moves, i, square, BITS_NORMAL);

        /* double square */
        final square2 = i + PAWN_OFFSETS[us]![1];
        if (second_rank[us] == rank(i) && board[square2] == null) {
          add_move(board, moves, i, square2, BITS_BIG_PAWN);
        }
      }

      /* pawn captures */
      for (var j = 2; j < 4; j++) {
        var square = i + PAWN_OFFSETS[us]![j];
        if ((square & 0x88) != 0) continue;

        if (board[square] != null && board[square]!.color == them) {
          add_move(board, moves, i, square, BITS_CAPTURE);
        } else if (square == ep_square) {
          add_move(board, moves, i, ep_square, BITS_EP_CAPTURE);
        }
      }
    } else {
      for (var j = 0, len = PIECE_OFFSETS[piece.type]!.length; j < len; j++) {
        final offset = PIECE_OFFSETS[piece.type]![j];
        var square = i;

        while (true) {
          square += offset;
          if ((square & 0x88) != 0) break;

          if (board[square] == null) {
            add_move(board, moves, i, square, BITS_NORMAL);
          } else {
            if (board[square]!.color == us) {
              break;
            }
            add_move(board, moves, i, square, BITS_CAPTURE);
            break;
          }

          /* break, if knight or king */
          if (piece.type == KNIGHT || piece.type == KING) break;
        }
      }
    }
  }

  // check for castling if: a) we're generating all moves, or b) we're doing
  // single square move generation on the king's square
  if ((!single_square) || last_sq == kings[us]) {
    /* king-side castling */
    if ((castling[us] & BITS_KSIDE_CASTLE) != 0) {
      final castling_from = kings[us];
      final castling_to = castling_from + 2;

      if (board[castling_from + 1] == null &&
          board[castling_to] == null &&
          !attacked(them, kings[us]) &&
          !attacked(them, castling_from + 1) &&
          !attacked(them, castling_to)) {
        add_move(board, moves, kings[us], castling_to, BITS_KSIDE_CASTLE);
      }
    }

    /* queen-side castling */
    if ((castling[us] & BITS_QSIDE_CASTLE) != 0) {
      final castling_from = kings[us];
      final castling_to = castling_from - 2;

      if (board[castling_from - 1] == null &&
          board[castling_from - 2] == null &&
          board[castling_from - 3] == null &&
          !attacked(them, kings[us]) &&
          !attacked(them, castling_from - 1) &&
          !attacked(them, castling_to)) {
        add_move(board, moves, kings[us], castling_to, BITS_QSIDE_CASTLE);
      }
    }
  }

  /* return all pseudo-legal moves (this includes moves that allow the king
   * to be captured)
   */
  if (!legal) {
    return moves;
  }

  /* filter out illegal moves */
  final legal_moves = <Move>[];
  for (var i = 0, len = moves.length; i < len; i++) {
    make_move(moves[i]);
    if (!king_attacked(us)) {
      legal_moves.add(moves[i]);
    }
    undo_move();
  }

  return legal_moves;
}