int play(int me, int opponent, int row, int col, bool first_player, move_t **moves) { bool can_capture_right, can_capture_left; move_t *moves_right, *moves_left; int score_right = 0; int score_left = 0; if (me == BLACK) { D printf("BLACK: "); can_capture_left = can_capture(me,opponent,row,col,row+1,col+1,row+2,col+2); can_capture_right = can_capture(me,opponent,row,col,row+1,col-1,row+2,col-2); D printf(" From (%d,%d): Right? %s Left? %s\n",row,col,(can_capture_right ? "Yes" : "No"), (can_capture_left ? "Yes" : "No")); if (can_capture_left) score_left = 1 + test_capture(me,opponent,row,col,row+1,col+1,row+2,col+2,first_player,&moves_left); if (can_capture_right) score_right = 1 + test_capture(me,opponent,row,col,row+1,col-1,row+2,col-2,first_player,&moves_right); moves_left = move_gen(row+2,col+2,moves_left); moves_right = move_gen(row+2,col-2,moves_right); } else { D printf("WHITE: "); can_capture_left = can_capture(me,opponent,row,col,row-1,col-1,row-2,col-2); can_capture_right = can_capture(me,opponent,row,col,row-1,col+1,row-2,col+2); if (can_capture_left) score_left = 1 + test_capture(me,opponent,row,col,row-1,col-1,row-2,col-2,first_player,&moves_left); if (can_capture_right) score_right = 1 + test_capture(me,opponent,row,col,row-1,col+1,row-2,col+2,first_player,&moves_right); moves_left = move_gen(row-2,col-2,moves_left); moves_right = move_gen(row-2,col+2,moves_right); } if ((!can_capture_right) && (!can_capture_left) && first_player) { move_t *temp_moves; *moves = NULL; D printf(" returning 0 (no captures available)\n"); return - find_best_move(opponent,me,false,&temp_moves); } else { if ((can_capture_left && (!can_capture_right)) || (can_capture_left && can_capture_right && (score_left > score_right))) { *moves = moves_left; D printf(" returning %d (score_left)\n",score_left); return score_left; } else { *moves = moves_right; D printf(" returning %d (score_right)\n",score_right); return score_right; } } }
/* * Add all pseudo-legal captures that the given pawn can make. */ static void generate_pawn_captures(const position_t* pos, square_t from, piece_t piece, move_t** moves_head) { color_t side = pos->side_to_move; int cap_left = side == WHITE ? 15 : -15; int cap_right = side == WHITE ? 17 : -17; square_t to; rank_t r_rank = relative_rank[side][square_rank(from)]; move_t* moves = *moves_head; if (r_rank < RANK_7) { // non-promote captures to = from + cap_left; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } else if (to == pos->ep_square && pos->board[to] == EMPTY) { moves = add_move(pos, create_move_enpassant(from, to, piece, pos->board[to + pawn_push[side^1]]), moves); } to = from + cap_right; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } else if (to == pos->ep_square && pos->board[to] == EMPTY) { moves = add_move(pos, create_move_enpassant(from, to, piece, pos->board[to + pawn_push[side^1]]), moves); } } else { // capture/promotes to = from + cap_left; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { for (piece_t promoted=QUEEN; promoted > PAWN; --promoted) { moves = add_move(pos, create_move_promote(from, to, piece, pos->board[to], promoted), moves); } } to = from + cap_right; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { for (piece_t promoted=QUEEN; promoted > PAWN; --promoted) { moves = add_move(pos, create_move_promote(from, to, piece, pos->board[to], promoted), moves); } } } *moves_head = moves; }
/* * Add all pseudo-legal captures that the given piece (N/B/Q/K) can make. */ static void generate_piece_captures(const position_t* pos, square_t from, piece_t piece, move_t** moves_head) { move_t* moves = *moves_head; square_t to; // Note: I unrolled all these loops to handle each direction explicitly. // The idea was to increase performance, but it's only about 1% faster // for much more code, so it's possible I'll change this back later. switch (piece_type(piece)) { case KING: to = from - 17; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 16; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 15; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 1; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 1; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 15; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 16; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 17; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case KNIGHT: to = from - 33; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 31; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 18; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 14; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 14; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 18; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 31; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 33; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case BISHOP: for (to=from-17; pos->board[to]==EMPTY; to-=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-15; pos->board[to]==EMPTY; to-=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+15; pos->board[to]==EMPTY; to+=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+17; pos->board[to]==EMPTY; to+=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case ROOK: for (to=from-16; pos->board[to]==EMPTY; to-=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-1; pos->board[to]==EMPTY; to-=1) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+1; pos->board[to]==EMPTY; to+=1) {} if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+16; pos->board[to]==EMPTY; to+=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case QUEEN: for (to=from-17; pos->board[to]==EMPTY; to-=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-16; pos->board[to]==EMPTY; to-=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-15; pos->board[to]==EMPTY; to-=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-1; pos->board[to]==EMPTY; to-=1) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+1; pos->board[to]==EMPTY; to+=1) {} if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+15; pos->board[to]==EMPTY; to+=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+16; pos->board[to]==EMPTY; to+=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+17; pos->board[to]==EMPTY; to+=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; default: assert(false); } *moves_head = moves; }
/* * 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; }