stellar

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

commit 1d52c2087a2d9211d186318ca6bd568f88828d60
parent 8f954a0404ce9be9d8d5cb9bb59257b6cb5b83f3
author Dimitrije Dobrota <mail@dimitrijedobrota.com>
date Sat, 29 Jul 2023 19:48:06 +0200

Extract Piece interface

Diffstat:
M CMakeLists.txt | + -
M src/attack/attack.c | + -
M src/board/board.c | +++++++++++++++++ -----------------------------------------------------------------
M src/engine/CMakeLists.txt | +
M src/engine/engine.c | +++++++++ ----------
M src/include/board.h | ++++ ------------------
M src/include/moves.h | +++ ---
A src/include/piece.h | ++++++++++++++++++++++++++++++++++++
M src/include/utils.h | -----------------
M src/moves/moves.c | +++++++++++++ -------------
M src/perft/CMakeLists.txt | +
M src/perft/perft.c | + -
A src/piece/CMakeLists.txt | +++++++
A src/piece/piece.c | ++++++++++++++++++++++++++++++++++++++++++++++++++++

14 files changed, 148 insertions(+), 141 deletions(-)


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

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

diff --git a/ src/attack/attack.c b/ src/attack/attack.c

@@ -1,7 +1,7 @@ #include "string.h" #include "attack.h"
#include "utils.h"
#include "piece.h"
#define UNUSED(x) (void)(x)

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

