Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
45 changes: 14 additions & 31 deletions src/tools/backendbench.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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() {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -260,20 +249,14 @@ void BackendBenchmark::Run() {
std::chrono::duration<double> 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<bool>(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;
Expand Down
101 changes: 101 additions & 0 deletions src/utils/clippy.cc
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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 <algorithm>
#include <iostream>
#include <sstream>
#include <vector>

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<std::string> 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<std::string> 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<std::string> 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
39 changes: 39 additions & 0 deletions src/utils/clippy.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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 <string>

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