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:
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'));
}