diff --git a/CMakeLists.txt b/CMakeLists.txt index d9eb054..370ee7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(FetchContent) FetchContent_Declare( chesslib GIT_REPOSITORY https://github.com/winapiadmin/chesslib.git - GIT_TAG maintaince_1 + GIT_TAG 46-null-move-modification-fix ) FetchContent_MakeAvailable(chesslib) add_executable(engine "main.cpp" "timeman.cpp" "timeman.h" "eval.h" "eval.cpp" "tune.h" "ucioption.h" "tune.cpp" "ucioption.cpp" "tt.h" "tt.cpp" "uci.cpp" "uci.h" "search.h" "search.cpp" "score.h" "score.cpp" "movepick.h" "movepick.cpp") diff --git a/eval.h b/eval.h index eb881e6..74c1e20 100644 --- a/eval.h +++ b/eval.h @@ -1,6 +1,4 @@ #pragma once -#include -#include #include using Value = int; namespace engine { diff --git a/score.cpp b/score.cpp index 2fdc2c5..2d07d9e 100644 --- a/score.cpp +++ b/score.cpp @@ -1,5 +1,6 @@ #include "score.h" #include +#include namespace engine { Score::Score(Value v) { assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); @@ -14,4 +15,4 @@ Score::Score(Value v) { score = (v > 0) ? Mate{distance} : Mate{-distance}; } } -} // namespace engine \ No newline at end of file +} // namespace engine diff --git a/search.cpp b/search.cpp index 07b9af5..4d33b3f 100644 --- a/search.cpp +++ b/search.cpp @@ -6,14 +6,12 @@ #include #include #include +#include using namespace chess; namespace engine { TranspositionTable search::tt(16); std::atomic stopSearch{false}; -void search::stop() { - tt.clear(); - stopSearch.store(true, std::memory_order_relaxed); -} +void search::stop() { stopSearch.store(true, std::memory_order_relaxed); } struct Session { timeman::TimeManagement tm; timeman::LimitsType tc; @@ -29,7 +27,11 @@ void update_pv(Move *pv, Move move, const Move *childPv) { } Value qsearch(Board &board, Value alpha, Value beta, Session &session, int ply = 0) { + if (session.tm.elapsed() >= session.tm.optimum() || + stopSearch.load(std::memory_order_relaxed)) + return VALUE_NONE; session.nodes++; + session.seldepth = std::max(session.seldepth, ply); int standPat = eval::eval(board); Value maxScore = standPat; if (maxScore >= beta) @@ -40,8 +42,11 @@ Value qsearch(Board &board, Value alpha, Value beta, Session &session, board.legals(moves); for (Move move : moves) { board.doMove(move); - Value score = -qsearch(board, -beta, -alpha, session, ply + 1); + Value score = qsearch(board, -beta, -alpha, session, ply + 1); board.undoMove(); + if (score == VALUE_NONE) + return VALUE_NONE; + score = -score; if (score >= beta) return score; if (score > maxScore) @@ -97,7 +102,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, preferred = Move(entry->getMove()); } if (depth == 0) { - return qsearch(board, alpha, beta, session, ply + 1); + return qsearch(board, alpha, beta, session, ply); } Value maxScore = -VALUE_INFINITE; Movelist moves; @@ -107,16 +112,26 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, return board.checkers() ? -MATE(ply) : 0; } movepick::orderMoves(board, moves, preferred, ply); - for (Move move : moves) { + if (bool useNMP = depth >= 3 && !board.checkers() && ply > 0) { + int R = 2 + depth / 6; + board.doNullMove(); + Value score = + doSearch(board, depth - 1 - R, -beta, -beta + 1, session, ply + 1); + if (score == VALUE_NONE) + return VALUE_NONE; + score = -score; + board.undoMove(); + if (score >= beta) + return beta; + } + for (Move move : moves) { board.doMove(move); - Value childScore = doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); board.undoMove(); - // ---- ABORT PROPAGATION ---- if (childScore == VALUE_NONE) return VALUE_NONE; @@ -165,6 +180,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, } void search::search(const chess::Board &board, const timeman::LimitsType timecontrol) { + stopSearch = false; static double originalTimeAdjust = -1; Session session; session.tc = timecontrol; @@ -183,7 +199,7 @@ void search::search(const chess::Board &board, Value score_ = doSearch(board_, i, -VALUE_INFINITE, VALUE_INFINITE, session); if (session.tm.elapsed() >= session.tm.optimum() || - stopSearch.load(std::memory_order_relaxed) || score_ == VALUE_NONE) + stopSearch.load(std::memory_order_relaxed) || abs(score_) == VALUE_NONE) break; InfoFull info{}; info.depth = i; @@ -195,6 +211,18 @@ void search::search(const chess::Board &board, info.timeMs = session.tm.elapsed(); info.multiPV = 1; info.score = score_; + TTEntry *entry = tt.lookup(board.hash()); + if (entry) + switch (entry->getFlag()) { + case LOWERBOUND: + info.bound = "lowerbound"; + break; + case UPPERBOUND: + info.bound = "upperbound"; + break; + default: + break; + } std::string pv = ""; for (Move *m = session.pv[0]; *m != Move::none(); m++) pv += chess::uci::moveToUci(*m, board.chess960()) + " "; @@ -233,7 +261,7 @@ void search::search(const chess::Board &board, info.nodes = 1; info.score = 0; info.multiPV = 1; - info.pv = chess::uci::moveToUci(best, board.chess960()); + info.pv = std::string(chess::uci::moveToUci(best, board.chess960())); report(info); report(chess::uci::moveToUci(best, board.chess960())); diff --git a/tt.cpp b/tt.cpp index bd400ef..f084236 100644 --- a/tt.cpp +++ b/tt.cpp @@ -68,7 +68,7 @@ TTEntry *TranspositionTable::lookup(uint64_t hash) { TTEntry &e0 = table[index], &e1 = table[index + 1]; // Check the entries for (TTEntry *e : {&e0, &e1}) { - if (e->key == hash) + if (e->key == hash && e->getGeneration() == this->time) return e; } return nullptr; diff --git a/tt.h b/tt.h index f2e2496..6c69dc3 100644 --- a/tt.h +++ b/tt.h @@ -37,8 +37,8 @@ struct TTEntry { << GEN_SHIFT; // getters - inline uint16_t getScore() const noexcept { - return static_cast((pack & SCORE_MASK) >> SCORE_SHIFT); + inline int16_t getScore() const noexcept { + return static_cast((pack & SCORE_MASK) >> SCORE_SHIFT); } inline uint8_t getDepth() const noexcept { @@ -176,4 +176,4 @@ class TranspositionTable { return (used * 1000) / buckets; } }; -} // namespace engine \ No newline at end of file +} // namespace engine diff --git a/uci.cpp b/uci.cpp index 4ee298b..5025555 100644 --- a/uci.cpp +++ b/uci.cpp @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include using namespace engine; chess::Position pos; OptionsMap engine::options; @@ -69,7 +71,18 @@ timeman::LimitsType parse_limits(std::istream &is) { return limits; } -void handleGo(std::istringstream &ss) { search::search(pos, parse_limits(ss)); } +std::thread searchThread; + +void handleGo(std::istringstream &ss) { + if (searchThread.joinable()) { + search::stop(); + searchThread.join(); + } + + searchThread = std::thread([ss = std::move(ss)]() mutable { + search::search(pos, parse_limits(ss)); + }); +} template struct overload : Ts... { using Ts::operator()...; }; @@ -159,7 +172,7 @@ void engine::loop() { break; } else if (token == "position") { handlePosition(ss); - break; // rest belongs to position + break; } else if (token == "go") { handleGo(ss); break; // rest belongs to go @@ -168,8 +181,13 @@ void engine::loop() { break; } else if (token == "stop") { search::stop(); + if (searchThread.joinable()) + searchThread.join(); break; } else if (token == "quit") { + search::stop(); + if (searchThread.joinable()) + searchThread.join(); return; } else if (token == "setoption") { options.setoption(ss);