Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NMP! #10

Merged
merged 1 commit into from
Jul 8, 2024
Merged

NMP! #10

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/board/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,28 @@ void position::make_move(const moves::move move) {
m_stm = ~m_stm;
m_key ^= utils::zobrist::get_side_key(m_stm);

m_checkers_bb = attacks_to_king(king_square(m_stm), m_stm);
m_checkers_bb = attacks_to_king(king_square(m_stm), m_stm);
m_last_move_was_null = false;
}

template void position::make_move<true>(moves::move move);
template void position::make_move<false>(moves::move move);

void position::make_null_move() {
++m_half_move_clock;

m_hash_history.push_back(m_key);

m_key ^= utils::zobrist::get_side_key(m_stm);
m_key ^= utils::zobrist::get_en_passant_key(m_ep_sq);

m_stm = ~m_stm;
m_key ^= utils::zobrist::get_side_key(m_stm);

m_checkers_bb = attacks_to_king(king_square(m_stm), m_stm);
m_last_move_was_null = true;
}

void position::reset_to_start_pos() {
m_hash_history.clear();
m_pieces.fill(piece::none);
Expand Down Expand Up @@ -296,6 +312,15 @@ void position::reset_to_start_pos() {
m_pieces[std::to_underlying(square::e8)] = piece::b_king;
}

template <color C>
bool position::has_no_pawns() const {
return piece_count<C>(piece_type::pawn) == 0;
}

bool position::has_no_pawns(const color c) const {
return c == color::white ? has_no_pawns<color::white>() : has_no_pawns<color::black>();
}

bool position::is_square_attacked_by(const square sq, const color c) const {
const auto& our_pieces = occupancies(c);
const auto& our_pawns = piece_type_bb(piece_type::pawn) & our_pieces;
Expand Down
24 changes: 16 additions & 8 deletions src/board/position.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ class position {
return copy;
}

[[nodiscard]] bitboards::bitboard checkers() const { return m_checkers_bb; }
[[nodiscard]] color side_to_move() const { return m_stm; }
[[nodiscard]] square ep_square() const { return m_ep_sq; }
[[nodiscard]] castling_rights castling() const { return m_castling; }
[[nodiscard]] u8 fifty_move_rule() const { return m_half_move_clock; }
[[nodiscard]] u16 full_moves() const { return m_full_move_number; }
[[nodiscard]] zobrist_key key() const { return m_key; }
[[nodiscard]] std::vector<zobrist_key> hash_history() const { return m_hash_history; }
[[nodiscard]] bitboards::bitboard checkers() const { return m_checkers_bb; }
[[nodiscard]] color side_to_move() const { return m_stm; }
[[nodiscard]] square ep_square() const { return m_ep_sq; }
[[nodiscard]] castling_rights castling() const { return m_castling; }
[[nodiscard]] u8 fifty_move_rule() const { return m_half_move_clock; }
[[nodiscard]] u16 full_moves() const { return m_full_move_number; }
[[nodiscard]] zobrist_key key() const { return m_key; }
[[nodiscard]] bool last_move_was_null() const { return m_last_move_was_null; }

[[nodiscard]] piece piece_on(const square sq) const {
return m_pieces[std::to_underlying(sq)];
Expand Down Expand Up @@ -182,8 +182,12 @@ class position {
template <bool SaveHashHistory>
void make_move(moves::move move);

void make_null_move();

void reset_to_start_pos();

[[nodiscard]] bool has_no_pawns(color c) const;

[[nodiscard]] bool is_square_attacked_by(square sq, color c) const;

[[nodiscard]] bool is_valid() const;
Expand All @@ -208,6 +212,9 @@ class position {
};
// clang-format on

template <color C>
[[nodiscard]] bool has_no_pawns() const;

std::vector<zobrist_key> m_hash_history;
std::array<piece, constants::num_squares> m_pieces;
std::array<bitboards::bitboard, constants::num_piece_types> m_piece_bb;
Expand All @@ -219,6 +226,7 @@ class position {
square m_ep_sq;
castling_rights m_castling;
u8 m_half_move_clock;
bool m_last_move_was_null{false};
};

namespace util {
Expand Down
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ int main(const int argc, const char *argv[]) {
board::bitboards::attacks::init();

if (argc > 1 && !strcmp(argv[1], "bench")) {
constexpr int bench_depth = 6;
constexpr int bench_depth = 7;

search::searcher searcher;
search::bench::run(searcher, bench_depth);
Expand Down
30 changes: 25 additions & 5 deletions src/search/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace search {
constexpr int rfp_depth_limit = 6;
constexpr int rfp_margin = 70;

constexpr int nmp_base_reduction = 3;

void searcher::reset_info() {
m_info.stopped = false;
m_info.searched_nodes = 0ULL;
Expand Down Expand Up @@ -123,7 +125,7 @@ score searcher::qsearch(const board::position& pos, score alpha, const score bet
for (usize i = 0; i < move_list.size(); i++) {
const auto current_move = move_list.move_at(i);

board::position copy = pos;
auto copy = pos;
copy.make_move<true>(current_move);

if (!copy.was_legal())
Expand Down Expand Up @@ -193,21 +195,39 @@ score searcher::negamax(const board::position& pos,
&& entry.can_use_score(alpha, beta))
return tt_score;

pv_line child_pv{};

const score static_eval = eval::evaluate(pos);
const bool in_check = pos.checkers().bit_count() > 0;

if (!in_check && !pv_node) {
// Reverse Futility Pruning
if (depth <= rfp_depth_limit && static_eval - rfp_margin * depth >= beta)
return static_eval;

// Null Move Pruning: If after making a null move (forfeiting the side to move) we still
// have a strong enough position to produce a cutoff, we cut the search.
// See https://en.wikipedia.org/wiki/Null-move_heuristic for reference

if (!pos.last_move_was_null() && !pos.has_no_pawns(pos.side_to_move())
&& static_eval >= beta) {
const int r = nmp_base_reduction + depth / nmp_base_reduction;

auto copy = pos;
copy.make_null_move();

const score null_move_score =
-negamax<false>(copy, -beta, -beta - 1, depth - r, ply + 1, child_pv);

if (null_move_score >= beta)
return null_move_score;
}
}

if (ply >= constants::max_ply)
return static_eval;

u16 legal_moves{};
pv_line child_pv{};

u16 legal_moves{};
score best_score = -constants::score_infinite;
auto best_move = moves::move::null();
const score original_alpha = alpha;
Expand All @@ -220,7 +240,7 @@ score searcher::negamax(const board::position& pos,
for (usize i = 0; i < move_list.size(); ++i) {
const auto current_move = move_list.move_at(i);

board::position copy = pos;
auto copy = pos;
copy.make_move<true>(current_move);

if (!copy.was_legal())
Expand Down