Skip to content

Commit

Permalink
Split UCI into UCIEngine and Engine
Browse files Browse the repository at this point in the history
This is another refactor which aims to decouple uci from stockfish. A new engine
class manages all engine related logic and uci is a "small" wrapper around it.

In the future we should also try to remove the need for the Position object in
the uci and replace the options with an actual options struct instead of using a
map. Also convert the std::string's in the Info structs a string_view.

closes official-stockfish#5147

No functional change
  • Loading branch information
Disservin committed Apr 3, 2024
1 parent 0716b84 commit e9f708b
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 151 deletions.
4 changes: 2 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench
SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
misc.cpp movegen.cpp movepick.cpp position.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp
nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp

HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \
nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \
nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \
nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \
search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h

OBJS = $(notdir $(SRCS:.cpp=.o))

Expand Down
153 changes: 153 additions & 0 deletions src/engine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish 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.
Stockfish 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "engine.h"

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <deque>
#include <memory>
#include <optional>
#include <sstream>
#include <vector>

#include "benchmark.h"
#include "evaluate.h"
#include "movegen.h"
#include "nnue/network.h"
#include "nnue/nnue_common.h"
#include "perft.h"
#include "position.h"
#include "search.h"
#include "syzygy/tbprobe.h"
#include "types.h"
#include "ucioption.h"

namespace Stockfish {

namespace NN = Eval::NNUE;

constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

Engine::Engine(std::string path) :
binaryDirectory(CommandLine::get_binary_directory(path)),
states(new std::deque<StateInfo>(1)),
networks(NN::Networks(
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
Tune::init(options);
pos.set(StartFEN, false, &states->back());
}

void Engine::go(const Search::LimitsType& limits) {
verify_networks();

if (limits.perft)
{
perft(pos.fen(), limits.perft, options["UCI_Chess960"]);
return;
}

threads.start_thinking(options, pos, states, limits);
}
void Engine::stop() { threads.stop = true; }

void Engine::search_clear() {
wait_for_search_finished();

tt.clear(options["Threads"]);
threads.clear();

// @TODO wont work multiple instances
Tablebases::init(options["SyzygyPath"]); // Free mapped files
}

void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); }

void Engine::set_position(const std::string& fen, const std::vector<std::string>& moves) {
// Drop the old state and create a new one
states = StateListPtr(new std::deque<StateInfo>(1));
pos.set(fen, options["UCI_Chess960"], &states->back());

for (const auto& move : moves)
{
auto m = UCIEngine::to_move(pos, move);

if (m == Move::none())
break;

states->emplace_back();
pos.do_move(m, states->back());
}
}

// modifiers

void Engine::resize_threads() { threads.set({options, threads, tt, networks}); }

void Engine::set_tt_size(size_t mb) {
wait_for_search_finished();
tt.resize(mb, options["Threads"]);
}

void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }

// network related

void Engine::verify_networks() {
networks.big.verify(options["EvalFile"]);
networks.small.verify(options["EvalFileSmall"]);
}

void Engine::load_networks() {
networks.big.load(binaryDirectory, options["EvalFile"]);
networks.small.load(binaryDirectory, options["EvalFileSmall"]);
}

void Engine::load_big_network(const std::string& file) { networks.big.load(binaryDirectory, file); }

void Engine::load_small_network(const std::string& file) {
networks.small.load(binaryDirectory, file);
}

void Engine::save_network(const std::pair<std::optional<std::string>, std::string> files[2]) {
networks.big.save(files[0].first);
networks.small.save(files[1].first);
}

// utility functions

OptionsMap& Engine::get_options() { return options; }

uint64_t Engine::nodes_searched() const { return threads.nodes_searched(); }

void Engine::trace_eval() {
StateListPtr trace_states(new std::deque<StateInfo>(1));
Position p;
p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back());

verify_networks();

sync_cout << "\n" << Eval::trace(p, networks) << sync_endl;
}

}
85 changes: 85 additions & 0 deletions src/engine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish 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.
Stockfish 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef ENGINE_H_INCLUDED
#define ENGINE_H_INCLUDED

#include "misc.h"
#include "nnue/network.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"

namespace Stockfish {

class Engine {
public:
Engine(std::string path = "");
~Engine() { wait_for_search_finished(); }

// non blocking call to start searching
void go(const Search::LimitsType&);
// non blocking call to stop searching
void stop();

// blocking call to wait for search to finish
void wait_for_search_finished();
// set a new position
void set_position(const std::string& fen, const std::vector<std::string>& moves);

// modifiers

void resize_threads();
void set_tt_size(size_t mb);
void set_ponderhit(bool);
// clears the search
void search_clear();

// network related

void verify_networks();
void load_networks();
void load_big_network(const std::string& file);
void load_small_network(const std::string& file);
void save_network(const std::pair<std::optional<std::string>, std::string> files[2]);

// utility functions

void trace_eval();
// nodes since last search clear
uint64_t nodes_searched() const;
OptionsMap& get_options();

private:
const std::string binaryDirectory;

Position pos;
StateListPtr states;

OptionsMap options;
ThreadPool threads;
TranspositionTable tt;
Eval::NNUE::Networks networks;
};

} // namespace Stockfish


