stellar

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

commitefb266c91585d3dcfe9ade481bc13199b9508418
parentd02e946f5b524acd6871c196d4ef505cfa0b5e0a
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateThu, 10 Aug 2023 13:30:42 +0200

Fix almost all of the bugs...

Diffstat:
Msrc/attacks/attacks.cpp|++--
Msrc/attacks/internal.cpp|+-
Dsrc/attacks/internal.h|------------------------------
Msrc/board/board.cpp|+++++++++-------------
Msrc/include/board.hpp|++++++------
Msrc/include/piece.hpp|+++----
Msrc/moves/moves_generate.cpp|++++++++++++++++++++++++------------------
Msrc/perft/CMakeLists.txt|-------
Msrc/perft/perft.cpp|+++-------

9 files changed, 48 insertions(+), 88 deletions(-)


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

@@ -48,7 +48,7 @@ U64 attacks_queen_get(Square square, U64 occupancy) {

void attacks_init_leapers(void) {
for (Square square : SquareIter()) {
uint8_t square_i;
uint8_t square_i = to_underlying(square);
pawn_attacks[to_underlying(Color::WHITE)][square_i] =
pawn_mask(Color::WHITE, square);
pawn_attacks[to_underlying(Color::BLACK)][square_i] =

@@ -60,7 +60,7 @@ void attacks_init_leapers(void) {

void attacks_init_sliders(int bishop) {
for (Square square : SquareIter()) {
uint8_t square_i;
uint8_t square_i = to_underlying(square);
U64 attack_mask;
if (bishop) {

diff --git a/src/attacks/internal.cpp b/src/attacks/internal.cpp

@@ -1,4 +1,4 @@

#include "internal.h"
#include "internal.hpp"
#include "utils_cpp.hpp"
#include <algorithm> // std::min

diff --git a/src/attacks/internal.h b/src/attacks/internal.h

@@ -1,30 +0,0 @@

#ifndef STELLAR_ATTAKCS_INTERNAL_H
#define STELLAR_ATTAKCS_INTERNAL_H
#include "utils_cpp.hpp"
extern U64 king_attacks[64]; // king attack table [square]
extern U64 knight_attacks[64]; // knight attack table [square]
extern U64 pawn_attacks[2][64]; // pawn attack table [side][square]
extern U64 rook_attacks[64][4096]; // rook attack table [square][occupancies]
extern U64 bishop_attacks[64][512]; // bishop attack table [square][occupancies]
extern U64 rook_masks[64]; // rook attack mask
extern U64 bishop_masks[64]; // bishop attack mask
extern const int rook_relevant_bits[64];
extern const int bishop_relevant_bits[64];
int hash(U64 key, U64 magic, int relevant_bits);
U64 set_occupancy(int index, int bits_in_mask, U64 attack_mask);
U64 bishop_mask(Square square);
U64 bishop_on_the_fly(Square square, U64 block);
U64 king_mask(Square square);
U64 knight_mask(Square square);
U64 pawn_mask(Color side, Square square);
U64 rook_mask(Square square);
U64 rook_on_the_fly(Square square, U64 block);
#endif

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

@@ -121,13 +121,11 @@ bool Board::is_square_occupied(Square square) const {

}
bool Board::is_square_attacked(Square square, Color side) const {
// side switch because of pawns
Color side_other = (side == Color::BLACK) ? Color::WHITE : Color::BLACK;
for (piece::Type type : piece::TypeIter()) {
const piece::Piece &piece = piece::get(type, side_other);
if (get_bitboard_piece_attacks(piece, square) &
get_bitboard_piece(piece))
if (get_bitboard_piece_attacks(piece::get(type, side_other), square) &
get_bitboard_piece(piece::get(type, side)))
return 1;
}

@@ -137,19 +135,14 @@ bool Board::is_square_attacked(Square square, Color side) const {

bool Board::is_check(void) const {
U64 king =
pieces[to_underlying(piece::Type::KING)] & colors[to_underlying(side)];
Color side_other = (side == Color::BLACK) ? Color::WHITE : Color::BLACK;
Square square = static_cast<Square>(bit_lsb_index(king));
return is_square_attacked(square, side);
return is_square_attacked(square, side_other);
}
Board::Board(const std::string &fen) {
*this = {0};
side = Color::WHITE;
enpassant = Square::no_sq;
castle = 0;
int file = 0, rank = 7, i;
for (i = 0; i < fen.size(); i++) {
for (i = 0; fen[i] != ' '; i++) {
if (isalpha(fen[i])) {
set_piece(piece::get_from_code(fen[i]),
static_cast<Square>(rank * 8 + file));

@@ -181,6 +174,8 @@ Board::Board(const std::string &fen) {

castle |= to_underlying(Castle::BK);
else if (fen[i] == 'q')
castle |= to_underlying(Castle::BQ);
else if (fen[i] == '-')
break;
else
throw std::exception();
}

@@ -192,13 +187,14 @@ Board::Board(const std::string &fen) {

std::ostream &operator<<(std::ostream &os, const Board &board) {
for (int rank = 0; rank < 8; rank++) {
for (int file = 0; file < 8; file++) {
if (!file) os << 8 - rank << " ";
Square square = static_cast<Square>((7 - rank) * 8 + file);
const piece::Piece *piece = board.get_square_piece(square);
os << (piece ? piece->code : '.') << " ";
}
printf("\n");
}
os << " A B C D E F G H\n";
os << " A B C D E F G H\n";
os << " Side: ";
os << ((board.side == Color::WHITE) ? "white" : "black") << "\n";
os << "Enpassant: " << square_to_coordinates(board.enpassant) << "\n";

diff --git a/src/include/board.hpp b/src/include/board.hpp

@@ -69,12 +69,12 @@ class Board {

bool is_check(void) const;
private:
U64 colors[2];
U64 pieces[6];
U64 hash;
Color side;
Square enpassant;
uint8_t castle;
U64 colors[2] = {0};
U64 pieces[6] = {0};
U64 hash = 0;
Color side = Color::WHITE;
Square enpassant = Square::no_sq;
uint8_t castle = 0;
};
const piece::Piece &board_square_piece(const Board *self, Square square,

diff --git a/src/include/piece.hpp b/src/include/piece.hpp

@@ -32,11 +32,10 @@ class Piece {

constexpr Piece(Type type, Color color, char code, const char *symbol,
attack_get_f attacks)
: type(type), color(color), code(code), symbol(symbol),
attacks(attacks), index(index_calc()) {}
attacks(attacks), index(index_calc(color, type)) {}
constexpr int index_calc() {
return to_underlying(Type::TypeSIZE) * to_underlying(color) *
to_underlying(type);
constexpr uint8_t index_calc(Color color, Type type) {
return to_underlying(color) * 6 + to_underlying(type);
}
};

diff --git a/src/moves/moves_generate.cpp b/src/moves/moves_generate.cpp

@@ -15,19 +15,19 @@

#define pawn_promote(source, target, piece, capture) \
res.push_back( \
{move_encode(source, target, &piece, 0, \
{move_encode(source, target, &piece, capture, \
&piece::get(piece::Type::KNIGHT, color), 0, 0, 0), \
0}); \
res.push_back( \
{move_encode(source, target, &piece, 0, \
{move_encode(source, target, &piece, capture, \
&piece::get(piece::Type::BISHOP, color), 0, 0, 0), \
0}); \
res.push_back( \
{move_encode(source, target, &piece, 0, \
{move_encode(source, target, &piece, capture, \
&piece::get(piece::Type::ROOK, color), 0, 0, 0), \
0}); \
res.push_back( \
{move_encode(source, target, &piece, 0, \
{move_encode(source, target, &piece, capture, \
&piece::get(piece::Type::QUEEN, color), 0, 0, 0), \
0});

@@ -53,22 +53,24 @@ std::vector<MoveE> move_list_generate(const Board &board) {

if (!board.is_square_occupied(tgt)) {
if (pawn_canPromote(color, src)) {
pawn_promote(src_i, tgt_i, piece, nullptr);
continue;
}
res.push_back(
{move_encode(src_i, tgt_i, &piece, 0, 0, 0, 0, 0), 0});
// two ahead
if (pawn_onStart(color, src) && !board.is_square_occupied(tgt))
} else {
res.push_back(
{move_encode(src_i, tgt_i + add, &piece, 0, 0, 1, 0, 0),
0});
{move_encode(src_i, tgt_i, &piece, 0, 0, 0, 0, 0), 0});
// two ahead
Square tgt = static_cast<Square>(tgt_i + add);
if (pawn_onStart(color, src) && !board.is_square_occupied(tgt))
res.push_back(
{move_encode(src_i, tgt_i + add, &piece, 0, 0, 1, 0, 0),
0});
}
}
// capture
U64 attack = board.get_bitboard_piece_attacks(piece, src) &
board.get_bitboard_color(colorOther);
bitboard_for_each_bit(tgt_i, attack) {
Square tgt = static_cast<Square>(tgt_i);
const piece::Piece *capture = board.get_square_piece(tgt);
if (pawn_canPromote(color, src)) {
pawn_promote(src_i, tgt_i, piece, capture);

@@ -92,11 +94,12 @@ std::vector<MoveE> move_list_generate(const Board &board) {

}
// All piece move
for (piece::Type type : ++piece::TypeIter()) {
const piece::Piece &piece = piece::get(type, color);
auto type_it = piece::TypeIter().begin();
for (++type_it; type_it != type_it.end(); ++type_it) {
const piece::Piece &piece = piece::get(*type_it, color);
U64 bitboard = board.get_bitboard_piece(piece);
Square src = static_cast<Square>(src_i);
bitboard_for_each_bit(src_i, bitboard) {
Square src = static_cast<Square>(src_i);
U64 attack = board.get_bitboard_piece_attacks(piece, src) &
~board.get_bitboard_color(color);
bitboard_for_each_bit(tgt_i, attack) {

@@ -111,7 +114,8 @@ std::vector<MoveE> move_list_generate(const Board &board) {

// Castling
if (color == Color::WHITE) {
const piece::Piece &piece = piece::get(piece::Type::KING, Color::WHITE);
static const piece::Piece &piece =
piece::get(piece::Type::KING, Color::WHITE);
if (board.get_castle() & to_underlying(Board::Castle::WK)) {
if (!board.is_square_occupied(Square::f1) &&
!board.is_square_occupied(Square::g1) &&

@@ -134,7 +138,8 @@ std::vector<MoveE> move_list_generate(const Board &board) {

0});
}
} else {
const piece::Piece &piece = piece::get(piece::Type::KING, Color::BLACK);
static const piece::Piece &piece =
piece::get(piece::Type::KING, Color::BLACK);
if (board.get_castle() & to_underlying(Board::Castle::BK)) {
if (!board.is_square_occupied(Square::f8) &&
!board.is_square_occupied(Square::g8) &&

@@ -158,5 +163,6 @@ std::vector<MoveE> move_list_generate(const Board &board) {

}
}
res.resize(res.size());
return res;
}

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

@@ -1,10 +1,3 @@

project(
perft
VERSION 1.1.0
DESCRIPTION "Performance Test"
LANGUAGES C CXX
)
add_executable(perft perft.cpp)
option(WITH_FULL_COUNT "Make count on types of moves" OFF)

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

@@ -14,10 +14,6 @@

// FEN debug positions
#define tricky_position \
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
#define killer_position \
"rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1"
#define cmk_position \
"r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
void perft_result_print(PerftResult res) {
printf(" - Perft Results -\n\n");

@@ -66,7 +62,7 @@ void perft_driver(Board &board, MoveList *moveList, int depth,

} else {
result->node++;
#ifdef USE_FULL_COUNT
if (board_isCheck(copy)) result->check++;
if (copy.is_check()) result->check++;
if (move_capture(move)) result->capture++;
if (move_enpassant(move)) result->enpassant++;
if (move_castle(move)) result->castle++;

@@ -113,7 +109,7 @@ void *perft_thread(void *arg) {

} else {
result.node++;
#ifdef USE_FULL_COUNT
if (board_isCheck(copy)) result.check++;
if (copy.is_check()) result.check++;
if (move_capture(move)) result.capture++;
if (move_enpassant(move)) result.enpassant++;
if (move_castle(move)) result.castle++;

@@ -145,7 +141,7 @@ PerftResult perft_test(const char *fen, int depth, int thread_num) {

int main(int argc, char *argv[]) {
int c, depth = 1, thread_num = 1;
std::string s(start_position);
std::string s(tricky_position);
const char *fen = s.data();
while ((c = getopt(argc, argv, "t:f:d:")) != -1) {
switch (c) {