/* * Generate all moves that evade check in the given position. This is purely * legal move generation; no pseudo-legal moves. */ int generate_evasions(const position_t* pos, move_t* moves) { assert(pos->is_check && pos->board[pos->check_square]); move_t* moves_head = moves; color_t side = pos->side_to_move, other_side = side^1; square_t king_sq = pos->pieces[side][0]; square_t check_sq = pos->check_square; piece_t king = create_piece(side, KING); piece_t checker = pos->board[check_sq]; // Generate king moves. // Don't let the king mask its possible destination squares in calls // to is_square_attacked. square_t from = king_sq, to = INVALID_SQUARE; ((position_t*)pos)->board[king_sq] = EMPTY; for (const direction_t* delta = piece_deltas[king]; *delta; ++delta) { to = from + *delta; piece_t capture = pos->board[to]; if (capture != EMPTY && !can_capture(king, capture)) continue; if (is_square_attacked((position_t*)pos,to,other_side)) continue; ((position_t*)pos)->board[king_sq] = king; moves = add_move(pos, create_move(from, to, king, capture), moves); ((position_t*)pos)->board[king_sq] = EMPTY; } ((position_t*)pos)->board[king_sq] = king; // If there are multiple checkers, only king moves are possible. if (pos->is_check > 1) { *moves = 0; return moves-moves_head; } // First, the most common case: a check that can be evaded via an // en passant capture. Note that if we're in check and an en passant // capture is available, the only way the en passant capture would evade // the check is if it's the newly moved pawn that's checking us. direction_t pin_dir; if (pos->ep_square != EMPTY && check_sq+pawn_push[side] == pos->ep_square && pos->board[pos->ep_square] == EMPTY) { piece_t our_pawn = create_piece(side, PAWN); to = pos->ep_square; for (int i=0; i<2; ++i) { from = to-piece_deltas[our_pawn][i]; if (pos->board[from] && pos->board[from] == our_pawn) { pin_dir = pin_direction(pos, from, king_sq); if (pin_dir) continue; moves = add_move(pos, create_move_enpassant(from, to, our_pawn, checker), moves); } } } // Generate captures of the checker. for (int i = 0; i < pos->num_pawns[side]; ++i) { from = pos->pawns[side][i]; piece_t piece = create_piece(side, PAWN); pin_dir = pin_direction(pos, from, king_sq); if (pin_dir) continue; if (!possible_attack(from, check_sq, piece)) continue; if (relative_rank[side][square_rank(from)] == RANK_7) { // Capture and promote. for (piece_t promoted=QUEEN; promoted > PAWN; --promoted) { moves = add_move(pos, create_move_promote(from, check_sq, piece, checker, promoted), moves); } } else { moves = add_move(pos, create_move(from, check_sq, piece, checker), moves); } } for (int i = 1; i < pos->num_pieces[side]; ++i) { from = pos->pieces[side][i]; piece_t piece = pos->board[from]; pin_dir = pin_direction(pos, from, king_sq); if (pin_dir) continue; if (!possible_attack(from, check_sq, piece)) continue; if (piece_slide_type(piece) == NONE) { moves = add_move(pos, create_move(from, check_sq, piece, checker), moves); } else { // A sliding piece, keep going until we hit something. direction_t check_dir = direction(from, check_sq); for (to=from+check_dir; pos->board[to] == EMPTY; to+=check_dir) {} if (to == check_sq) { moves = add_move(pos, create_move(from, to, piece, checker), moves); continue; } } } if (piece_slide_type(checker) == NONE) { *moves = 0; return moves-moves_head; } // A slider is doing the checking; generate blocking moves. direction_t block_dir = direction(check_sq, king_sq); for (int i = 0; i < pos->num_pawns[side]; ++i) { from = pos->pawns[side][i]; piece_t piece = create_piece(side, PAWN); direction_t k_dir; pin_dir = pin_direction(pos, from, king_sq); if (pin_dir) continue; to = from + pawn_push[side]; if (pos->board[to] != EMPTY) continue; rank_t rank = relative_rank[side][square_rank(from)]; k_dir = direction(to, king_sq); if (k_dir == block_dir && ((king_sq < to && check_sq > to) || (king_sq > to && check_sq < to))) { if (rank == RANK_7) { // Block and promote. for (piece_t promoted=QUEEN; promoted > PAWN; --promoted) { moves = add_move(pos, create_move_promote(from, to, piece, EMPTY, promoted), moves); } } else { moves = add_move(pos, create_move(from, to, piece, EMPTY), moves); } } if (rank != RANK_2) continue; to += pawn_push[side]; if (pos->board[to]) continue; k_dir = direction(to, king_sq); if (k_dir == block_dir && ((king_sq < to && check_sq > to) || (king_sq > to && check_sq < to))) { moves = add_move(pos, create_move(from, to, piece, EMPTY), moves); } } for (int i=1; i<pos->num_pieces[side]; ++i) { from = pos->pieces[side][i]; piece_t piece = pos->board[from]; direction_t k_dir; pin_dir = pin_direction(pos, from, king_sq); if (pin_dir) continue; if (piece_is_type(piece, KNIGHT)) { for (const direction_t* delta=piece_deltas[piece]; *delta; ++delta) { to = from + *delta; if (pos->board[to] != EMPTY) continue; k_dir = direction(to, king_sq); if (k_dir == block_dir && ((king_sq < to && check_sq > to) || (king_sq > to && check_sq < to))) { moves = add_move(pos, create_move(from, to, piece, EMPTY), moves); } } } else { for (const direction_t* delta=piece_deltas[piece]; *delta; ++delta) { for (to = from+*delta; pos->board[to] == EMPTY; to+=*delta) { k_dir = direction(to, king_sq); if (k_dir == block_dir && ((king_sq < to && check_sq > to) || (king_sq > to && check_sq < to))) { moves = add_move(pos, create_move(from, to, piece, EMPTY), moves); break; } } } } } *moves = 0; return moves-moves_head; }
BOOL test_genmove() { BOOL ok = TRUE; struct t_move_list moves[1]; set_fen(position, "r5r1/n1q1pP1k/3pPppp/P1pP4/2P4N/R1B5/2Q3PP/7K w - -"); assert(integrity(position)); assert(is_square_attacked(position, E4, WHITE)); assert(!is_square_attacked(position, A7, WHITE)); assert(!is_square_attacked(position, F4, BLACK)); assert(is_square_attacked(position, D8, BLACK)); generate_moves(position, moves); ok = ok && (moves->count == 42); flip_board(position); generate_moves(position, moves); //write_move_list(moves, "movelist.txt"); ok = ok && (moves->count == 42); set_fen(position, "1r2k2r/p1ppqpb1/b3pnp1/3PN3/1pn1P3/2N2Q1p/PPPBBPPP/R4K1R w - -"); assert(integrity(position)); generate_moves(position, moves); assert(move_list_integrity(position, moves)); ok = ok && (moves->count == 44); set_fen(position, "4q3/3P1P2/b4N2/8/3Q2Bb/2p3B1/1k4N1/4K1Nr w - -"); assert(integrity(position)); generate_evade_check(position, moves); ok = ok && (moves->count == 18); flip_board(position); generate_evade_check(position, moves); ok = ok && (moves->count == 18); set_fen(position, "1r2k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R4K1R w --k- -"); assert(integrity(position)); generate_moves(position, moves); //write_move_list(moves, "movelist.txt"); assert(move_list_integrity(position, moves)); ok = ok && (moves->count == 46); flip_board(position); generate_moves(position, moves); ok = ok && (moves->count == 46); // Chess960 Examples set_fen(position, "Rr4kr/8/8/8/8/8/PPPP4/R1K5 w Ahb -"); assert(integrity(position)); generate_moves(position, moves); assert(move_list_integrity(position, moves)); ok = ok && (moves->count == 18); flip_board(position); generate_moves(position, moves); ok = ok && (moves->count == 18); set_fen(position, "1r1kbb1r/1pp2ppp/3npn2/3pN3/1Q3P2/4PN2/2PP2PP/qR1KBB1R w HBhb -"); assert(integrity(position)); generate_legal_moves(position, moves); assert(move_list_integrity(position, moves)); ok = ok && (moves->count == 48); set_fen(position, "rkrbqnb1/pp2p2p/3p1pp1/2p1nP2/2P1P3/3P2N1/PP4PP/RKRBQNB1 w CAca -"); assert(integrity(position)); generate_legal_moves(position, moves); write_move_list(moves, "movelist.txt"); assert(move_list_integrity(position, moves)); ok = ok && (moves->count == 34); flip_board(position); generate_legal_moves(position, moves); ok = ok && (moves->count == 34); return ok; }
/* * Generate pseudo-legal moves which are neither captures nor promotions. */ int generate_pseudo_quiet_moves(const position_t* pos, move_t* moves) { move_t* moves_head = moves; color_t side = pos->side_to_move; piece_t piece; square_t from; // Castling. Castles are considered pseudo-legal if we have appropriate // castling rights, the squares between king and rook are unoccupied, // and the intermediate square is unattacked. Therefore checking for // legality just requires seeing if we're in check afterwards. // This is messy for Chess960, so it's separated into separate cases. square_t my_king_home = king_home + side*A8; if (!options.chess960) { if (has_oo_rights(pos, side) && pos->board[my_king_home+1] == EMPTY && pos->board[my_king_home+2] == EMPTY && !is_square_attacked((position_t*)pos,my_king_home+1,side^1)) { assert(pos->board[my_king_home] == create_piece(pos->side_to_move, KING)); moves = add_move(pos, create_move_castle(my_king_home, my_king_home+2, create_piece(side, KING)), moves); } if (has_ooo_rights(pos, side) && pos->board[my_king_home-1] == EMPTY && pos->board[my_king_home-2] == EMPTY && pos->board[my_king_home-3] == EMPTY && !is_square_attacked((position_t*)pos,my_king_home-1,side^1)) { assert(pos->board[my_king_home] == create_piece(pos->side_to_move, KING)); moves = add_move(pos, create_move_castle(my_king_home, my_king_home-2, create_piece(side, KING)), moves); } } else { if (has_oo_rights(pos, side)) { square_t my_f1 = F1 + side*A8; square_t my_g1 = G1 + side*A8; square_t my_kr = king_rook_home + side*A8; bool castle_ok = true; // Check that rook is unimpeded. for (square_t sq = MIN(my_kr, my_f1); sq <= MAX(my_kr, my_f1); ++sq) { if (sq != my_kr && sq != my_king_home && pos->board[sq] != EMPTY) { castle_ok = false; break; } } // Check that the king is unimpeded and unattacked if (castle_ok) { for (square_t sq = MIN(my_king_home, my_g1); sq <= my_g1; ++sq) { if (sq != my_king_home && sq != my_kr && pos->board[sq] != EMPTY) { castle_ok = false; break; } if (sq != my_g1 && is_square_attacked((position_t*)pos, sq, side^1)) { castle_ok = false; break; } } } if (castle_ok) moves = add_move(pos, create_move_castle(my_king_home, my_g1, create_piece(side, KING)), moves); } if (has_ooo_rights(pos, side)) { square_t my_d1 = D1 + side*A8; square_t my_c1 = C1 + side*A8; square_t my_qr = queen_rook_home + side*A8; bool castle_ok = true; // Check that rook is unimpeded. for (square_t sq = MIN(my_qr, my_d1); sq <= MAX(my_qr, my_d1); ++sq) { if (sq != my_qr && sq != my_king_home && pos->board[sq] != EMPTY) { castle_ok = false; break; } } // Check that the king is unimpeded and unattacked if (castle_ok) { for (square_t sq = MIN(my_king_home, my_c1); sq <= MAX(my_king_home, my_c1); ++sq) { if (sq != my_king_home && sq != my_qr && pos->board[sq] != EMPTY) { castle_ok = false; break; } if (sq != my_c1 && is_square_attacked((position_t*)pos, sq, side^1)) { castle_ok = false; break; } } } if (castle_ok) moves = add_move(pos, create_move_castle(my_king_home, my_c1, create_piece(side, KING)), moves); } } for (int i = 0; i < pos->num_pieces[side]; ++i) { from = pos->pieces[side][i]; piece = pos->board[from]; assert(piece_color(piece) == side && piece_type(piece) != PAWN); generate_piece_noncaptures(pos, from, piece, &moves); } piece = create_piece(side, PAWN); for (int i=0; i<pos->num_pawns[side]; ++i) { from = pos->pawns[side][i]; assert(pos->board[from] == piece); generate_pawn_quiet_moves(pos, from, piece, &moves); } *moves = 0; return (moves-moves_head); }