stellar

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

commit 97eeac5b570b67e3fa6e327ec142ea0c5f135d49
parent 8bab42ac758595bc29cfc1ae86398a57e72cd963
author Dimitrije Dobrota <mail@dimitrijedobrota.com>
date Wed, 30 Aug 2023 20:15:26 +0200

Prevent move repetition

Diffstat:
M CMakeLists.txt | + -
M src/board/board.cpp | ++++ ---
M src/engine/engine.cpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --------------------
M src/engine/uci.cpp | ++++++++++++ -----
M src/engine/uci.hpp | +++ -
M src/move/move.hpp | ++++ --
M src/move/movelist.cpp | + -
M src/move/movelist.hpp | ++

8 files changed, 93 insertions(+), 34 deletions(-)


diff --git a/ CMakeLists.txt b/ CMakeLists.txt

@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Stellar
VERSION 0.0.26
VERSION 0.0.28
DESCRIPTION "Chess engine written in C" HOMEPAGE_URL https://git.dimitrijedobrota.com/stellar.git LANGUAGES C CXX

diff --git a/ src/board/board.cpp b/ src/board/board.cpp

@@ -23,7 +23,7 @@ Board::Board(const std::string &fen) { file = 0; rank--; } else {
throw std::exception();
throw std::runtime_error("Invalid piece position");
} }

