From 84b45a574fe0193006c1b254b3a261f0d5e1c26a Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Sat, 8 Jun 2024 14:58:04 +0200 Subject: [PATCH] improved uci commands, minor changes --- CMakeLists.txt | 27 +++----- dependencies/search_board.hpp | 5 -- dependencies/uci.hpp | 2 + .../feature_transformer/bias.bin | Bin .../feature_transformer/weights.bin | Bin .../layer_2/bias.bin | Bin .../layer_2/weights.bin | Bin .../layer_3/bias.bin | Bin .../layer_3/weights.bin | Bin .../layer_4/bias.bin | Bin .../layer_4/weights.bin | 0 readme.md | 18 ++---- src/Bread_engine_core.cpp | 11 ++-- src/search_board.cpp | 20 ------ src/uci.cpp | 60 +++++++++++++----- 15 files changed, 65 insertions(+), 78 deletions(-) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/feature_transformer/bias.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/feature_transformer/weights.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/layer_2/bias.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/layer_2/weights.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/layer_3/bias.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/layer_3/weights.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/layer_4/bias.bin (100%) rename neural_networks/{model_NNUE_2-40960_2x256_8_8_1_quant_layers => model_NNUE_2-40960_2x256_32_32_1_quant_layers}/layer_4/weights.bin (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23daa03..b178e1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.20) -project(bread_engine) +project(bread_engine VERSION 0.0.2) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -9,20 +9,9 @@ set(bread_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/utils) set(bread_DEPENDENCIES ${CMAKE_CURRENT_SOURCE_DIR}/dependencies) set(bread_NEURAL_NETWORKS ${CMAKE_CURRENT_SOURCE_DIR}/neural_networks) -option(bread_USE_TB "use endgame tablebase" ON) -option(bread_EMBED_NN "embed neural network in the executable" ON) - -message(STATUS "use endgame tablebase: ${bread_USE_TB}") -message(STATUS "embed neural network in executable: ${bread_EMBED_NN}") - # this path is relative to the executable. It can be changed if needed set(bread_NNUE_MODEL_PATH - "${bread_NEURAL_NETWORKS}/model_NNUE_2-40960_2x256_8_8_1_quant_layers" -) - -# this path is relative to the executable. It can be changed if needed -set(bread_TB_PATH - "3-4-5_pieces_Syzygy/3-4-5" + "${bread_NEURAL_NETWORKS}/model_NNUE_2-40960_2x256_32_32_1_quant_layers" ) set(bread_NN_HEADER_PATH @@ -33,11 +22,9 @@ set(bread_INCLUDE_DIRS ${bread_DEPENDENCIES} ${bread_DEPENDENCIES}/extern ${bread_DEPENDENCIES}/unit_tests + ${bread_DEPENDENCIES}/extern/Fathom/src ) -if(bread_USE_TB) - set(bread_INCLUDE_DIRS ${bread_INCLUDE_DIRS} ${bread_DEPENDENCIES}/extern/Fathom/src) -endif(bread_USE_TB) set(bread_SOURCE ${bread_SRC}/search_board.cpp @@ -45,11 +32,9 @@ set(bread_SOURCE ${bread_SRC}/nnue/nnue_board.cpp ${bread_SRC}/transposition_table.cpp ${bread_SRC}/bread_engine_core.cpp + ${bread_DEPENDENCIES}/extern/Fathom/src/tbprobe.cpp ) -if(bread_USE_TB) - set(bread_SOURCE ${bread_SOURCE} ${bread_DEPENDENCIES}/extern/Fathom/src/tbprobe.cpp) -endif(bread_USE_TB) function(bread_configure bread_TARGET) @@ -59,12 +44,16 @@ function(bread_configure bread_TARGET) target_include_directories(${bread_TARGET} PRIVATE ${bread_INCLUDE_DIRS}) + target_compile_definitions(${bread_TARGET} PRIVATE bread_VERSION="${CMAKE_PROJECT_VERSION}") + target_compile_definitions(${bread_TARGET} PRIVATE bread_USE_TB=$) target_compile_definitions(${bread_TARGET} PRIVATE bread_EMBED_NN=$) if (TARGET generate_neural_net_header) target_compile_definitions(${bread_TARGET} PRIVATE bread_EMBED_NN=false) + else() + target_compile_definitions(${bread_TARGET} PRIVATE bread_EMBED_NN=true) endif() target_compile_definitions(${bread_TARGET} PRIVATE bread_TB_PATH="${bread_TB_PATH}") diff --git a/dependencies/search_board.hpp b/dependencies/search_board.hpp index 8687df7..934b5d9 100644 --- a/dependencies/search_board.hpp +++ b/dependencies/search_board.hpp @@ -2,17 +2,12 @@ #include "chess.hpp" #include "piece_square_tables.hpp" - -#if bread_USE_TB #include "tbprobe.hpp" -#endif #include class SearchBoard: public chess::Board { public: - SearchBoard(); - SearchBoard(std::string_view fen); virtual void synchronize() = 0; diff --git a/dependencies/uci.hpp b/dependencies/uci.hpp index bd6d371..4353a85 100644 --- a/dependencies/uci.hpp +++ b/dependencies/uci.hpp @@ -21,6 +21,8 @@ class UCIAgent { std::vector split_string(std::string str); + void process_setoption(std::vector command); + void process_position(std::vector command); void process_go(std::vector command); diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/feature_transformer/bias.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/feature_transformer/bias.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/feature_transformer/bias.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/feature_transformer/bias.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/feature_transformer/weights.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/feature_transformer/weights.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/feature_transformer/weights.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/feature_transformer/weights.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_2/bias.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_2/bias.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_2/bias.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_2/bias.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_2/weights.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_2/weights.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_2/weights.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_2/weights.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_3/bias.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_3/bias.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_3/bias.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_3/bias.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_3/weights.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_3/weights.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_3/weights.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_3/weights.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_4/bias.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_4/bias.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_4/bias.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_4/bias.bin diff --git a/neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_4/weights.bin b/neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_4/weights.bin similarity index 100% rename from neural_networks/model_NNUE_2-40960_2x256_8_8_1_quant_layers/layer_4/weights.bin rename to neural_networks/model_NNUE_2-40960_2x256_32_32_1_quant_layers/layer_4/weights.bin diff --git a/readme.md b/readme.md index 3bbecf0..62eaead 100644 --- a/readme.md +++ b/readme.md @@ -2,26 +2,18 @@ Bread engine is a chess engine written in c++. I started working on it in 2021, and only just finished. There is still a lot of room for improvement, but the engine is quite strong (for humans, at least). It uses NNUE (efficiently updatable neural network) to evaluate positions, as well as minimax search. Bread engine does not have a GUI built in, however it supports the uci protocol, you can therefore run it on any chess GUI. -The engine has two build options: - -- Option 1: use endgame tablebase -If this option is on, the engine will search for a folder next to the executable, named `3-4-5_pieces_Syzygy` containing the tablebase. You can download the folder [here](https://chess.massimilianogoi.com/download/tablebases/Syzygy3-4-5/). Thanks to Massimiliano Goi for sharing this tablebase. - -- Option 2: embed neural network in executable. -If this option is off, the engine will search for the folder `model_NNUE_2-40960_2x256_8_8_1_quant_layers` next to the executable. - # Installation -For windows, you can find precompiled binaries in the release section. There are 4 of them, one for each option combination. +For windows, you can find the precompiled binary in the release section. +Please note that the engine requires a cpu with AVX2 support. -If you want to change the tablebase or neural network path, you can build the engine yourself using cmake. - -Once you have the engine installed, you can run the executable, and write the following commands: +Once you have the engine installed, you can run the executable, and write the following commands one after the other: ```console uci position startpos -go wtime 10000 btime 10000 winc 1000 binc 1000 +go movetime 5000 ``` + and the engine will return the best move. To set a position with a specific fen, you can use `position fen 5rkq/3prp1p/5RpP/p1p5/5Q2/1B4P1/P4PK1/8 w - a6 0 51` for instance. You can find more details on the [uci protocol](https://www.wbec-ridderkerk.nl/html/UCIProtocol.html) webpage. Please note that only the main commands are supported. Besides the main engine build, you can also build some utility code to: diff --git a/src/Bread_engine_core.cpp b/src/Bread_engine_core.cpp index 84a663d..3a0ed6c 100644 --- a/src/Bread_engine_core.cpp +++ b/src/Bread_engine_core.cpp @@ -242,13 +242,11 @@ chess::Move Engine::iterative_deepening(int time_limit_, int min_depth_=4, int m engine_color = (inner_board.sideToMove() == chess::Color::WHITE) ? 1: -1; - #if bread_USE_TB chess::Move tb_move; if (inner_board.probe_dtz(tb_move)){ std::cout << "bestmove " << chess::uci::moveToUci(tb_move) << std::endl; return tb_move; }; - #endif while (true){ search_depth = current_depth; @@ -438,13 +436,11 @@ float Engine::negamax(int depth, int color, float alpha, float beta){ float Engine::qsearch(float alpha, float beta, int color, int depth){ nodes++; - #if bread_USE_TB // tablebase probe float wdl_eval; if (inner_board.probe_wdl(wdl_eval)){ return wdl_eval; } - #endif // this is recomputed when qsearch is called the first time. Performance loss is probably low. uint64_t zobrist_hash = inner_board.hash(); @@ -461,7 +457,8 @@ float Engine::qsearch(float alpha, float beta, int color, int depth){ // we can already check for cutoff if (stand_pat >= beta){ - return stand_pat; + // return stand_pat; + return beta; } } else if (transposition->flag == TFlag::EXACT){ // this means that it is either a higher depth search, in which case we can return, @@ -493,7 +490,8 @@ float Engine::qsearch(float alpha, float beta, int color, int depth){ stand_pat = inner_board.evaluate(); transposition_table.store(zobrist_hash, stand_pat, 0, NO_MOVE, TFlag::EXACT, static_cast(inner_board.fullMoveNumber())); if (stand_pat >= beta){ - return stand_pat; + // return stand_pat; + return beta; } if (depth == 0){ @@ -523,6 +521,7 @@ float Engine::qsearch(float alpha, float beta, int color, int depth){ if (pos_eval > alpha){ alpha = pos_eval; if (alpha >= beta){ // only check for cutoffs when alpha gets updated. + // return alpha; return beta; } } diff --git a/src/search_board.cpp b/src/search_board.cpp index be759bd..92ac4cc 100644 --- a/src/search_board.cpp +++ b/src/search_board.cpp @@ -1,19 +1,6 @@ #include "search_board.hpp" -SearchBoard::SearchBoard(): chess::Board::Board(){ - #if bread_USE_TB - tb_init(bread_TB_PATH); - #endif -}; - -SearchBoard::SearchBoard(std::string_view fen): chess::Board::Board(fen) { - #if bread_USE_TB - tb_init(bread_TB_PATH); - #endif -}; - bool SearchBoard::probe_wdl(float& eval){ - #if bread_USE_TB if (occ().count() > 5){ return false; } @@ -39,13 +26,9 @@ bool SearchBoard::probe_wdl(float& eval){ return true; } return false; - #else - return false; - #endif } bool SearchBoard::probe_dtz(chess::Move& move){ - #if bread_USE_TB if (occ().count() > TB_LARGEST){ return false; } @@ -101,7 +84,4 @@ bool SearchBoard::probe_dtz(chess::Move& move){ move.setScore(0); } return true; - #else - return false; - #endif } diff --git a/src/uci.cpp b/src/uci.cpp index 9027e3a..92a978b 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -5,12 +5,16 @@ bool UCIAgent::process_uci_command(std::string command){ std::string first = parsed_command[0]; if (first == "uci"){ - std::cout << "id name Bread Engine\n"; - std::cout << "id author Nonlinear\n"; - std::cout << "uciok\n"; + std::cout << "id name Bread Engine " << bread_VERSION << std::endl; + std::cout << "id author Nonlinear" << std::endl; + std::cout << std::endl; + std::cout << "option name SyzygyPath type string default " << std::endl; + std::cout << "uciok" << std::endl; + } else if (first == "setoption"){ + process_setoption(parsed_command); } else if (first == "isready"){ - std::cout << "readyok\n"; + std::cout << "readyok" << std::endl; } else if (first == "ucinewgame"){ engine.transposition_table.clear(); @@ -30,9 +34,7 @@ bool UCIAgent::process_uci_command(std::string command){ } else if (first == "quit"){ interrupt_if_searching(); - #if bread_USE_TB tb_free(); - #endif return 0; } else { @@ -54,6 +56,25 @@ std::vector UCIAgent::split_string(std::string str){ return split; } +void UCIAgent::process_setoption(std::vector command){ + std::string option_name = command[2]; + std::transform(option_name.begin(), option_name.end(), option_name.begin(), [](unsigned char c){ return std::tolower(c); }); + if (option_name == "syzygypath"){ + std::string path = command[4]; + for (int i = 5; i < command.size(); i++){ + path += " " + command[i]; + } + bool tb_success = tb_init(path.c_str()); + if (!tb_success){ + std::cout << "info string tablebase initialisation failed" << std::endl; + } else if (TB_LARGEST == 0){ + std::cout << "info string no tablebase loaded" << std::endl; + } else { + std::cout << "info string tablebase loaded" << std::endl; + } + } +} + void UCIAgent::process_position(std::vector command){ interrupt_if_searching(); @@ -81,6 +102,7 @@ void UCIAgent::process_position(std::vector command){ } void UCIAgent::process_go(std::vector command){ + bool exact_movetime = false; int wtime = -1; int btime = -1; int inc = 0; @@ -96,18 +118,26 @@ void UCIAgent::process_go(std::vector command){ } else if (token == "movestogo"){ movestogo = std::stoi(command[i+1]); // normally, handle depth, node, mate, movetime command now + } else if (token == "movetime"){ + exact_movetime = true; + think_time = std::stoi(command[i+1]); + break; } else if (token == "infinite"){ - wtime = btime = INT32_MAX; + exact_movetime = true; + think_time = INT32_MAX; + break; } } - if ((wtime == -1) || (btime == -1)){ - std::cout << "no time specified\n"; - return; - }; - - int engine_time_left = (engine.inner_board.sideToMove() == chess::Color::WHITE) ? wtime: btime; - - think_time = static_cast(engine.get_think_time(engine_time_left, num_moves_out_of_book, movestogo, inc)); + if (!exact_movetime){ + if ((wtime == -1) || (btime == -1)){ + std::cout << "no time specified\n"; + return; + }; + int engine_time_left = (engine.inner_board.sideToMove() == chess::Color::WHITE) ? wtime: btime; + + think_time = static_cast(engine.get_think_time(engine_time_left, num_moves_out_of_book, movestogo, inc)); + } + if (command[1] == "ponder"){ main_search_thread = std::thread(&Engine::iterative_deepening, &engine, INT32_MAX, 0, 25); // infinite search time