stellar

Stellar - UCI Chess engine written in C++20
git clone git://git.dimitrijedobrota.com/stellar.git
Log | Files | Refs | README | LICENSE

commit c36cee0f9d0031a7c068cab56947327f2a199c1e
parent 90991d29e87f77efec7d9cafb2df36ba4c5d4dfd
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Mon, 28 Aug 2023 18:24:59 +0200

Rewrite uci logic in c++

Diffstat:
MCMakeLists.txt | 2+-
Msrc/board/CMakeLists.txt | 2+-
Msrc/engine/CMakeLists.txt | 1+
Msrc/engine/engine.cpp | 197+++++++------------------------------------------------------------------------
Asrc/engine/engine.hpp | 11+++++++++++
Msrc/engine/transposition.cpp | 4++--
Msrc/engine/transposition.hpp | 6+++---
Asrc/engine/uci.cpp | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/engine/uci.hpp | 32++++++++++++++++++++++++++++++++
Msrc/move/move.hpp | 2+-
Msrc/move/movelist.hpp | 2++
Msrc/utils/utils.hpp | 3++-
12 files changed, 179 insertions(+), 190 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Stellar - VERSION 0.0.22 + VERSION 0.0.23 DESCRIPTION "Chess engine written in C" HOMEPAGE_URL https://git.dimitrijedobrota.com/stellar.git LANGUAGES C CXX diff --git a/src/board/CMakeLists.txt b/src/board/CMakeLists.txt @@ -3,8 +3,8 @@ add_library(board OBJECT ) target_link_libraries(board - PRIVATE piece PRIVATE utils + PUBLIC piece PUBLIC random ) diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable(engine engine.cpp transposition.cpp + uci.cpp ) target_link_libraries(engine diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp @@ -10,8 +10,8 @@ #include "movelist.hpp" #include "piece.hpp" #include "score.hpp" -#include "stellar_version.hpp" #include "transposition.hpp" +#include "uci.hpp" #include "utils.hpp" #define MAX_PLY 64 @@ -21,6 +21,8 @@ #define WINDOW 50 +namespace engine { + Board board; TTable ttable(C64(0x400000)); Move pv_table[MAX_PLY][MAX_PLY]; @@ -31,7 +33,6 @@ bool follow_pv; U64 nodes; U32 ply; -Move move_list_best_move; U32 inline move_list_score(Move move) { const piece::Type type = board.get_square_piece_type(move.source()); if (move.is_capture()) { @@ -43,6 +44,7 @@ U32 inline move_list_score(Move move) { return history[piece::get_index(type, board.get_side())][to_underlying(move.target())]; } +Move move_list_best_move; void move_list_sort(MoveList &list, std::vector<int> &index, bool bestCheck = true) { static std::vector<int> score(256); @@ -157,7 +159,7 @@ int quiescence(int alpha, int beta) { return alpha; } -int negamax(int alpha, int beta, int depth, bool null) { +int negamax(int alpha, int beta, uint8_t depth, bool null) { int pv_node = (beta - alpha) > 1; HasheFlag flag = HasheFlag::Alpha; int futility = 0; @@ -306,18 +308,15 @@ int negamax(int alpha, int beta, int depth, bool null) { return alpha; } -void move_print_UCI(Move move) { - std::cout << square_to_coordinates(move.source()) << square_to_coordinates(move.target()); - if (move.is_promote()) std::cout << piece::get_code(move.promoted(), board.get_side()); -} - -void search_position(int depth) { +void search_position(const uci::Settings &setting) { int alpha = -SCORE_INFINITY, beta = SCORE_INFINITY; + nodes = 0; - for (int crnt = 1; crnt <= depth;) { + board = setting.board; + for (uint8_t depth_crnt = 1; depth_crnt <= setting.depth;) { follow_pv = 1; - int score = negamax(alpha, beta, crnt, true); + int score = negamax(alpha, beta, depth_crnt, true); if ((score <= alpha) || (score >= beta)) { alpha = -SCORE_INFINITY; beta = SCORE_INFINITY; @@ -335,189 +334,25 @@ void search_position(int depth) { std::cout << "info score cp " << score; } - std::cout << " depth " << crnt; + std::cout << " depth " << (unsigned)depth_crnt; std::cout << " nodes " << nodes; std::cout << " pv "; for (int i = 0; i < pv_length[0]; i++) { - move_print_UCI(pv_table[0][i]); + uci::move_print(board, pv_table[0][i]); std::cout << " "; } std::cout << "\n"; } - crnt++; + depth_crnt++; } std::cout << "bestmove "; - move_print_UCI(pv_table[0][0]); + uci::move_print(board, pv_table[0][0]); std::cout << "\n"; } - -void print_info(void) { - std::cout << "id name Stellar " << getStellarVersion() << "\n"; - std::cout << "id author Dimitrije Dobrota\n"; - std::cout << "uciok\n"; -} - -typedef struct Instruction Instruction; -struct Instruction { - char *command; - char *token; - char *crnt; -}; - -char *Instruction_token_next(Instruction *self); - -Instruction *Instruction_new(char *command) { - Instruction *p = new Instruction(); - p->command = new char[strlen(command) + 1]; - p->token = new char[strlen(command) + 1]; - strcpy(p->command, command); - p->crnt = command; - Instruction_token_next(p); - return p; -} - -void Instruction_free(Instruction **p) { - delete ((*p)->command); - delete ((*p)->token); - delete (*p); -} - -char *Instruction_token(Instruction *self) { return self->token; } -char *Instruction_token_n(Instruction *self, int n) { - while (isspace(*self->crnt) && *self->crnt != '\0') - self->crnt++; - - if (*self->crnt == '\0') { - *self->token = '\0'; - return NULL; - } - - char *p = self->token; - while (n--) { - while (!isspace(*self->crnt) && *self->crnt != '\0' && *self->crnt != ';') - *p++ = *self->crnt++; - if (*self->crnt == '\0') { - p++; - break; - } - self->crnt++; - *p++ = ' '; - } - *--p = '\0'; - - return self->token; -} - -char *Instruction_token_next(Instruction *self) { return Instruction_token_n(self, 1); } - -Move parse_move(char *move_string) { - Square source = square_from_coordinates(move_string); - Square target = square_from_coordinates(move_string + 2); - - const MoveList list(board); - for (int i = 0; i < list.size(); i++) { - const Move move = list[i]; - if (move.source() == source && move.target() == target) { - if (move_string[4]) { - if (tolower(piece::get_code(move.promoted())) != move_string[4]) continue; - } - return move; - } - } - return {}; -} - -Board *Instruction_parse(Instruction *self) { - char *token = Instruction_token(self); - - do { - if (strcmp(token, "ucinewgame") == 0) { - board = Board(start_position); - continue; - } - - if (strcmp(token, "quit") == 0) return nullptr; - - if (strcmp(token, "position") == 0) { - token = Instruction_token_next(self); - if (token && strcmp(token, "startpos") == 0) { - board = Board(start_position); - } else if (token && strcmp(token, "fen") == 0) { - token = Instruction_token_n(self, 6); - board = Board(token); - } else { - printf("Unknown argument after position\n"); - } - // board_print(board); - continue; - } - - if (strcmp(token, "moves") == 0) { - while ((token = Instruction_token_next(self))) { - Move move = parse_move(token); - if (move != Move()) { - move.make(board, 0); - } else { - printf("Invalid move %s!\n", token); - } - } - return &board; - } - - if (strcmp(token, "go") == 0) { - int depth = 6; - for (token = Instruction_token_next(self); token; token = Instruction_token_next(self)) { - - if (token && strcmp(token, "depth") == 0) { - token = Instruction_token_next(self); - depth = atoi(token); - } else { - // printf("Unknown argument %s after go\n", token); - } - } - search_position(depth); - continue; - } - - if (strcmp(token, "isready") == 0) { - printf("readyok\n"); - continue; - } - - if (strcmp(token, "uci") == 0) { - print_info(); - continue; - } - } while ((token = Instruction_token_next(self))); - - return &board; -} - -void uci_loop(void) { - Instruction *instruction; - char input[200000]; - - setbuf(stdin, NULL); - setbuf(stdout, NULL); - - print_info(); - while (1) { - memset(input, 0, sizeof(input)); - fflush(stdout); - if (!fgets(input, sizeof(input), stdin)) continue; - - instruction = Instruction_new(input); - if (!Instruction_parse(instruction)) break; - Instruction_free(&instruction); - } - - Instruction_free(&instruction); -} - -/* MAIN */ +} // namespace engine int main(void) { - uci_loop(); + uci::loop(); return 0; } diff --git a/src/engine/engine.hpp b/src/engine/engine.hpp @@ -0,0 +1,11 @@ +#ifndef STELLAR_ENGINE_H +#define STELLAR_ENGINE_H + +#include "engine/uci.hpp" +#include "uci.hpp" + +namespace engine { +void search_position(const uci::Settings &setting); +} + +#endif diff --git a/src/engine/transposition.cpp b/src/engine/transposition.cpp @@ -2,7 +2,7 @@ #include "board.hpp" #include "score.hpp" -int TTable::read(const Board &board, int ply, Move *best, int alpha, int beta, int depth) const { +int TTable::read(const Board &board, int ply, Move *best, int alpha, int beta, uint8_t depth) const { U64 hash = board.get_hash(); const Hashe &phashe = table[hash % table.size()]; @@ -22,7 +22,7 @@ int TTable::read(const Board &board, int ply, Move *best, int alpha, int beta, i return TTABLE_UNKNOWN; } -void TTable::write(const Board &board, int ply, Move best, int score, int depth, HasheFlag flag) { +void TTable::write(const Board &board, int ply, Move best, int score, uint8_t depth, HasheFlag flag) { U64 hash = board.get_hash(); Hashe &phashe = table[hash % table.size()]; diff --git a/src/engine/transposition.hpp b/src/engine/transposition.hpp @@ -20,7 +20,7 @@ typedef struct Hashe Hashe; struct Hashe { U64 key; Move best; - int depth; + uint8_t depth; int score; HasheFlag flag; }; @@ -30,8 +30,8 @@ class TTable { TTable(U64 size) : table(size, {0}) {} void clear() { table.clear(); }; - int read(const Board &board, int ply, Move *best, int alpha, int beta, int depth) const; - void write(const Board &board, int ply, Move best, int score, int depth, HasheFlag flag); + int read(const Board &board, int ply, Move *best, int alpha, int beta, uint8_t depth) const; + void write(const Board &board, int ply, Move best, int score, uint8_t depth, HasheFlag flag); private: std::vector<Hashe> table; diff --git a/src/engine/uci.cpp b/src/engine/uci.cpp @@ -0,0 +1,107 @@ +#include <iostream> +#include <sstream> +#include <string> + +#include "engine.hpp" +#include "stellar_version.hpp" +#include "uci.hpp" + +namespace uci { + +void move_print(const Board &board, Move move) { + std::cout << square_to_coordinates(move.source()) << square_to_coordinates(move.target()); + if (move.is_promote()) std::cout << piece::get_code(move.promoted(), board.get_side()); +} + +inline bool parse_move(const Board &board, Move &move, const std::string &move_string) { + Square source = square_from_coordinates(move_string.substr(0, 2)); + Square target = square_from_coordinates(move_string.substr(2, 2)); + + const MoveList list(board); + for (int i = 0; i < list.size(); i++) { + const Move crnt = list[i]; + if (crnt.source() == source && crnt.target() == target) { + if (move_string[4]) { + if (tolower(piece::get_code(crnt.promoted())) != move_string[4]) continue; + } + move = crnt; + return true; + } + } + return false; +} + +void loop(void) { + static Settings settings; + static std::string line, command; + static Move move; + + while (true) { + std::getline(std::cin, line); + std::istringstream iss(line); + iss >> command; + if (command == "quit") { + break; + } else if (command == "uci") { + std::cout << "id name Stellar " << getStellarVersion() << "\n"; + std::cout << "id author Dimitrije Dobrota\n"; + std::cout << "uciok\n"; + } else if (command == "debug") { + iss >> command; + settings.debug = (command == "on"); + } else if (command == "isready") { + std::cout << "readyok\n"; + } else if (command == "ucinewgame") { + settings = Settings(); + settings.board = Board(start_position); + } else if (command == "position") { + iss >> command; + if (command == "startpos") { + settings.board = Board(start_position); + } else if (command == "fen") { + iss >> command; + settings.board = Board(command); + } + iss >> command; + while (iss >> command) { + if (parse_move(settings.board, move, command)) + move.make(settings.board); + else + break; + } + } else if (command == "go") { + while (iss >> command) { + if (command == "wtime") + iss >> settings.wtime; + else if (command == "btime") + iss >> settings.btime; + else if (command == "winc") + iss >> settings.winc; + else if (command == "binc") + iss >> settings.binc; + else if (command == "depth") + iss >> settings.depth; + else if (command == "nodes") + iss >> settings.nodes; + else if (command == "movetime") + iss >> settings.movetime; + else if (command == "ponder") + settings.ponder = true; + else if (command == "mate") + settings.mate = true; + else if (command == "infinite") + settings.infinite = true; + else if (command == "searchmoves") { + while (iss >> command) { + if (parse_move(settings.board, move, command)) + settings.searchmoves.push(move); + else + break; + } + } + } + engine::search_position(settings); + } + } +} +} // namespace uci diff --git a/src/engine/uci.hpp b/src/engine/uci.hpp @@ -0,0 +1,32 @@ +#ifndef STELLAR_UCI_H +#define STELLAR_UCI_H + +#include "board.hpp" +#include "move.hpp" +#include "movelist.hpp" +#include "utils.hpp" + +namespace uci { + +struct Settings { + MoveList searchmoves; + Board board; + uint32_t wtime = 0; + uint32_t btime = 0; + uint32_t movetime = 0; + uint32_t nodes = 0; + uint16_t depth = 0; + uint16_t winc = 0; + uint16_t binc = 0; + bool ponder = false; + bool debug = false; + bool mate = false; + bool infinite = false; +}; + +void loop(void); +void move_print(const Board &board, Move move); + +} // namespace uci + +#endif diff --git a/src/move/move.hpp b/src/move/move.hpp @@ -51,7 +51,7 @@ struct Move { const piece::Type promoted(void) const { return static_cast<piece::Type>((flags_i & 0x3) + 1); } - bool make(Board &board, bool attack_only) const; + bool make(Board &board, bool attack_only = false) const; friend std::ostream &operator<<(std::ostream &os, Move move); diff --git a/src/move/movelist.hpp b/src/move/movelist.hpp @@ -15,6 +15,7 @@ class MoveList { using index_t = std::vector<int>; public: + MoveList() : list(){}; MoveList(const Board &board) : list() { list.reserve(256); generate(board); @@ -22,6 +23,7 @@ class MoveList { int size() const { return list.size(); } const Move operator[](size_t idx) const { return list[idx]; } + void push(const Move move) { list.push_back(move); } friend std::ostream &operator<<(std::ostream &os, const MoveList &list); diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp @@ -3,6 +3,7 @@ #include <cstdint> #include <exception> +#include <string> #include <type_traits> template <typename E> constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept { @@ -55,7 +56,7 @@ enum class Square: uint8_t { typedef Iterator<Square, Square::a1, Square::h8> SquareIter; -inline Square square_from_coordinates(const char *cord) { +inline Square square_from_coordinates(const std::string &cord) { return static_cast<Square>((cord[1] - '1') * 8 + (cord[0] - 'a')); }