@@ -33,7 +33,7 @@ Board::Board(const std::string &fen) { else if (fen[i] == 'b') side = Color::BLACK; else
throw std::exception();
throw std::runtime_error("Invalid player char");
for (i += 2; fen[i] != ' '; i++) { if (fen[i] == 'K')

@@ -47,9 +47,10 @@ Board::Board(const std::string &fen) { else if (fen[i] == '-') break; else
throw std::exception();
throw std::runtime_error("Invalid castle rights");
}
i++;
if (fen[++i] != '-') enpassant = square_from_coordinates(fen.data() + i); hash = Zobrist::hash(*this); }

diff --git a/ src/engine/engine.cpp b/ src/engine/engine.cpp

@@ -76,16 +76,46 @@ class TTable { std::vector<Hashe> table; };
const uci::Settings *settings = nullptr;
Board board;
TTable ttable;
Move pv_table[MAX_PLY][MAX_PLY];
Move killer[2][MAX_PLY];
U32 history[12][64];
int pv_length[MAX_PLY];
bool follow_pv;
U64 nodes;
U32 ply;
class RTable {
public:
RTable() = default;
bool is_repetition(const U64 hash) const {
for (int i = repetitions.size() - 1; i >= 0; i--) {
if (repetitions[i] == hash) return true;
if (repetitions[i] == hashNull) return false;
}
return false;
}
void pop(void) { repetitions.pop_back(); }
void clear(void) { repetitions.clear(); }
void push_null(void) { repetitions.push_back(hashNull); }
void push_hash(U64 hash) { repetitions.push_back(hash); }
friend std::ostream &operator<<(std::ostream &os, const RTable &rtable) {
for (const U64 hash : rtable.repetitions)
os << hash << " ";
return os;
}
private:
std::vector<U64> repetitions;
const static int hashNull = 0;
};
static const uci::Settings *settings = nullptr;
static Board board;
static TTable ttable;
static RTable rtable;
static Move pv_table[MAX_PLY][MAX_PLY];
static Move killer[2][MAX_PLY];
static U32 history[12][64];
static int pv_length[MAX_PLY];
static bool follow_pv;
static U64 nodes;
static U32 ply;
U32 inline move_list_score(Move move) { const piece::Type type = board.get_square_piece_type(move.source());

@@ -152,15 +182,15 @@ int evaluate(const Board &board) { return score; }
int is_repetition() { return 0; }
int stats_move_make(Board &copy, Move move, int flag) {
int stats_move_make(Board &copy, const Move move, int flag) {
copy = board; if (!move.make(board, flag)) { board = copy; return 0; } ply++;
rtable.push_hash(copy.get_hash());
if (!move.is_repetable()) rtable.push_null();
return 1; }

@@ -171,8 +201,15 @@ void stats_move_make_pruning(Board &copy) { ply++; }
void stats_move_unmake(Board &copy) {
void stats_move_unmake_pruning(Board &copy) {
board = copy;
ply--;
}
void stats_move_unmake(Board &copy, const Move move) {
board = copy;
if (!move.is_repetable()) rtable.pop();
rtable.pop();
ply--; }

@@ -201,13 +238,14 @@ int quiescence(int16_t alpha, int16_t beta) { MoveList list(board); for (int idx : move_list_sort(list, Move())) {
const Move move = list[idx];
if (!stats_move_make(copy, list[idx], 1)) continue; score = -quiescence(-beta, -alpha);
stats_move_unmake(copy);
stats_move_unmake(copy, move);
if (score > alpha) { alpha = score;
stats_pv_store(list[idx]);
stats_pv_store(move);
if (score >= beta) return beta; }

@@ -234,7 +272,7 @@ int negamax(int16_t alpha, int16_t beta, uint8_t depth, bool null) { if (ply && score != TTable::unknown && !pv_node) return score; // && fifty >= 100
if (ply && is_repetition()) return 0;
if (ply && rtable.is_repetition(board.get_hash())) return 0;
if (depth == 0) { nodes++; int score = quiescence(alpha, beta);

@@ -267,7 +305,7 @@ int negamax(int16_t alpha, int16_t beta, uint8_t depth, bool null) { if (ply && depth > 2 && staticEval >= beta) { stats_move_make_pruning(copy); score = -negamax(-beta, -beta + 1, depth - 1 - REDUCTION_MOVE, false);
stats_move_unmake(copy);
stats_move_unmake_pruning(copy);
if (score >= beta) return beta; }

@@ -306,7 +344,7 @@ int negamax(int16_t alpha, int16_t beta, uint8_t depth, bool null) { // futility pruning if (futility && searched && !move.is_capture() && !move.is_promote() && !board.is_check()) {
stats_move_unmake(copy);
stats_move_unmake(copy, move);
continue; }

@@ -331,7 +369,7 @@ int negamax(int16_t alpha, int16_t beta, uint8_t depth, bool null) { } }
stats_move_unmake(copy);
stats_move_unmake(copy, move);
searched++; if (settings->stopped) return 0;

@@ -378,9 +416,16 @@ void search_position(const uci::Settings &settingsr) { ttable = TTable(C64(0x1000000)); }
rtable.clear();
board = settings->board;
for (int i = 0; i < settings->madeMoves.size(); i++) {
rtable.push_hash(board.get_hash());
settings->madeMoves[i].make(board);
if (!settings->madeMoves[i].is_repetable()) rtable.push_null();
}
ply = 0; nodes = 0;
board = settings->board;
settings->stopped = false; memset(killer, 0x00, sizeof(killer)); memset(history, 0x00, sizeof(history));

diff --git a/ src/engine/uci.cpp b/ src/engine/uci.cpp

@@ -76,19 +76,26 @@ void loop(void) { settings = Settings(); settings.board = Board(start_position); } else if (command == "position") {
settings.madeMoves.clear();
iss >> command; if (command == "startpos") { settings.board = Board(start_position); } else if (command == "fen") { iss >> command;
settings.board = Board(command);
settings.board = Board(line.substr(13));
}
iss >> command;
while (iss >> command)
if (command == "moves") break;
Board board = settings.board;
while (iss >> command) {
if (!parse_move(settings.board, move, command)) break;
move.make(settings.board);
if (!parse_move(board, move, command)) break;
settings.madeMoves.push(move);
move.make(board);
} } else if (command == "go") {
settings.searchMoves.clear();
uint32_t wtime = 0, btime = 0, movetime = 0; uint16_t winc = 0, binc = 0, movestogo = 30;

@@ -118,7 +125,7 @@ void loop(void) { else if (command == "searchmoves") { while (iss >> command) { if (!parse_move(settings.board, move, command)) break;
settings.searchmoves.push(move);
settings.searchMoves.push(move);
} } }

diff --git a/ src/engine/uci.hpp b/ src/engine/uci.hpp

@@ -9,7 +9,9 @@ namespace uci { struct Settings {
MoveList searchmoves;
MoveList searchMoves;
MoveList madeMoves;
Board board; uint32_t starttime; uint32_t stoptime;

diff --git a/ src/move/move.hpp b/ src/move/move.hpp

@@ -16,6 +16,7 @@ struct Move { CASTLEQ, CAPTURE, ENPASSANT,
PQUIET,
PKNIGHT = 8, PBISHOP, PROOK,

@@ -37,11 +38,12 @@ struct Move { Square source(void) const { return static_cast<Square>(source_i); } Square target(void) const { return static_cast<Square>(target_i); }
bool is_capture(void) const { return flags_i & CAPTURE; }
bool is_capture(void) const { return flags_i != PQUIET && (flags_i & CAPTURE); }
bool is_promote(void) const { return flags_i & 0x8; }
bool is_quiet(void) const { return flags_i == QUIET; }
bool is_double(void) const { return flags_i == DOUBLE; }
bool is_repetable(void) const { return flags_i == QUIET; }
bool is_quiet(void) const { return flags_i == QUIET || flags_i == PQUIET; }
bool is_castle(void) const { return flags_i == CASTLEK || flags_i == CASTLEQ; } bool is_castle_king(void) const { return flags_i == CASTLEK; }

diff --git a/ src/move/movelist.cpp b/ src/move/movelist.cpp

@@ -32,7 +32,7 @@ void MoveList::generate(const Board &board) { list.push_back({src, tgt, Move::PROOK}); list.push_back({src, tgt, Move::PQUEEN}); } else {
list.push_back({src, tgt, Move::QUIET});
list.push_back({src, tgt, Move::PQUIET});
// two ahead const Square tgt = static_cast<Square>(tgt_i + add);

diff --git a/ src/move/movelist.hpp b/ src/move/movelist.hpp

@@ -21,7 +21,9 @@ class MoveList { generate(board); }
void clear() { list.clear(); }
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); }