From 7de6bab6de692f134f32e7b33310e36ec3cad8e4 Mon Sep 17 00:00:00 2001 From: borg323 Date: Tue, 25 Nov 2025 19:01:23 +0200 Subject: [PATCH] make clippy globally available --- meson.build | 1 + src/tools/backendbench.cc | 45 ++++++----------- src/utils/clippy.cc | 101 ++++++++++++++++++++++++++++++++++++++ src/utils/clippy.h | 39 +++++++++++++++ 4 files changed, 155 insertions(+), 31 deletions(-) create mode 100644 src/utils/clippy.cc create mode 100644 src/utils/clippy.h diff --git a/meson.build b/meson.build index aa87cf88cf..cc5a92672f 100644 --- a/meson.build +++ b/meson.build @@ -196,6 +196,7 @@ files += [ 'src/tools/describenet.cc', 'src/tools/leela2onnx.cc', 'src/tools/onnx2leela.cc', + 'src/utils/clippy.cc', 'src/utils/histogram.cc', 'src/utils/numa.cc', 'src/utils/weights_adapter.cc', diff --git a/src/tools/backendbench.cc b/src/tools/backendbench.cc index 88a131aff3..1147f72397 100644 --- a/src/tools/backendbench.cc +++ b/src/tools/backendbench.cc @@ -31,6 +31,7 @@ #include "neural/register.h" #include "neural/shared_params.h" #include "search/classic/node.h" +#include "utils/clippy.h" #include "utils/optionsparser.h" namespace lczero { @@ -52,29 +53,6 @@ const OptionId kHeaderOnlyOnceId{"header-only-once", "", const OptionId kFenId{"fen", "", "Benchmark initial position FEN."}; const OptionId kClippyId{"clippy", "", "Enable helpful assistant."}; - -void Clippy(std::string title, std::string msg3, std::string best3, - std::string msg2, std::string best2, std::string msg, - std::string best) { - std::cout << " __" << std::endl; - std::cout << " / \\" << std::endl; - std::cout << " | | " << std::string(title.length() + 2, '_') << std::endl; - std::cout << " + + | " << std::string(title.length() + 1, ' ') << "|" - << std::endl; - std::cout << "(@)(@) _| " << title << " |" << std::endl; - std::cout << " | | \\ " << std::string(6, ' ') << msg3 - << std::string(4 - best3.length(), ' ') << best3 - << std::string(title.length() - 33, ' ') << "|" << std::endl; - std::cout << " || |/ | " << std::string(6, ' ') << msg2 - << std::string(4 - best2.length(), ' ') << best2 - << std::string(title.length() - 33, ' ') << "|" << std::endl; - std::cout << " || || | " << std::string(6, ' ') << msg - << std::string(4 - best.length(), ' ') << best - << std::string(title.length() - 33, ' ') << "|" << std::endl; - std::cout << " |\\_/| |" << std::string(title.length() + 2, '_') << "|" - << std::endl; - std::cout << " \\___/" << std::endl; -} } // namespace void BackendBenchmark::Run() { @@ -128,6 +106,17 @@ void BackendBenchmark::Run() { int best = 1; int best2 = 1; int best3 = 1; + auto clippy_msg = [&](bool fin) { + // clang-format off + std::string s = + "Recommended minibatch-size for this net" + + std::string(fin ? "" : " (so far)") + ":\n" + + "1s/move (Bullet): " + std::to_string(best3) + "\n" + + "15s/move (Rapid): " + std::to_string(best2) + "\n" + + "3min/move (Tournament): " + std::to_string(best); + // clang-format on + return s; + }; float best_nps = 0.0f; float best_nps2 = 0.0f; float best_nps3 = 0.0f; @@ -260,20 +249,14 @@ void BackendBenchmark::Run() { std::chrono::duration time = std::chrono::steady_clock::now() - *pending; if (time.count() > 10) { - Clippy("Recommended minibatch-size for this net (so far):", - "1s/move (Bullet): ", std::to_string(best3), - "15s/move (Rapid): ", std::to_string(best2), - "3min/move (Tournament): ", std::to_string(best)); + Clippy(clippy_msg(false)); pending.reset(); } } } } if (option_dict.Get(kClippyId)) { - Clippy("Recommended minibatch-size for this net:", - "1s/move (Bullet): ", std::to_string(best3), - "15s/move (Rapid): ", std::to_string(best2), - "3min/move (Tournament): ", std::to_string(best)); + Clippy(clippy_msg(true)); } } catch (Exception& ex) { std::cerr << ex.what() << std::endl; diff --git a/src/utils/clippy.cc b/src/utils/clippy.cc new file mode 100644 index 0000000000..74827852d4 --- /dev/null +++ b/src/utils/clippy.cc @@ -0,0 +1,101 @@ +/* + This file is part of Leela Chess Zero. + Copyright (C) 2025 The LCZero Authors + + Leela Chess is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Leela Chess is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Leela Chess. If not, see . + + Additional permission under GNU GPL version 3 section 7 + + If you modify this Program, or any covered work, by linking or + combining it with NVIDIA Corporation's libraries from the NVIDIA CUDA + Toolkit and the NVIDIA CUDA Deep Neural Network library (or a + modified version of those libraries), containing parts covered by the + terms of the respective license agreement, the licensors of this + Program grant you additional permission to convey the resulting work. +*/ + +#include "utils/clippy.h" + +#include +#include +#include +#include + +namespace lczero { + +// This is based on a vibe coded first draft, handle with care. +void Clippy(const std::string& formatted) { + // Split into lines. + std::vector lines; + std::istringstream ss(formatted); + std::string ln; + while (std::getline(ss, ln)) lines.push_back(ln); + + size_t maxlen = 0; + for (const auto& l : lines) maxlen = std::max(maxlen, l.size()); + // content padding: one space on each side + const size_t bubble_inner = + maxlen + 2; // inner width used for top/side calculations + + // Build speech bubble: + std::vector bubble; + bubble.push_back(" " + std::string(bubble_inner, '_') + " "); + bubble.push_back((lines.size() == 1 ? "_/" : " /") + + std::string(bubble_inner, ' ') + "\\"); + for (size_t i = 0; const auto& l : lines) { + std::string header; + header = i == lines.size() / 2 - 1 + (lines.size() > 4) ? "_| " + : i == lines.size() / 2 + (lines.size() > 4) ? "\\ " + : " | "; + bubble.push_back(header + l + + std::string(bubble_inner - 2 - l.size(), ' ') + " |"); + i++; + } + bubble.push_back(" \\" + std::string(bubble_inner, '_') + "/"); + + // Clippy ASCII art. + std::vector clippy = {" __", " / \\", " | |", " + +", + "(@)(@)", " | |", " || |/", " || ||", + " |\\_/|", " \\___/"}; + size_t clippy_width = 0; + for (const auto& c : clippy) clippy_width = std::max(clippy_width, c.size()); + clippy_width++; // One space extra padding. + const size_t rows = std::max(clippy.size(), bubble.size()); + // Pad one side's top as needed to center spech bubble to Clippy's edge. + if (clippy.size() < bubble.size()) { + clippy.insert(clippy.begin(), (bubble.size() - clippy.size() + 1) / 2, ""); + } else if (clippy.size() > bubble.size()) { + bubble.insert(bubble.begin(), + (clippy.size() - bubble.size()) / 2 + (lines.size() <= 4), + ""); + } + // Print side-by-side, aligning rows. + for (size_t i = 0; i < rows; ++i) { + std::string left; + if (i < clippy.size()) { + left = clippy[i]; + } + // pad left to fixed width + if (left.size() < clippy_width) { + left += std::string(clippy_width - left.size(), ' '); + } + std::string right; + if (i < bubble.size()) { + right = bubble[i]; + } + std::cout << left << right << std::endl; + } +} + +} // namespace lczero diff --git a/src/utils/clippy.h b/src/utils/clippy.h new file mode 100644 index 0000000000..b3f4ffd1e2 --- /dev/null +++ b/src/utils/clippy.h @@ -0,0 +1,39 @@ +/* + This file is part of Leela Chess Zero. + Copyright (C) 2025 The LCZero Authors + + Leela Chess is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Leela Chess is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Leela Chess. If not, see . + + Additional permission under GNU GPL version 3 section 7 + + If you modify this Program, or any covered work, by linking or + combining it with NVIDIA Corporation's libraries from the NVIDIA CUDA + Toolkit and the NVIDIA CUDA Deep Neural Network library (or a + modified version of those libraries), containing parts covered by the + terms of the respective license agreement, the licensors of this + Program grant you additional permission to convey the resulting work. +*/ + +#pragma once + +#include + +namespace lczero { + +// Clippy accepts a pre-formatted multi-line string. It will size the +// speech bubble to the widest line, and aligns the bubble so that it +// appears to originate from Clippy's edge. +void Clippy(const std::string& formatted); + +} // namespace lczero