generateMoves method Null safety

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

Implementation

List<Move> generateMoves([Map? options]) {
  addMove(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)) {
      List pieces = [QUEEN, ROOK, BISHOP, KNIGHT];
      for (var i = 0, len = pieces.length; i < len; i++) {
        moves.add(buildMove(board, from, to, flags, pieces[i]));
      }
    } else {
      moves.add(buildMove(board, from, to, flags));
    }
  }

  List<Move> moves = [];
  Color us = turn;
  Color them = swapColor(us);
  ColorMap<int> secondRank = ColorMap(0);
  secondRank[BLACK] = RANK_7;
  secondRank[WHITE] = RANK_2;

  var firstSq = SQUARES_A8;
  var lastSq = SQUARES_H1;
  bool singleSquare = false;

  /* do we want legal moves? */
  var 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'])) {
      firstSq = lastSq = SQUARES[options['square']];
      singleSquare = true;
    } else {
      /* invalid square */
      return [];
    }
  }

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

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

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

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

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

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

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

          if (board[square] == null) {
            addMove(board, moves, i, square, BITS_NORMAL);
          } else {
            if (board[square]!.color == us) {
              break;
            }
            addMove(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 ((!singleSquare) || lastSq == kings[us]) {
    /* king-side castling */
    if ((castling[us] & BITS_KSIDE_CASTLE) != 0) {
      var castlingFrom = kings[us];
      var castlingTo = castlingFrom + 2;

      if (board[castlingFrom + 1] == null &&
          board[castlingTo] == null &&
          !attacked(them, kings[us]) &&
          !attacked(them, castlingFrom + 1) &&
          !attacked(them, castlingTo)) {
        addMove(board, moves, kings[us], castlingTo, BITS_KSIDE_CASTLE);
      }
    }

    /* queen-side castling */
    if ((castling[us] & BITS_QSIDE_CASTLE) != 0) {
      var castlingFrom = kings[us];
      var castlingTo = castlingFrom - 2;

      if (board[castlingFrom - 1] == null &&
          board[castlingFrom - 2] == null &&
          board[castlingFrom - 3] == null &&
          !attacked(them, kings[us]) &&
          !attacked(them, castlingFrom - 1) &&
          !attacked(them, castlingTo)) {
        addMove(board, moves, kings[us], castlingTo, 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 */
  List<Move> legalMoves = [];
  for (int i = 0, len = moves.length; i < len; i++) {
    makeMove(moves[i]);
    if (!kingAttacked(us)) {
      legalMoves.add(moves[i]);
    }
    undoMove();
  }

  return legalMoves;
}