From b05574920d4c2251a4091995c186840e1c9316d9 Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Sat, 28 Mar 2026 18:14:56 +0700 Subject: [PATCH 01/10] does what the name said --- CMakeLists.txt | 2 +- search.cpp | 7 ++++--- uci.cpp | 22 +++++++++++++++++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9eb054..f9c26e3 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 main ) 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/search.cpp b/search.cpp index 07b9af5..5f17766 100644 --- a/search.cpp +++ b/search.cpp @@ -11,7 +11,6 @@ namespace engine { TranspositionTable search::tt(16); std::atomic stopSearch{false}; void search::stop() { - tt.clear(); stopSearch.store(true, std::memory_order_relaxed); } struct Session { @@ -72,7 +71,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, uint64_t hash = board.hash(); Move preferred = Move::none(); if (TTEntry *entry = search::tt.lookup(hash)) { - if (entry->getDepth() >= depth) { + if (entry->getDepth() >= depth && ply!=0) { Value ttScore = entry->getScore(); TTFlag flag = entry->getFlag(); @@ -116,7 +115,6 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, board.undoMove(); - // ---- ABORT PROPAGATION ---- if (childScore == VALUE_NONE) return VALUE_NONE; @@ -165,6 +163,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; @@ -195,6 +194,8 @@ 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()) + " "; diff --git a/uci.cpp b/uci.cpp index 4ee298b..bff6144 100644 --- a/uci.cpp +++ b/uci.cpp @@ -4,8 +4,10 @@ #include "ucioption.h" #include #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,11 @@ 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); From ec707b933d0bbd17195056c8b93f142b1d7f0d7a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 28 Mar 2026 11:15:29 +0000 Subject: [PATCH 02/10] Apply clang-format --- search.cpp | 20 ++++++++++++++------ uci.cpp | 24 +++++++++++++----------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/search.cpp b/search.cpp index 5f17766..ea41424 100644 --- a/search.cpp +++ b/search.cpp @@ -10,9 +10,7 @@ using namespace chess; namespace engine { TranspositionTable search::tt(16); std::atomic stopSearch{false}; -void search::stop() { - 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; @@ -71,7 +69,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, uint64_t hash = board.hash(); Move preferred = Move::none(); if (TTEntry *entry = search::tt.lookup(hash)) { - if (entry->getDepth() >= depth && ply!=0) { + if (entry->getDepth() >= depth && ply != 0) { Value ttScore = entry->getScore(); TTFlag flag = entry->getFlag(); @@ -163,7 +161,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, } void search::search(const chess::Board &board, const timeman::LimitsType timecontrol) { - stopSearch=false; + stopSearch = false; static double originalTimeAdjust = -1; Session session; session.tc = timecontrol; @@ -195,7 +193,17 @@ void search::search(const chess::Board &board, 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;} + 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()) + " "; diff --git a/uci.cpp b/uci.cpp index bff6144..5025555 100644 --- a/uci.cpp +++ b/uci.cpp @@ -4,10 +4,10 @@ #include "ucioption.h" #include #include -#include -#include #include #include +#include +#include using namespace engine; chess::Position pos; OptionsMap engine::options; @@ -74,14 +74,14 @@ timeman::LimitsType parse_limits(std::istream &is) { std::thread searchThread; void handleGo(std::istringstream &ss) { - if (searchThread.joinable()) { - search::stop(); - searchThread.join(); - } + if (searchThread.joinable()) { + search::stop(); + searchThread.join(); + } - searchThread = std::thread([ss = std::move(ss)]() mutable { - search::search(pos, parse_limits(ss)); - }); + searchThread = std::thread([ss = std::move(ss)]() mutable { + search::search(pos, parse_limits(ss)); + }); } template struct overload : Ts... { using Ts::operator()...; @@ -181,11 +181,13 @@ void engine::loop() { break; } else if (token == "stop") { search::stop(); - if (searchThread.joinable()) searchThread.join(); + if (searchThread.joinable()) + searchThread.join(); break; } else if (token == "quit") { search::stop(); - if (searchThread.joinable()) searchThread.join(); + if (searchThread.joinable()) + searchThread.join(); return; } else if (token == "setoption") { options.setoption(ss); From 8ed5191ae373d8b0b7deecf500fd588355faf3cc Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Sat, 28 Mar 2026 18:37:50 +0700 Subject: [PATCH 03/10] fix regression --- search.cpp | 2 +- tt.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/search.cpp b/search.cpp index ea41424..5b45691 100644 --- a/search.cpp +++ b/search.cpp @@ -69,7 +69,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, uint64_t hash = board.hash(); Move preferred = Move::none(); if (TTEntry *entry = search::tt.lookup(hash)) { - if (entry->getDepth() >= depth && ply != 0) { + if (entry->getDepth() >= depth) { Value ttScore = entry->getScore(); TTFlag flag = entry->getFlag(); diff --git a/tt.cpp b/tt.cpp index bd400ef..6d0bef7 100644 --- a/tt.cpp +++ b/tt.cpp @@ -68,7 +68,8 @@ 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; From 6bfdc24d9bda51238b658c92e67948f95a50d44d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 28 Mar 2026 11:38:17 +0000 Subject: [PATCH 04/10] Apply clang-format --- tt.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tt.cpp b/tt.cpp index 6d0bef7..f084236 100644 --- a/tt.cpp +++ b/tt.cpp @@ -68,8 +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 && - e->getGeneration() == this->time) + if (e->key == hash && e->getGeneration() == this->time) return e; } return nullptr; From 1824aca0b56d6c0bd41516032477f1b6bb3c8d35 Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Mon, 30 Mar 2026 11:52:37 +0700 Subject: [PATCH 05/10] implement null move pruning --- search.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/search.cpp b/search.cpp index 5b45691..5f6abf8 100644 --- a/search.cpp +++ b/search.cpp @@ -27,6 +27,7 @@ void update_pv(Move *pv, Move move, const Move *childPv) { Value qsearch(Board &board, Value alpha, Value beta, Session &session, int ply = 0) { session.nodes++; + session.seldepth = std::max(session.seldepth, ply); int standPat = eval::eval(board); Value maxScore = standPat; if (maxScore >= beta) @@ -104,10 +105,19 @@ 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); @@ -242,7 +252,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())); From a87d311a906fca666f07fff72eaf9295d31512f8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 30 Mar 2026 08:50:00 +0000 Subject: [PATCH 06/10] Apply clang-format --- search.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/search.cpp b/search.cpp index 5f6abf8..db93697 100644 --- a/search.cpp +++ b/search.cpp @@ -105,16 +105,18 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, return board.checkers() ? -MATE(ply) : 0; } movepick::orderMoves(board, moves, preferred, ply); - if (bool useNMP=depth>=3 && !board.checkers() && ply>0){ - int R=2+depth/6; + 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); + Value score = + doSearch(board, depth - 1 - R, -beta, -beta + 1, session, ply + 1); if (score == VALUE_NONE) return VALUE_NONE; - score=-score; + score = -score; board.undoMove(); - if (score>=beta) return beta; + if (score >= beta) + return beta; } for (Move move : moves) { board.doMove(move); From 528ba55232bcb0a679b5c2db707ebb773019cc65 Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Mon, 30 Mar 2026 17:07:25 +0700 Subject: [PATCH 07/10] fixed tons of bugs --- eval.h | 2 -- search.cpp | 15 ++++++++++----- tt.h | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) 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/search.cpp b/search.cpp index 5f6abf8..7e5ff38 100644 --- a/search.cpp +++ b/search.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace chess; namespace engine { TranspositionTable search::tt(16); @@ -26,6 +27,9 @@ 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); @@ -38,8 +42,10 @@ 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) @@ -95,7 +101,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; @@ -109,11 +115,10 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, int R=2+depth/6; board.doNullMove(); Value score=doSearch(board, depth-1-R, -beta, -beta+1, session, ply+1); - + board.undoMove(); if (score == VALUE_NONE) return VALUE_NONE; score=-score; - board.undoMove(); if (score>=beta) return beta; } for (Move move : moves) { @@ -190,7 +195,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; 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 From 45bc06afc74832888dce182c7746590e802ffc99 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 30 Mar 2026 10:09:02 +0000 Subject: [PATCH 08/10] Apply clang-format --- search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/search.cpp b/search.cpp index 23a059c..4d33b3f 100644 --- a/search.cpp +++ b/search.cpp @@ -44,8 +44,9 @@ Value qsearch(Board &board, Value alpha, Value beta, Session &session, board.doMove(move); Value score = qsearch(board, -beta, -alpha, session, ply + 1); board.undoMove(); - if (score==VALUE_NONE) return VALUE_NONE; - score=-score; + if (score == VALUE_NONE) + return VALUE_NONE; + score = -score; if (score >= beta) return score; if (score > maxScore) From a37009c7fbcd561f99cfc9960c1fa03d60137467 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:12:40 +0700 Subject: [PATCH 09/10] Update score.cpp --- score.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 0f597cdc30f80daba959df6020c7b0e9d319201c Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:54:55 +0000 Subject: [PATCH 10/10] using experimental branch 46-null-move-modification-fix without merging library PR --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9c26e3..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 main + 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")