From 9d398326b16cfc08118f0c5a282e664fb8eb4fdd Mon Sep 17 00:00:00 2001 From: Plan B Date: Sun, 25 Nov 2018 12:27:20 +0800 Subject: [PATCH] Better PV output --- src/search.cpp | 60 ++++++++++++++++++++++++++------------------------ src/search.h | 22 +++++++++++++----- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b9d2c4c..d68d905 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -106,7 +106,7 @@ std::vector> scoreNoisyMoves(const Board& b, const std::vec return scoredMoves; } -int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bool allowNull = true) { +int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, Move (&ppv)[MAX_PLY], bool allowNull = true) { if (timeOver(si)) return alpha; bool isRoot = (!ply); @@ -134,6 +134,9 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo } } + Move pv[MAX_PLY]; + for (auto& m : pv) m = NO_MOVE; + bool isInCheck = inCheck(b, b.turn); if (depth <= 0) { @@ -144,7 +147,7 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo } else { // Drop into qsearch - int qscore = qsearch(b, ply, alpha, beta, si); + int qscore = qsearch(b, ply, alpha, beta, si, pv); return qscore; } } @@ -166,7 +169,7 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo auto u = makeNullMove(b); int nullMoveR = nullMoveBaseR + depth / 6; nullMoveR = std::min(nullMoveR, 4); - score = -search(b, depth - 1 - nullMoveR, ply + 1, -beta, -beta + 1, si, false); + score = -search(b, depth - 1 - nullMoveR, ply + 1, -beta, -beta + 1, si, pv, false); undoNullMove(b, u); if (score >= beta) return score; } @@ -218,10 +221,10 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo if (isKiller) lateMoveR -= 1; lateMoveR = std::max(0, std::min(lateMoveR, depth - 1)); // Do not drop directly into qsearch - score = -search(b, depth - lateMoveR - 1, ply + 1, -alpha - 1, -alpha, si); + score = -search(b, depth - lateMoveR - 1, ply + 1, -alpha - 1, -alpha, si, pv); } - if (score > alpha) score = -search(b, depth - 1, ply + 1, -beta, -alpha, si); + if (score > alpha) score = -search(b, depth - 1, ply + 1, -beta, -alpha, si, pv); undoMove(b, m, u); @@ -238,7 +241,7 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo } // Update history score - if (!isNoisy) historyMoves[b.turn][pieceType(b.squares[m.from])][m.to] += depth * depth; + if (!isNoisy && depth <= 12) historyMoves[b.turn][pieceType(b.squares[m.from])][m.to] += depth * depth; // Update move ordering info si.failHigh++; @@ -251,6 +254,13 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo alpha = score; bestMove = m; TTFlag = TT_EXACT; + + // Copy PV from child nodes + ppv[ply] = m; + for (int i = ply + 1; i < MAX_PLY; ++i) { + if (pv[i] == NO_MOVE) break; + ppv[i] = pv[i]; + } } } @@ -269,7 +279,7 @@ int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bo return alpha; } -int qsearch(Board& b, int ply, int alpha, int beta, SearchInfo& si) { +int qsearch(Board& b, int ply, int alpha, int beta, SearchInfo& si, Move (&ppv)[MAX_PLY]) { if (timeOver(si)) return alpha; if (ply > si.seldepth) si.seldepth = ply; @@ -289,6 +299,9 @@ int qsearch(Board& b, int ply, int alpha, int beta, SearchInfo& si) { std::vector> scoredNoisyMoves = scoreNoisyMoves(b, noisyMoves); std::sort(scoredNoisyMoves.begin(), scoredNoisyMoves.end(), [](const auto& a, const auto& b) { return a.second > b.second; }); + Move pv[MAX_PLY]; + for (auto& m : pv) m = NO_MOVE; + for (int i = 0, size = scoredNoisyMoves.size(); i < size; ++i) { Move m = scoredNoisyMoves[i].first; @@ -304,13 +317,21 @@ int qsearch(Board& b, int ply, int alpha, int beta, SearchInfo& si) { continue; } - int score = -qsearch(b, ply + 1, -beta, -alpha, si); + int score = -qsearch(b, ply + 1, -beta, -alpha, si, pv); undoMove(b, m, u); si.qnodes++; if (score >= beta) return beta; - if (score > alpha) alpha = score; + if (score > alpha) { + alpha = score; + + ppv[ply] = m; + for (int i = ply + 1; i < MAX_PLY; ++i) { + if (pv[i] == NO_MOVE) break; + ppv[i] = pv[i]; + } + } } return alpha; } @@ -409,7 +430,7 @@ void iterativeDeepening(Board& b, SearchInfo& si, int timeLimit) { si.reset(); si.depth = i; - int score = search(b, i, 0, alpha, beta, si); + int score = search(b, i, 0, alpha, beta, si, si.pv); if (timeOver(si)) break; if ((score <= alpha) || (score >= beta)) { @@ -421,8 +442,6 @@ void iterativeDeepening(Board& b, SearchInfo& si, int timeLimit) { } si.print(); - printPV(b, i); - std::cout << "\n"; research = false; if (i >= aspirationMinDepth) { @@ -434,21 +453,4 @@ void iterativeDeepening(Board& b, SearchInfo& si, int timeLimit) { si = searchCache; std::cout << "bestmove " << toNotation(si.bestMove) << "\n"; ageTT(); -} - -void printPV(Board b, int limit) { - int ply = 0; - std::cout << "pv "; - auto it = tt.find(b.key); - Move hashMove = (it != tt.end()) ? it->second.move : NO_MOVE; - while (hashMove != NO_MOVE) { - ply++; - makeMove(b, hashMove); - std::cout << toNotation(hashMove) << " "; - it = tt.find(b.key); - hashMove = (it != tt.end()) ? it->second.move : NO_MOVE; - // PV currently does not account for threefold repetition and will cycle hash moves - // We use a failsafe to prevent an infinite loop - if (ply > limit) break; - } } \ No newline at end of file diff --git a/src/search.h b/src/search.h index 6b7141a..f7a816f 100644 --- a/src/search.h +++ b/src/search.h @@ -17,6 +17,7 @@ struct SearchInfo { Move bestMove = NO_MOVE; double start = clock(); int limit = 0; + Move pv[MAX_PLY]; void operator=(const SearchInfo& si) { depth = si.depth; @@ -29,6 +30,10 @@ struct SearchInfo { bestMove = si.bestMove; start = si.start; limit = si.limit; + for (int i = 0; i < MAX_PLY; ++i) { + if (si.pv[i] == NO_MOVE) break; + pv[i] = si.pv[i]; + } } void initTime(int limitParam) { @@ -45,6 +50,7 @@ struct SearchInfo { failHigh = 0; failHighFirst = 0; bestMove = NO_MOVE; + for (auto& m : pv) m = NO_MOVE; } void print() { @@ -56,7 +62,13 @@ struct SearchInfo { std::cout << "mate " << (MATE_SCORE - abs(score)) / 2 + (score > 0); } std::cout << " depth " << depth << " seldepth " << seldepth << " nodes " << nodes + qnodes; - std::cout << " time " << (double)(clock() - start) / (CLOCKS_PER_SEC / 1000) << " "; + std::cout << " time " << (double)(clock() - start) / (CLOCKS_PER_SEC / 1000); + std::cout << " pv "; + for (const auto& m : pv) { + if (m == NO_MOVE) break; + std::cout << toNotation(m) << " "; + } + std::cout << "\n"; } }; @@ -76,7 +88,7 @@ static constexpr int lateMoveMinDepth = 3; static constexpr int lateMoveMinMove = 4; static constexpr int lateMovePruningMaxDepth = 4; -static constexpr int lateMovePruningMove = 8; +static constexpr int lateMovePruningMove = 7; static constexpr int hashMoveBonus = 500000; @@ -103,8 +115,8 @@ std::vector> scoreMoves(const Board& b, const std::vector> scoreNoisyMoves(const Board& b, const std::vector& moves); -int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, bool allowNull); -int qsearch(Board& b, int ply, int alpha, int beta, SearchInfo& si); +int search(Board& b, int depth, int ply, int alpha, int beta, SearchInfo& si, Move(&ppv)[MAX_PLY], bool allowNull); +int qsearch(Board& b, int ply, int alpha, int beta, SearchInfo& si, Move (&ppv)[MAX_PLY]); int staticExchangeEvaluation(const Board& b, const Move& m, int threshold = 0); int SEEMoveVal(const Board& b, const Move& m); @@ -112,6 +124,4 @@ int greatestTacticalGain(const Board& b); void iterativeDeepening(Board& b, SearchInfo& si, int timeLimit); -void printPV(Board b, int limit); - #endif \ No newline at end of file