@@ -5,66 +5,8 @@ #include <cul/assert.h> #include <cul/mem.h>
#include "attack.h"
#include "board.h"
#include "utils.h"
U64 board_pieceBB_get(Board self, ePiece piece, Square target);
// PIECE
struct Piece {
ePiece piece;
eColor color;
char code;
char asci;
char *unicode;
attack_f attacks;
};
// clang-format off
struct Piece Pieces[2][6] = {
{
[PAWN] = {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks},
[KNIGHT] = {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks},
},
{
[PAWN] = {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks},
[KNIGHT] = {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks},
},
};
// clang-format on
attack_f Piece_attacks(Piece self) { return self->attacks; }
char Piece_asci(Piece self) { return self->asci; }
char Piece_code(Piece self) { return self->code; }
char *Piece_unicode(Piece self) { return self->unicode; }
eColor Piece_color(Piece self) { return self->color; }
ePiece Piece_piece(Piece self) { return self->piece; }
int Piece_index(Piece self) { return self->color * 8 + self->piece; }
Piece Piece_fromCode(char code) {
int color = (isupper(code)) ? WHITE : BLACK;
for (int i = 0; i < 6; i++)
if (Pieces[color][i].code == code) return &Pieces[color][i];
return NULL;
}
ePiece Piece_piece_fromCode(int index) {
return Pieces[WHITE][index % 8].piece;
}
Piece Piece_fromIndex(int index) { return &Pieces[index / 8][index % 8]; }
Piece Piece_get(ePiece piece, eColor color) { return &Pieces[color][piece]; }
// CBOARD
struct Board { U64 colorBB[2]; U64 pieceBB[6];

@@ -97,8 +39,8 @@ U64 board_pieceBB_get(Board self, ePiece piece, Square target) { } U64 board_pieceSet(Board self, Piece piece) {
return self->pieceBB[Piece_piece(piece)] &
self->colorBB[Piece_color(piece)];
return self->pieceBB[piece_piece(piece)] &
self->colorBB[piece_color(piece)];
} void board_enpassant_set(Board self, Square target) {

@@ -121,14 +63,14 @@ int board_piece_get(Board self, Square square) { return -1; }
void board_piece_pop(Board self, Piece Piece, Square square) {
bit_pop(self->pieceBB[Piece->piece], square);
bit_pop(self->colorBB[Piece->color], square);
void board_piece_pop(Board self, Piece piece, Square square) {
bit_pop(self->pieceBB[piece_piece(piece)], square);
bit_pop(self->colorBB[piece_color(piece)], square);
}
void board_piece_set(Board self, Piece Piece, Square square) {
bit_set(self->pieceBB[Piece->piece], square);
bit_set(self->colorBB[Piece->color], square);
void board_piece_set(Board self, Piece piece, Square square) {
bit_set(self->pieceBB[piece_piece(piece)], square);
bit_set(self->colorBB[piece_color(piece)], square);
} void board_piece_move(Board self, Piece Piece, Square source, Square target) {

@@ -136,8 +78,8 @@ void board_piece_move(Board self, Piece Piece, Square source, Square target) { board_piece_set(self, Piece, target); }
U64 board_piece_attacks(Board self, Piece Piece, Square src) {
return Piece_attacks(Piece)(src, board_occupancy(self));
U64 board_piece_attacks(Board self, Piece piece, Square src) {
return piece_attacks(piece)(src, board_occupancy(self));
} void board_piece_capture(Board self, Piece piece, Piece taken, Square source,

@@ -166,8 +108,8 @@ int board_square_isAttack(Board self, Square square, eColor side) { U64 occupancy = self->colorBB[WHITE] | self->colorBB[BLACK]; for (int i = 0; i < 6; i++) {
if (Pieces[!side][i].attacks(square, occupancy) & self->pieceBB[i] &
self->colorBB[side])
if (piece_attacks(piece_get(i, !side))(square, occupancy) &
self->pieceBB[i] & self->colorBB[side])
return 1; }

@@ -176,7 +118,7 @@ int board_square_isAttack(Board self, Square square, eColor side) { Piece board_square_piece(Board self, Square square, eColor color) { for (ePiece i = 0; i < 6; i++)
if (board_pieceBB_get(self, i, square)) return Piece_get(i, color);
if (board_pieceBB_get(self, i, square)) return piece_get(i, color);
return NULL; }

@@ -195,7 +137,7 @@ void board_print(Board self) { if (color != -1) { for (int piece_index = 0; piece_index < 6; piece_index++) { if (bit_get(self->pieceBB[piece_index], square)) {
piece = &Pieces[color][piece_index];
piece = piece_get(piece_index, color);
break; } }

@@ -203,7 +145,7 @@ void board_print(Board self) { if (!file) printf(" %d ", 8 - rank);
printf("%s", (piece) ? Piece_unicode(piece) : ". ");
printf("%s", (piece) ? piece_unicode(piece) : ". ");
} printf("\n"); }

@@ -216,7 +158,7 @@ void board_print(Board self) { printf("\n"); }
Board board_fromFEN(Board board, char *fen) {
Board board_from_FEN(Board board, char *fen) {
if (!board) NEW(board); memset(board, 0, sizeof(*board));

@@ -229,9 +171,9 @@ Board board_fromFEN(Board board, char *fen) { for (Piece piece; *fen != ' '; fen++) { Square square = rank * 8 + file; if (isalpha(*fen)) {
if (!(piece = Piece_fromCode(*fen))) assert(0);
bit_set(board->colorBB[piece->color], square);
bit_set(board->pieceBB[piece->piece], square);
if (!(piece = piece_from_code(*fen))) assert(0);
bit_set(board->colorBB[piece_color(piece)], square);
bit_set(board->pieceBB[piece_piece(piece)], square);
file++; } else if (isdigit(*fen)) { file += *fen - '0';

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

@@ -4,6 +4,7 @@ target_link_libraries(engine PRIVATE attack PRIVATE board PRIVATE moves
PRIVATE piece
PRIVATE score PRIVATE utils )

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

@@ -13,7 +13,6 @@ #include <cul/assert.h> #include <cul/mem.h>
#define MAX_PLY 64 typedef struct Stats_T *Stats_T;

@@ -36,15 +35,15 @@ void Stats_free(Stats_T *p) { FREE(*p); } int move_score(Stats_T stats, Move move) { if (move_capture(move)) {
return Score_capture(Piece_piece(move_piece(move)),
Piece_piece(move_piece_capture(move)));
return Score_capture(piece_piece(move_piece(move)),
piece_piece(move_piece_capture(move)));
} else { if (!move_cmp(stats->killer_moves[0][stats->ply], move)) return 9000; else if (!move_cmp(stats->killer_moves[1][stats->ply], move)) return 8000; else
return stats->history_moves[Piece_index(move_piece(move))]
return stats->history_moves[piece_index(move_piece(move))]
[move_target(move)]; }

@@ -184,7 +183,7 @@ int negamax(Stats_T stats, Board board, int alpha, int beta, int depth) { if (score > alpha) { if (!move_capture(move))
stats->history_moves[Piece_index(move_piece(move))]
stats->history_moves[piece_index(move_piece(move))]
[move_target(move)] += depth; alpha = score;

@@ -210,7 +209,7 @@ int negamax(Stats_T stats, Board board, int alpha, int beta, int depth) { void move_print_UCI(Move move) { printf("%s%s", square_to_coordinates[move_source(move)], square_to_coordinates[move_target(move)]);
if (move_promote(move)) printf(" %c", Piece_asci(move_piece_promote(move)));
if (move_promote(move)) printf(" %c", piece_asci(move_piece_promote(move)));
} void search_position(Board board, int depth) {

@@ -312,7 +311,7 @@ Move parse_move(Board board, char *move_string) { Move move = moves->moves[i]; if (move_source(move) == source && move_target(move) == target) { if (move_string[4]) {
if (tolower(Piece_code(move_piece_promote(move))) !=
if (tolower(piece_code(move_piece_promote(move))) !=
move_string[4]) continue; }

@@ -332,7 +331,7 @@ Board Instruction_parse(Instruction_T self, Board board) { do { if (strcmp(token, "ucinewgame") == 0) {
board = board_fromFEN(board, start_position);
board = board_from_FEN(board, start_position);
continue; }

@@ -341,10 +340,10 @@ Board Instruction_parse(Instruction_T self, Board board) { if (strcmp(token, "position") == 0) { token = Instruction_token_next(self); if (token && strcmp(token, "startpos") == 0) {
board = board_fromFEN(board, start_position);
board = board_from_FEN(board, start_position);
} else if (token && strcmp(token, "fen") == 0) { token = Instruction_token_n(self, 6);
board = board_fromFEN(board, token);
board = board_from_FEN(board, token);
} else { printf("Unknown argument after position\n"); }

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

@@ -1,7 +1,7 @@
#ifndef CBOARD_H
#define CBOARD_H
#ifndef BOARD_H
#define BOARD_H
#include "utils.h"
#include "piece.h"
enum enumCastle { WK = 1,

@@ -11,20 +11,6 @@ enum enumCastle { }; typedef enum enumCastle eCastle;
typedef struct Piece *Piece;
char Piece_asci(Piece self);
char Piece_code(Piece self);
char *Piece_unicode(Piece self);
eColor Piece_color(Piece self);
ePiece Piece_piece(Piece self);
int Piece_index(Piece self);
Piece Piece_get(ePiece piece, eColor color);
Piece Piece_fromCode(char code);
Piece Piece_fromIndex(int index);
ePiece Piece_piece_fromCode(int index);
typedef struct Board *Board; Board board_new(void);

@@ -60,7 +46,7 @@ Piece board_square_piece(Board self, Square square, eColor side); int board_square_isAttack(Board self, Square square, eColor side); int board_square_isOccupied(Board self, Square square);
Board board_fromFEN(Board board, char *fen);
Board board_from_FEN(Board board, char *fen);
int board_isCheck(Board self); void board_print(Board self); void board_side_switch(Board self);

diff --git a/ src/include/moves.h b/ src/include/moves.h

@@ -47,8 +47,8 @@ int move_make(Move move, Board board, int flag); #define move_capture(move) (move.capture) #define move_promote(move) (move.promote)
#define move_piece(move) (Piece_fromIndex(move.piece))
#define move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
#define move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
#define move_piece(move) (piece_from_index(move.piece))
#define move_piece_capture(move) (piece_from_index(move.piece_capture))
#define move_piece_promote(move) (piece_from_index(move.piece_promote))
#endif

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

@@ -0,0 +1,36 @@
#ifndef PIECE_H
#define PIECE_H
#include "attack.h"
typedef const struct Piece *Piece;
typedef enum enumColor eColor;
enum enumColor {
WHITE = 0,
BLACK
};
typedef enum enumPiece ePiece;
enum enumPiece {
PAWN = 0,
KNIGHT,
BISHOP,
ROOK,
QUEEN,
KING
};
char piece_asci(Piece self);
attack_f piece_attacks(Piece self);
char piece_code(Piece self);
char *piece_unicode(Piece self);
eColor piece_color(Piece self);
ePiece piece_piece(Piece self);
int piece_index(Piece self);
Piece piece_get(ePiece piece, eColor color);
Piece piece_from_code(char code);
Piece piece_from_index(int index);
#endif

diff --git a/ src/include/utils.h b/ src/include/utils.h

@@ -62,23 +62,6 @@ U64 noWeOne(U64 b); U64 rotateLeft(U64 x, int s); U64 rotateRight(U64 x, int s);
// enum types for color and piece type
enum enumColor {
WHITE = 0,
BLACK
};
enum enumPiece {
PAWN = 0,
KNIGHT,
BISHOP,
ROOK,
QUEEN,
KING
};
typedef enum enumColor eColor;
typedef enum enumPiece ePiece;
int get_time_ms(void); typedef U64 (*attack_f)(Square square, U64 occupancy);

diff --git a/ src/moves/moves.c b/ src/moves/moves.c

@@ -17,9 +17,9 @@ Move move_encode(Square src, Square tgt, Piece piece, Piece capture, .castle = castle, .capture = capture != NULL, .promote = promote != NULL,
.piece = Piece_index(piece),
.piece_capture = capture ? Piece_index(capture) : 0,
.piece_promote = promote ? Piece_index(promote) : 0,
.piece = piece_index(piece),
.piece_capture = capture ? piece_index(capture) : 0,
.piece_promote = promote ? piece_index(promote) : 0,
}; }

@@ -27,9 +27,9 @@ void move_print(Move move) { printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n", square_to_coordinates[move_source(move)], square_to_coordinates[move_target(move)],
Piece_unicode(move_piece(move)),
move_capture(move) ? Piece_unicode(move_piece_capture(move)) : "X ",
move_promote(move) ? Piece_unicode(move_piece_promote(move)) : "X ",
piece_unicode(move_piece(move)),
move_capture(move) ? piece_unicode(move_piece_capture(move)) : "X ",
move_promote(move) ? piece_unicode(move_piece_promote(move)) : "X ",
move_double(move) ? 1 : 0, move_enpassant(move) ? 1 : 0, move_castle(move) ? 1 : 0, move_capture(move) ? 1 : 0, move_promote(move) ? 1 : 0);

@@ -69,7 +69,7 @@ void move_list_print(MoveList self) { #define pawn_promote(source, target, Piece, Capture) \ for (int i = 1; i < 5; i++) { \ move = move_encode(source, target, Piece, Capture, \
Piece_get(i, color), 0, 0, 0); \
piece_get(i, color), 0, 0, 0); \
move_list_add(moves, move); \ }

@@ -81,7 +81,7 @@ MoveList move_list_generate(MoveList moves, Board board) { if (!moves) moves = move_list_new(); { // pawn moves
Piece Piece = Piece_get(PAWN, color);
Piece Piece = piece_get(PAWN, color);
U64 bitboard = board_pieceSet(board, Piece); bitboard_for_each_bit(src, bitboard) { { // quiet

@@ -135,7 +135,7 @@ MoveList move_list_generate(MoveList moves, Board board) { // All piece move for (int piece = 1; piece < 6; piece++) {
Piece Piece = Piece_get(piece, color);
Piece Piece = piece_get(piece, color);
U64 bitboard = board_pieceSet(board, Piece); bitboard_for_each_bit(src, bitboard) { U64 attack = board_piece_attacks(board, Piece, src) &

@@ -153,7 +153,7 @@ MoveList move_list_generate(MoveList moves, Board board) { // Castling { if (color == WHITE) {
Piece Piece = Piece_get(KING, WHITE);
Piece Piece = piece_get(KING, WHITE);
if (board_castle(board) & WK) { if (!board_square_isOccupied(board, f1) && !board_square_isOccupied(board, g1) &&

@@ -172,7 +172,7 @@ MoveList move_list_generate(MoveList moves, Board board) { move_encode(e1, c1, Piece, 0, 0, 0, 0, 1)); } } else {
Piece Piece = Piece_get(KING, BLACK);
Piece Piece = piece_get(KING, BLACK);
if (board_castle(board) & BK) { if (!board_square_isOccupied(board, f8) && !board_square_isOccupied(board, g8) &&

@@ -231,13 +231,13 @@ int move_make(Move move, Board board, int flag) { { int ntarget = target + (color == WHITE ? -8 : +8); if (move_enpassant(move))
board_piece_pop(board, Piece_get(PAWN, !color), ntarget);
board_piece_pop(board, piece_get(PAWN, !color), ntarget);
board_enpassant_set(board, move_double(move) ? ntarget : no_sq); } if (move_castle(move)) {
Piece Rook = Piece_get(ROOK, board_side(board));
Piece Rook = piece_get(ROOK, board_side(board));
switch (target) { case g1: board_piece_move(board, Rook, h1, f1);

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

@@ -4,6 +4,7 @@ target_link_libraries(perft PRIVATE attack PRIVATE board PRIVATE moves
PRIVATE piece
PRIVATE score PRIVATE utils )

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

@@ -140,6 +140,6 @@ void perft_test_threaded(Board board, int depth) { int main(void) { init_attacks(); Board board = board_new();
board_fromFEN(board, tricky_position);
board_from_FEN(board, tricky_position);
perft_test_threaded(board, 5); }

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

@@ -0,0 +1,7 @@
add_library(piece OBJECT
piece.c
)
target_include_directories(piece
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)

diff --git a/ src/piece/piece.c b/ src/piece/piece.c

@@ -0,0 +1,52 @@
#include <ctype.h>
#include <stdlib.h>
#include "attack.h"
#include "board.h"
struct Piece {
attack_f attacks;
char *unicode;
ePiece piece;
eColor color;
char code;
char asci;
};
// clang-format off
const struct Piece Pieces[2][6] = {
[WHITE] = {
[KNIGHT] = {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[QUEEN] = {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks},
[PAWN] = {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks},
[ROOK] = {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks},
},
[BLACK] = {
[KNIGHT] = {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[QUEEN] = {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks},
[PAWN] = {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks},
[ROOK] = {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks},
},
};
// clang-format on
attack_f piece_attacks(Piece self) { return self->attacks; }
char piece_asci(Piece self) { return self->asci; }
char piece_code(Piece self) { return self->code; }
char *piece_unicode(Piece self) { return self->unicode; }
eColor piece_color(Piece self) { return self->color; }
ePiece piece_piece(Piece self) { return self->piece; }
int piece_index(Piece self) { return self->color * 8 + self->piece; }
Piece piece_get(ePiece piece, eColor color) { return &Pieces[color][piece]; }
Piece piece_from_index(int index) { return &Pieces[index / 8][index % 8]; }
Piece piece_from_code(char code) {
int color = (isupper(code)) ? WHITE : BLACK;
for (int i = 0; i < 6; i++)
if (Pieces[color][i].code == code) return &Pieces[color][i];
return NULL;
}