generate_moves method
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;
}