char char_from_piece(piece_t p) { static const char sPIECES[6] = { 'P', 'N', 'B', 'R', 'Q', 'K' }; if (piece_color(p) == BLACK) { return tolower(sPIECES[piece_type(p)]); } return sPIECES[piece_type(p)]; }
/* * Generate all non-capturing, non-promoting, pseudo-legal checks. Used for * quiescent move generation. */ int generate_pseudo_checks(const position_t* pos, move_t* moves) { move_t* moves_head = moves; color_t side = pos->side_to_move, other_side = side^1; square_t king_sq = pos->pieces[other_side][0]; for (int i = 0; i < pos->num_pawns[side]; ++i) { square_t from = pos->pawns[side][i]; square_t to = INVALID_SQUARE; piece_t piece = pos->board[from]; // Figure out whether or not we can discover check by moving. direction_t discover_check_dir = 0; bool will_discover_check; const attack_data_t* king_atk = &get_attack_data(from, king_sq); if ((king_atk->possible_attackers & Q_FLAG) == 0) { discover_check_dir = 0; } else { direction_t king_dir = king_atk->relative_direction; square_t sq; for (sq = from+king_dir; pos->board[sq] == EMPTY; sq+=king_dir) {} if (sq == king_sq) { // Nothing between us and the king. Is there anything // behind us to do the check when we move? for (sq = from-king_dir; pos->board[sq] == EMPTY; sq -= king_dir) {} if (side == piece_color(pos->board[sq]) && (king_atk->possible_attackers & get_piece_flag(pos->board[sq]))) { discover_check_dir = king_dir; } } } // Generate checking moves. will_discover_check = discover_check_dir && abs(discover_check_dir)!=N; to = from + pawn_push[side]; if (pos->board[to] != EMPTY) continue; rank_t r_rank = relative_rank[side][square_rank(from)]; if (r_rank == RANK_7) continue; // non-promotes only for (const direction_t* delta = piece_deltas[piece]; *delta; ++delta) { if (will_discover_check || to + *delta == king_sq) { moves = add_move(pos, create_move(from, to, piece, EMPTY), moves); break; } } to += pawn_push[side]; if (r_rank == RANK_2 && pos->board[to] == EMPTY) { for (const direction_t* delta = piece_deltas[piece]; *delta; ++delta) { if (will_discover_check || to + *delta == king_sq) { moves = add_move(pos, create_move(from, to, piece, EMPTY), moves); break; } } } } for (int i = 0; i < pos->num_pieces[side]; ++i) { square_t from = pos->pieces[side][i]; square_t to = INVALID_SQUARE; piece_t piece = pos->board[from]; // Figure out whether or not we can discover check by moving. direction_t discover_check_dir = 0; bool will_discover_check; const attack_data_t* king_atk = &get_attack_data(from, king_sq); if ((king_atk->possible_attackers & Q_FLAG) == 0) { discover_check_dir = 0; } else { direction_t king_dir = king_atk->relative_direction; square_t sq; for (sq = from+king_dir; pos->board[sq] == EMPTY; sq+=king_dir) {} if (sq == king_sq) { // Nothing between us and the king. Is there anything // behind us to do the check when we move? for (sq = from - king_dir; pos->board[sq] == EMPTY; sq -= king_dir) {} if (side == piece_color(pos->board[sq]) && (king_atk->possible_attackers & get_piece_flag(pos->board[sq]))) { discover_check_dir = king_dir; } } } // Generate checking moves. 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; if (discover_check_dir || possible_attack(to, king_sq, WN)) { 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) { will_discover_check = discover_check_dir && abs(discover_check_dir) != abs(*delta); if (will_discover_check) { moves = add_move(pos, create_move(from, to, piece, NONE), moves); continue; } if (possible_attack(to, king_sq, piece)) { const direction_t to_king = direction(to, king_sq); square_t x = to + to_king; for (; pos->board[x] == EMPTY; x+=to_king) {} if (x == king_sq) { moves = add_move(pos, create_move(from, to, piece, NONE), moves); } } } } } } *moves = 0; return moves-moves_head; }
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth) { int palpha = alpha; int i, score = -MATE, highest = -MATE; node_t node; move_t cutoff = 0; piece_t p; init_node(&node); assert(app); app->search.nodes++; node.depth = depth; /* max depth */ if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) { /* return evaluate(app->board); */ return quiescent(app, parent, alpha, beta); } /* recursive base */ if (depth == 0) { return evaluate(app->game.board); } /* draws */ if (repetitions(app) || (app->game.board->half >= 100)) { return 0; } /* if we are checked, set the nodes checked flag */ if (check(app->game.board, app->game.board->side)) { node.flags |= NODE_CHECK; /* extend our search by 1 depth if we are in check */ /* NOTES: we may want to NOT extend our search here if the parent is in check, because the means we already extended once */ depth++; } /* TODO: - NULL moves - Late-move reduction - Tactical extensions (pins & forks -> depth++) */ /* probe our table */ if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) { app->hash.cut++; return score; } /* generate moves */ generate_moves(app->game.board, &node.ml, &node.ml); /* reset score */ score = -MATE; /* try to match our hash hit move */ if (cutoff != 0) { for (i = 0; i < node.ml.count; i++) { if (node.ml.moves[i] == cutoff) { node.ml.scores[i] = 20000; break; } } } /* search negamax */ for (i = 0; i < node.ml.count; i++) { /* get the next move ordered */ next_move(i, &node.ml); if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i]))) continue; score = -alpha_beta(app, &node, -beta, -alpha, depth - 1); node.made++; undo_move(app->game.board, &app->game.undo); /* score whatever is best so far */ if (score > highest) { node.best = node.ml.moves[i]; highest = score; /* update alpha */ if (score > alpha) { if (score >= beta) { /* non-captures causing beta cutoffs (killers) */ if (!is_capture(node.ml.moves[i])) { app->game.board->killers[1][app->game.board->ply] = app->game.board->killers[0][app->game.board->ply]; app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i]; } /* store this beta in our transposition table */ store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth); return beta; } /* update alpha */ alpha = score; /* update our history */ if (!is_capture(node.best)) { p = app->game.board->pos.squares[move_from(node.best)]; app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth; } } } } /* check for checkmate or stalemate */ if (!node.made) { if (node.flags & NODE_CHECK) { return -MATE + app->game.board->ply; } else { return 0; } } if (alpha != palpha) { /* store this as an exact, since we beat alpha */ store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth); } else { /* store the current alpha */ store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth); } return alpha; }
/* * 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); }