stellarUCI 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 16:24:59 +0200 | 
Rewrite uci logic in c++
| M | CMakeLists.txt | | | + - | 
| M | src/board/CMakeLists.txt | | | + - | 
| M | src/engine/CMakeLists.txt | | | + | 
| M | src/engine/engine.cpp | | | +++++++ --------------------------------------------------------------------------- | 
| A | src/engine/engine.hpp | | | +++++++++++ | 
| M | src/engine/transposition.cpp | | | ++ -- | 
| M | src/engine/transposition.hpp | | | +++ --- | 
| A | src/engine/uci.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | src/engine/uci.hpp | | | ++++++++++++++++++++++++++++++++ | 
| M | src/move/move.hpp | | | + - | 
| M | src/move/movelist.hpp | | | ++ | 
| M | src/utils/utils.hpp | | | ++ - | 
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'));
          }