stellarUCI 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;
}