From b05574920d4c2251a4091995c186840e1c9316d9 Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Sat, 28 Mar 2026 18:14:56 +0700 Subject: [PATCH 01/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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") From 381978af1e6fe5f91923027aabf74bbe3fabd4f9 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:35:15 +0000 Subject: [PATCH 11/14] disabled library testing --- CMakeLists.txt | 3 ++- eval.cpp | 4 ++-- search.cpp | 34 +++++++++++++++++++++------------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 370ee7a..3405d8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,10 +5,11 @@ project(cppchess_engine) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) include(FetchContent) +set(BUILD_TESTING OFF) FetchContent_Declare( chesslib GIT_REPOSITORY https://github.com/winapiadmin/chesslib.git - GIT_TAG 46-null-move-modification-fix + 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/eval.cpp b/eval.cpp index a9b3722..7e38c2f 100644 --- a/eval.cpp +++ b/eval.cpp @@ -97,11 +97,11 @@ Value eg_king_table[64] = {-74, -35, -18, -18, -11, 15, 4, -17, -12, 17, 14, -17, -53, -34, -21, -11, -28, -14, -24, -43}; Value *mg_pesto_table[] = { - {0}, mg_pawn_table, mg_knight_table, mg_bishop_table, + nullptr, mg_pawn_table, mg_knight_table, mg_bishop_table, mg_rook_table, mg_queen_table, mg_king_table}; Value *eg_pesto_table[] = { - {0}, eg_pawn_table, eg_knight_table, eg_bishop_table, + nullptr, eg_pawn_table, eg_knight_table, eg_bishop_table, eg_rook_table, eg_queen_table, eg_king_table}; Value eval(const chess::Board &board) { diff --git a/search.cpp b/search.cpp index 4d33b3f..78f0ae3 100644 --- a/search.cpp +++ b/search.cpp @@ -56,7 +56,7 @@ Value qsearch(Board &board, Value alpha, Value beta, Session &session, } return maxScore; } -Value doSearch(Board &board, int depth, Value alpha, Value beta, +Value doSearch(Board board, int depth, Value alpha, Value beta, Session &session, int ply = 0) { if (ply >= MAX_PLY - 1) return eval::eval(board); @@ -118,25 +118,34 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta, Value score = doSearch(board, depth - 1 - R, -beta, -beta + 1, session, ply + 1); - if (score == VALUE_NONE) + if (score == VALUE_NONE) { + board.undoMove(); return VALUE_NONE; + } score = -score; board.undoMove(); if (score >= beta) - return beta; + return score; } - for (Move move : moves) { + for (size_t i = 0; i < moves.size(); ++i) { + Move move = moves[i]; + int reduction = (i >= 3 && depth >= 3 && !board.isCapture(move)) ? 1 : 0; board.doMove(move); - Value childScore = - doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); - - board.undoMove(); - - if (childScore == VALUE_NONE) + Value childScore = doSearch(board, depth - 1 - reduction, -alpha - 1, -alpha, session, ply + 1); + if (childScore == VALUE_NONE){ + board.undoMove(); return VALUE_NONE; - + } Value score = -childScore; - + if (reduction > 0 && score > alpha) { + childScore = doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); + board.undoMove(); + if (childScore == VALUE_NONE) return VALUE_NONE; + score = -childScore; + } + else + board.undoMove(); + if (score > maxScore) { maxScore = score; update_pv(session.pv[ply], move, session.pv[ply + 1]); @@ -194,7 +203,6 @@ void search::search(const chess::Board &board, // since MAX_PLY=64 session.pv[_][j] = Move::none(); } - session.nodes = 0; auto board_ = board; Value score_ = doSearch(board_, i, -VALUE_INFINITE, VALUE_INFINITE, session); From 2d7b591e86e8585c884c1acf780b461f9367384d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 1 Apr 2026 11:35:40 +0000 Subject: [PATCH 12/14] Apply clang-format --- eval.cpp | 12 ++++++------ search.cpp | 13 +++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/eval.cpp b/eval.cpp index 7e38c2f..861b1e7 100644 --- a/eval.cpp +++ b/eval.cpp @@ -96,13 +96,13 @@ Value eg_king_table[64] = {-74, -35, -18, -18, -11, 15, 4, -17, -12, 17, 14, 23, 16, 7, -9, -27, -11, 4, 13, 14, 4, -5, -17, -53, -34, -21, -11, -28, -14, -24, -43}; -Value *mg_pesto_table[] = { - nullptr, mg_pawn_table, mg_knight_table, mg_bishop_table, - mg_rook_table, mg_queen_table, mg_king_table}; +Value *mg_pesto_table[] = {nullptr, mg_pawn_table, mg_knight_table, + mg_bishop_table, mg_rook_table, mg_queen_table, + mg_king_table}; -Value *eg_pesto_table[] = { - nullptr, eg_pawn_table, eg_knight_table, eg_bishop_table, - eg_rook_table, eg_queen_table, eg_king_table}; +Value *eg_pesto_table[] = {nullptr, eg_pawn_table, eg_knight_table, + eg_bishop_table, eg_rook_table, eg_queen_table, + eg_king_table}; Value eval(const chess::Board &board) { int pieceCount[10] = { diff --git a/search.cpp b/search.cpp index 78f0ae3..d380be4 100644 --- a/search.cpp +++ b/search.cpp @@ -131,8 +131,9 @@ Value doSearch(Board board, int depth, Value alpha, Value beta, Move move = moves[i]; int reduction = (i >= 3 && depth >= 3 && !board.isCapture(move)) ? 1 : 0; board.doMove(move); - Value childScore = doSearch(board, depth - 1 - reduction, -alpha - 1, -alpha, session, ply + 1); - if (childScore == VALUE_NONE){ + Value childScore = doSearch(board, depth - 1 - reduction, -alpha - 1, + -alpha, session, ply + 1); + if (childScore == VALUE_NONE) { board.undoMove(); return VALUE_NONE; } @@ -140,12 +141,12 @@ Value doSearch(Board board, int depth, Value alpha, Value beta, if (reduction > 0 && score > alpha) { childScore = doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); board.undoMove(); - if (childScore == VALUE_NONE) return VALUE_NONE; + if (childScore == VALUE_NONE) + return VALUE_NONE; score = -childScore; - } - else + } else board.undoMove(); - + if (score > maxScore) { maxScore = score; update_pv(session.pv[ply], move, session.pv[ply + 1]); From 3998d8a74ef1dcc958e7413528393a5a16d83534 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:12:32 +0000 Subject: [PATCH 13/14] fix LMR/PVS bug --- search.cpp | 97 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/search.cpp b/search.cpp index d380be4..874d2ad 100644 --- a/search.cpp +++ b/search.cpp @@ -129,49 +129,88 @@ Value doSearch(Board board, int depth, Value alpha, Value beta, } for (size_t i = 0; i < moves.size(); ++i) { Move move = moves[i]; - int reduction = (i >= 3 && depth >= 3 && !board.isCapture(move)) ? 1 : 0; + + bool isCapture = board.isCapture(move); + bool givesCheck = board.givesCheck(move)!=CheckType::NO_CHECK; + + // --- LMR reduction --- + int reduction = 0; + if (i >= 3 && depth >= 3 && !isCapture && !givesCheck) { + reduction = 1 + (int)(i / 6) + (depth / 8); + + // history heuristic: good moves get reduced less + if (movepick::historyHeuristic[(int)move.from()][(int)move.to()] > 0) + reduction--; + + reduction = std::max(0, reduction); + reduction = std::min(reduction, depth - 2); + } + board.doMove(move); - Value childScore = doSearch(board, depth - 1 - reduction, -alpha - 1, - -alpha, session, ply + 1); - if (childScore == VALUE_NONE) { - board.undoMove(); - return VALUE_NONE; + + Value score; + + if (i == 0) { + // --- First move: full window (PVS root move) --- + score = -doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); + + if (score == VALUE_NONE){ + board.undoMove(); + return VALUE_NONE; + } + } else { + // --- Null-window search (PVS + LMR) --- + score = doSearch(board, + depth - 1 - reduction, + -alpha - 1, -alpha, + session, ply + 1); + if (score == VALUE_NONE) { + board.undoMove(); + return VALUE_NONE; + } + score=-score; + // --- Re-search if it improves alpha --- + if (score > alpha) { + score = doSearch(board, + depth - 1, + -beta, -alpha, + session, ply + 1); + if (score == VALUE_NONE) { + board.undoMove(); + return VALUE_NONE; + } + score = -score; + } } - Value score = -childScore; - if (reduction > 0 && score > alpha) { - childScore = doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); - board.undoMove(); - if (childScore == VALUE_NONE) - return VALUE_NONE; - score = -childScore; - } else - board.undoMove(); + + board.undoMove(); if (score > maxScore) { - maxScore = score; - update_pv(session.pv[ply], move, session.pv[ply + 1]); + maxScore = score; + update_pv(session.pv[ply], move, session.pv[ply + 1]); } if (score > alpha) { - alpha = score; - if (!board.isCapture(move)) - movepick::historyHeuristic[(int)move.from()][(int)move.to()] += - depth * depth; + alpha = score; + + if (!isCapture) + movepick::historyHeuristic[(int)move.from()][(int)move.to()] += depth * depth; } + if (alpha >= beta) { - if (!board.isCapture(move)) { - if (movepick::killerMoves[ply][0] != move) { - movepick::killerMoves[ply][1] = movepick::killerMoves[ply][0]; - movepick::killerMoves[ply][0] = move; + // killer moves + if (!isCapture) { + if (movepick::killerMoves[ply][0] != move) { + movepick::killerMoves[ply][1] = movepick::killerMoves[ply][0]; + movepick::killerMoves[ply][0] = move; + } } - } - - break; + break; } if (session.tm.elapsed() >= session.tm.optimum() || stopSearch.load(std::memory_order_relaxed)) - return VALUE_NONE; + return VALUE_NONE; } if (maxScore != -VALUE_INFINITE) { From 55b4b0e9a27a8e73456710e42e19ebc54dc70ca9 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 1 Apr 2026 12:12:57 +0000 Subject: [PATCH 14/14] Apply clang-format --- search.cpp | 88 ++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/search.cpp b/search.cpp index 874d2ad..f05dfd0 100644 --- a/search.cpp +++ b/search.cpp @@ -131,19 +131,19 @@ Value doSearch(Board board, int depth, Value alpha, Value beta, Move move = moves[i]; bool isCapture = board.isCapture(move); - bool givesCheck = board.givesCheck(move)!=CheckType::NO_CHECK; + bool givesCheck = board.givesCheck(move) != CheckType::NO_CHECK; // --- LMR reduction --- int reduction = 0; if (i >= 3 && depth >= 3 && !isCapture && !givesCheck) { - reduction = 1 + (int)(i / 6) + (depth / 8); + reduction = 1 + (int)(i / 6) + (depth / 8); - // history heuristic: good moves get reduced less - if (movepick::historyHeuristic[(int)move.from()][(int)move.to()] > 0) - reduction--; + // history heuristic: good moves get reduced less + if (movepick::historyHeuristic[(int)move.from()][(int)move.to()] > 0) + reduction--; - reduction = std::max(0, reduction); - reduction = std::min(reduction, depth - 2); + reduction = std::max(0, reduction); + reduction = std::min(reduction, depth - 2); } board.doMove(move); @@ -151,66 +151,62 @@ Value doSearch(Board board, int depth, Value alpha, Value beta, Value score; if (i == 0) { - // --- First move: full window (PVS root move) --- - score = -doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); + // --- First move: full window (PVS root move) --- + score = -doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); - if (score == VALUE_NONE){ - board.undoMove(); - return VALUE_NONE; - } + if (score == VALUE_NONE) { + board.undoMove(); + return VALUE_NONE; + } } else { - // --- Null-window search (PVS + LMR) --- - score = doSearch(board, - depth - 1 - reduction, - -alpha - 1, -alpha, - session, ply + 1); + // --- Null-window search (PVS + LMR) --- + score = doSearch(board, depth - 1 - reduction, -alpha - 1, -alpha, + session, ply + 1); + if (score == VALUE_NONE) { + board.undoMove(); + return VALUE_NONE; + } + score = -score; + // --- Re-search if it improves alpha --- + if (score > alpha) { + score = doSearch(board, depth - 1, -beta, -alpha, session, ply + 1); if (score == VALUE_NONE) { - board.undoMove(); - return VALUE_NONE; - } - score=-score; - // --- Re-search if it improves alpha --- - if (score > alpha) { - score = doSearch(board, - depth - 1, - -beta, -alpha, - session, ply + 1); - if (score == VALUE_NONE) { - board.undoMove(); - return VALUE_NONE; - } - score = -score; + board.undoMove(); + return VALUE_NONE; } + score = -score; + } } board.undoMove(); if (score > maxScore) { - maxScore = score; - update_pv(session.pv[ply], move, session.pv[ply + 1]); + maxScore = score; + update_pv(session.pv[ply], move, session.pv[ply + 1]); } if (score > alpha) { - alpha = score; + alpha = score; - if (!isCapture) - movepick::historyHeuristic[(int)move.from()][(int)move.to()] += depth * depth; + if (!isCapture) + movepick::historyHeuristic[(int)move.from()][(int)move.to()] += + depth * depth; } if (alpha >= beta) { - // killer moves - if (!isCapture) { - if (movepick::killerMoves[ply][0] != move) { - movepick::killerMoves[ply][1] = movepick::killerMoves[ply][0]; - movepick::killerMoves[ply][0] = move; - } + // killer moves + if (!isCapture) { + if (movepick::killerMoves[ply][0] != move) { + movepick::killerMoves[ply][1] = movepick::killerMoves[ply][0]; + movepick::killerMoves[ply][0] = move; } - break; + } + break; } if (session.tm.elapsed() >= session.tm.optimum() || stopSearch.load(std::memory_order_relaxed)) - return VALUE_NONE; + return VALUE_NONE; } if (maxScore != -VALUE_INFINITE) {