stellar

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 21:48:06 +0200

Extract Piece interface

Diffstat:
MCMakeLists.txt | 2+-
Msrc/attack/attack.c | 2+-
Msrc/board/board.c | 96++++++++++++++++---------------------------------------------------------------
Msrc/engine/CMakeLists.txt | 1+
Msrc/engine/engine.c | 19+++++++++----------
Msrc/include/board.h | 22++++------------------
Msrc/include/moves.h | 6+++---
Asrc/include/piece.h | 36++++++++++++++++++++++++++++++++++++
Msrc/include/utils.h | 17-----------------
Msrc/moves/moves.c | 26+++++++++++++-------------
Msrc/perft/CMakeLists.txt | 1+
Msrc/perft/perft.c | 2+-
Asrc/piece/CMakeLists.txt | 7+++++++
Asrc/piece/piece.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +}