From fa459c389ef158eed5ff1fd8d39994a4bf4032eb Mon Sep 17 00:00:00 2001 From: rn5f107s2 <121191597+rn5f107s2@users.noreply.github.com> Date: Sat, 21 Sep 2024 16:58:49 +0200 Subject: [PATCH] Add prettyprint (#101) Can be activated with "pretty" and deactivated again with "uci", off by default (Also works in bench, so something like ./build/Molybdenum pretty bench would run bench in pretty mode) bench 8493466 --- src/Position.h | 56 +++++++++++++++++ src/UCI.cpp | 6 +- src/UCI.h | 2 + src/Utility.h | 37 +++++++++++ src/search.cpp | 164 ++++++++++++++++++++++++++++++++++++++++++------- src/search.h | 6 ++ 6 files changed, 247 insertions(+), 24 deletions(-) diff --git a/src/Position.h b/src/Position.h index 0847e4e..bc48bee 100644 --- a/src/Position.h +++ b/src/Position.h @@ -3,6 +3,7 @@ #include #include + #include "Constants.h" #include "BitStuff.h" #include "Move.h" @@ -40,6 +41,8 @@ class Position { inline Piece pieceOn(int sq); inline u64 getOccupied(); template u64 getOccupied(); + inline std::string moveToSAN(Move move, u64 attacks); + inline int ambigious(Move move, u64 attacks); private: Stack capturedHistory; Stack plys50mrHistory; @@ -92,5 +95,58 @@ inline bool Position::isCapture(Move move) { return pieceLocations[to] != NO_PIECE; } +inline std::string Position::moveToSAN(Move move, u64 attacks) { + int from = extract(move); + int to = extract(move); + + if (extract(move) == CASTLING) + return from > to ? "0-0 " : "0-0-0 "; + + std::string piece; + std::string toSquare; + + if (typeOf(pieceOn(from)) != PAWN) { + piece += std::toupper(pieceToChar(typeOf(pieceOn(from)))); + + int a = ambigious(move, attacks); + + if (a & 1) + piece += char('a' + (fileOf(from))); + + if (a & 2) + piece += char('a' + (rankOf(from))); + + if (isCapture(move)) + piece += "x"; + } else { + if (isCapture(move) || extract(move) == ENPASSANT) { + piece += char('a' + (fileOf(from))); + piece += 'x'; + } + } + + + return piece + char('a' + (fileOf(to))) + char('1' + (rankOf(to))) + " "; +} + +inline int Position::ambigious(Move move, u64 attacks) { + int from = extract(move); + int to = extract(move); + Piece pc = pieceOn(from); + + u64 pieces = getPieces(sideToMove, PieceType(typeOf(pc))) ^ (1ULL << from); + u64 overlap = attacks & pieces; + + if (!overlap) + return 0; + + if (!(lFIleOf(to) & overlap)) + return 1; + + if (!(lRankOf(to) & overlap)) + return 2; + + return 3; +} #endif //MOLYBDENUM_POSITION_H diff --git a/src/UCI.cpp b/src/UCI.cpp index 835f019..df45723 100644 --- a/src/UCI.cpp +++ b/src/UCI.cpp @@ -44,13 +44,17 @@ void UCI::uci([[maybe_unused]] const std::string &args) { #endif std::cout << "uciok" << std::endl; - return; + prettyprint = false; } void UCI::isready([[maybe_unused]] const std::string &args) { std::cout << "readyok" << std::endl; } +void UCI::pretty([[maybe_unused]] const std::string &args) { + prettyprint = true; +} + void UCI::ucinewgame([[maybe_unused]] const std::string &args) { if (!threads.done()) return; diff --git a/src/UCI.h b/src/UCI.h index 5cacdc7..54c1902 100644 --- a/src/UCI.h +++ b/src/UCI.h @@ -40,6 +40,7 @@ class UCI { commands["eval"] = &UCI::eval; commands["stop"] = &UCI::stop; commands["bench"] = &UCI::bench; + commands["pretty"] = &UCI::pretty; commands["isready"] = &UCI::isready; commands["goPerft"] = &UCI::goPerft; commands["position"] = &UCI::position; @@ -63,6 +64,7 @@ class UCI { void eval(const std::string &args); void stop(const std::string &args); void bench(const std::string &args); + void pretty(const std::string &args); void goPerft(const std::string &args); void isready(const std::string &args); void position(const std::string &args); diff --git a/src/Utility.h b/src/Utility.h index c53f13c..4b8c74c 100644 --- a/src/Utility.h +++ b/src/Utility.h @@ -3,6 +3,7 @@ #include #include +#include #include "Constants.h" constexpr int MAX_STACK_SIZE = 6000; //Longer then the longest possible chess game @@ -140,4 +141,40 @@ inline int stringRoRule50(const std::string& rule50) { return std::stoi(rule50); } +enum PaddType { + Back, Front, Equal +}; + +enum ColorType { + Foreground, Background +}; + +template +inline void paddString(std::string &s, size_t c) { + if constexpr (PT != Equal) { + for (size_t size = s.length(); size < c; size++) + s = (PT == Front) ? (" " + s) : (s + " "); + + return; + } + + paddString(s, c / 2); + paddString(s, c ); +} + +template +inline void zeroPaddString(std::string &s, size_t c) { + for (size_t size = s.length(); size < c; size++) + s = PT ? ("0" + s) : (s + "0"); +} + +template +inline void colorString(std::string &s, int c) { + std::string reset = "\u001b[0m"; + std::string prefix[2] = {"\u001b[38;5;", "\u001b[48;5;"}; + std::string color = prefix[ct] + std::to_string(c) + "m"; + + s = color + s + reset; +} + #endif //MOLYBDENUM_UTILITY_H diff --git a/src/search.cpp b/src/search.cpp index 52cfd35..a7590c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -6,6 +6,7 @@ #include "Movepicker.h" #include "searchUtil.h" #include "thread.h" +#include "Movegen.h" #include #include @@ -16,6 +17,8 @@ Tune tune; #endif +bool prettyprint = false; + std::string SearchState::outputWDL(Position &pos) { for (int i = 0; i < pvLength[0]; i++) pos.makeMove(pvMoves[0][i]); @@ -57,6 +60,8 @@ int SearchState::iterativeDeepening(Position &pos, SearchTime &st, int maxDepth si.clear(); si.st = st; + //prettyInitial(); + for (int depth = 1; depth != maxDepth; depth++) { score = aspirationWindow(score, pos, si, depth); @@ -67,39 +72,43 @@ int SearchState::iterativeDeepening(Position &pos, SearchTime &st, int maxDepth continue; #ifndef DATAGEN - std::string uciOutput; - auto searchTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - si.st.searchStart).count(); - uciOutput += "info depth "; - uciOutput += std::to_string(depth); + if (prettyprint) + prettyPrint(pos, si, score, depth); + else { + std::string uciOutput; + auto searchTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - si.st.searchStart).count(); + uciOutput += "info depth "; + uciOutput += std::to_string(depth); - uciOutput += " seldepth "; - uciOutput += std::to_string(si.selDepth); + uciOutput += " seldepth "; + uciOutput += std::to_string(si.selDepth); - uciOutput += " currmove "; - uciOutput += moveToString(si.bestRootMove); + uciOutput += " currmove "; + uciOutput += moveToString(si.bestRootMove); - uciOutput += " score "; - uciOutput += abs(score) > MAXMATE ? "mate " : "cp "; - uciOutput += std::to_string(abs(score) > MAXMATE ? mateInPlies(score) : score); + uciOutput += " score "; + uciOutput += abs(score) > MAXMATE ? "mate " : "cp "; + uciOutput += std::to_string(abs(score) > MAXMATE ? mateInPlies(score) : score); - u64 nodeCount = thread->threads->nodes(); + u64 nodeCount = thread->threads->nodes(); - uciOutput += " nodes "; - uciOutput += std::to_string(nodeCount); + uciOutput += " nodes "; + uciOutput += std::to_string(nodeCount); - uciOutput += " time "; - uciOutput += std::to_string(searchTime); + uciOutput += " time "; + uciOutput += std::to_string(searchTime); - uciOutput += " nps "; - uciOutput += std::to_string((nodeCount / std::max(int(searchTime), 1)) * 1000); + uciOutput += " nps "; + uciOutput += std::to_string((nodeCount / std::max(int(searchTime), 1)) * 1000); - uciOutput += outputWDL(pos); + uciOutput += outputWDL(pos); - uciOutput += " pv "; - for (int i = 0; i < pvLength[0]; i++) - uciOutput += moveToString(pvMoves[0][i]) + " "; + uciOutput += " pv "; + for (int i = 0; i < pvLength[0]; i++) + uciOutput += moveToString(pvMoves[0][i]) + " "; - std::cout << uciOutput << std::endl; + std::cout << uciOutput << std::endl; + } if (stop(st, si)) { thread->threads->stop(); @@ -535,3 +544,112 @@ int SearchState::qsearch(int alpha, int beta, Position &pos, SearchInfo &si, Sea return bestScore; } + +void prettyInitial() { + std::cout << std::endl; + std::cout << " depth time nodes speed score" << std::endl; + std::cout << "_______________________________________________________________________________" << std::endl; +} + +// This is pretty print, not pretty code +void SearchState::prettyPrint(Position &pos, SearchInfo &si, int s, int de) { + auto searchTime = std::chrono::steady_clock::now() - si.st.searchStart; + auto searchTimeMinutes = std::chrono::duration_cast(searchTime).count(); + auto searchTimeSeconds = std::chrono::duration_cast(searchTime).count(); + auto searchTimeMilliseconds = std::chrono::duration_cast(searchTime).count(); + u64 nodes = thread->threads->nodes(); + u64 nodesPerMillisecond = (nodes / (searchTimeMilliseconds + 1)); + std::string temp = std::to_string(abs(s) % 100); + std::vector sanPV; + + zeroPaddString(temp, 2); + + for (int i = 0; i < pvLength[0]; i++) { + sanPV.push_back(pos.moveToSAN(pvMoves[0][i], getAttacks(typeOf(pos.pieceOn(extract(pvMoves[0][i]))), + extract(pvMoves[0][i]), + pos.getOccupied(), + pos.sideToMove ^ bool(i + 1)))); + pos.makeMove(pvMoves[0][i]); + } + + std::tuple wdl = pos.net.getWDL(pos.sideToMove); + + for (int i = pvLength[0] - 1; i >= 0; i--) + pos.unmakeMove(pvMoves[0][i]); + + bool flip = pvLength[0] & 1; + float w = flip ? std::get<0>(wdl) : std::get<2>(wdl); + float l = flip ? std::get<2>(wdl) : std::get<0>(wdl); + float d = std::get<1>(wdl); + + std::string pretty = ""; + std::string depth = std::to_string(de); + std::string seperator = "|"; + std::string selDepth = std::to_string(si.selDepth); + std::string megaNodes = std::to_string(nodes / 1000000); + std::string decimalNodes = std::to_string((nodes % 100000) / 10000); + std::string dot = "."; + std::string mn = "Mn"; + std::string minutes = std::to_string(searchTimeMinutes % 60); + std::string seconds = std::to_string(searchTimeSeconds % 60); + std::string milliseconds = std::to_string((searchTimeMilliseconds % 10000) / 1000); + std::string mnps = std::to_string(nodesPerMillisecond / 1000); + std::string decimalMnps = std::to_string(nodesPerMillisecond % 1000 / 100); + std::string scorePrefix = s > 0 ? "+" : "-"; + std::string matePrefix = abs(s) >= MAXMATE ? "M" : ""; + std::string score = std::to_string(abs(s) >= MAXMATE ? mateInPlies(s) : (abs(s) / 100)); + std::string scoreDecimal = abs(s) >= MAXMATE ? "" : ("." + temp); + std::string completeScore = scorePrefix + matePrefix + score + scoreDecimal; + std::string wPercentage = std::to_string(int(std::roundf(w * 100.0f))) + "% "; + std::string dPercentage = std::to_string(int(std::roundf(d * 100.0f))) + "% "; + std::string lPercentage = std::to_string(int(std::roundf(l * 100.0f))) + "% "; + + paddString(depth , 3); + paddString(selDepth , 3); + paddString(megaNodes, 5); + paddString(minutes, 5); + zeroPaddString(seconds, 2); + paddString(mnps, 6); + paddString(completeScore, 11); + paddString(completeScore, completeScore.size() + 6); + paddString(wPercentage, 5); + paddString(dPercentage, 5); + paddString(lPercentage, 5); + + wPercentage = "W:" + wPercentage; + dPercentage = "D:" + dPercentage; + lPercentage = "L:" + lPercentage; + + paddString(lPercentage, 13); + + int evalColorTones[11] = {88, 124, 196, 202, 208, 229, 106, 64, 76, 28, 22}; + int evalColorIndex = std::clamp(5+ (s / 25), 0, 10); + + colorString(wPercentage, 15); + colorString(dPercentage, 8); + colorString(lPercentage, 16); + colorString(completeScore, evalColorTones[evalColorIndex]); + + colorString(sanPV[0], 15); + sanPV[0] = "\033[1m" + sanPV[0] + "\033[0m"; + + for (size_t i = 1; i < sanPV.size(); i++) + colorString(sanPV[i], 242); + + // Eval bar for wdl? Current issue inability to find a way to display percentages on top of bars in a way that make sense + + pretty += depth + seperator + selDepth; + pretty += minutes + "m " + seconds + "." + milliseconds + "s "; + pretty += megaNodes + dot + decimalNodes + mn; + pretty += mnps + dot + decimalMnps + "Mn/s"; + + colorString(pretty, 246); + + pretty += completeScore; + pretty += wPercentage + dPercentage + lPercentage; + + for (auto &move : sanPV) + pretty += move; + + std::cout << pretty << std::endl; +} diff --git a/src/search.h b/src/search.h index b911be4..e687d8e 100644 --- a/src/search.h +++ b/src/search.h @@ -17,6 +17,8 @@ extern Tune tune; #endif +extern bool prettyprint; + class Thread; struct SearchInfo { @@ -83,6 +85,8 @@ class SearchState { Thread* thread; SearchInfo si; + + void prettyPrint(Position &pos, SearchInfo &si, int score, int depth); }; inline std::array initReductions() { @@ -112,4 +116,6 @@ bool stop(SearchTime &st, SearchInfo &si) { || (st.limit == Nodes && si.nodeCount >= st.nodeLimit); } +void prettyInitial(); + #endif //MOLYBDENUM_SEARCH_H