#endif // #ifndef ENGINE_H_INCLUDED
4 changes: 2 additions & 2 deletions src/evaluate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) {

Value v = networks.big.evaluate(pos, false);
v = pos.side_to_move() == WHITE ? v : -v;
ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v, pos) << " (white side)\n";
ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n";

v = evaluate(networks, pos, VALUE_ZERO);
v = pos.side_to_move() == WHITE ? v : -v;
ss << "Final evaluation " << 0.01 * UCI::to_cp(v, pos) << " (white side)";
ss << "Final evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)";
ss << " [with scaled NNUE, ...]";
ss << "\n";

Expand Down
5 changes: 1 addition & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ int main(int argc, char* argv[]) {
Bitboards::init();
Position::init();

UCI uci(argc, argv);

Tune::init(uci.options);

UCIEngine uci(argc, argv);
uci.loop();

return 0;
Expand Down
31 changes: 18 additions & 13 deletions src/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -723,13 +723,9 @@ void bind_this_thread(size_t idx) {
#define GETCWD getcwd
#endif

CommandLine::CommandLine(int _argc, char** _argv) :
argc(_argc),
argv(_argv) {
std::string pathSeparator;

// Extract the path+name of the executable binary
std::string argv0 = argv[0];
std::string CommandLine::get_binary_directory(std::string argv0) {
std::string pathSeparator;

#ifdef _WIN32
pathSeparator = "\\";
Expand All @@ -745,15 +741,11 @@ CommandLine::CommandLine(int _argc, char** _argv) :
#endif

// Extract the working directory
workingDirectory = "";
char buff[40000];
char* cwd = GETCWD(buff, 40000);
if (cwd)
workingDirectory = cwd;
auto workingDirectory = CommandLine::get_working_directory();

// Extract the binary directory path from argv0
binaryDirectory = argv0;
size_t pos = binaryDirectory.find_last_of("\\/");
auto binaryDirectory = argv0;
size_t pos = binaryDirectory.find_last_of("\\/");
if (pos == std::string::npos)
binaryDirectory = "." + pathSeparator;
else
Expand All @@ -762,6 +754,19 @@ CommandLine::CommandLine(int _argc, char** _argv) :
// Pattern replacement: "./" at the start of path is replaced by the working directory
if (binaryDirectory.find("." + pathSeparator) == 0)
binaryDirectory.replace(0, 1, workingDirectory);

return binaryDirectory;
}

std::string CommandLine::get_working_directory() {
std::string workingDirectory = "";
char buff[40000];
char* cwd = GETCWD(buff, 40000);
if (cwd)
workingDirectory = cwd;

return workingDirectory;
}


} // namespace Stockfish
10 changes: 6 additions & 4 deletions src/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,15 @@ void bind_this_thread(size_t idx);

struct CommandLine {
public:
CommandLine(int, char**);
CommandLine(int _argc, char** _argv) :
argc(_argc),
argv(_argv) {}

static std::string get_binary_directory(std::string argv0);
static std::string get_working_directory();

int argc;
char** argv;

std::string binaryDirectory; // path of the executable directory
std::string workingDirectory; // path of the working directory
};

namespace Utility {
Expand Down
4 changes: 2 additions & 2 deletions src/nnue/nnue_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void format_cp_compact(Value v, char* buffer, const Position& pos) {

buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');

int cp = std::abs(UCI::to_cp(v, pos));
int cp = std::abs(UCIEngine::to_cp(v, pos));
if (cp >= 10000)
{
buffer[1] = '0' + cp / 10000;
Expand Down Expand Up @@ -92,7 +92,7 @@ void format_cp_compact(Value v, char* buffer, const Position& pos) {
// Converts a Value into pawns, always keeping two decimals
void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) {

const double pawns = std::abs(0.01 * UCI::to_cp(v, pos));
const double pawns = std::abs(0.01 * UCIEngine::to_cp(v, pos));

stream << (v < 0 ? '-'
: v > 0 ? '+'
Expand Down
2 changes: 1 addition & 1 deletion src/perft.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ uint64_t perft(Position& pos, Depth depth) {
pos.undo_move(m);
}
if (Root)
sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
sync_cout << UCIEngine::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
}
return nodes;
}
Expand Down
Loading

0 comments on commit e9f708b

Please sign in to comment.