stellar

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

commit6056b66a9b9eac41ea5d442fc9f877598e9a6eed
parentbb400a401c05a3cdd711086350996316a3a428ba
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateSun, 30 Jul 2023 22:25:51 +0200

Improve perft binary, optional move counting

Diffstat:
MCMakeLists.txt|+-
Msrc/board/board.c|+-
Msrc/include/board.h|+-
Msrc/include/perft.h|+++++++++++++++++--
Msrc/include/utils.h|+-
Msrc/perft/CMakeLists.txt|++++++++++++++++
Msrc/perft/perft.c|++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/utils/utils.c|++--

8 files changed, 164 insertions(+), 98 deletions(-)


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

@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(
Stellar
VERSION 0.0.4
VERSION 0.0.5
DESCRIPTION "Chess engine written in C"
HOMEPAGE_URL https://git.dimitrijedobrota.com/stellar.git
LANGUAGES C

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

@@ -158,7 +158,7 @@ void board_print(Board self) {

printf("\n");
}
Board board_from_FEN(Board board, char *fen) {
Board board_from_FEN(Board board, const char *fen) {
if (!board) NEW(board);
memset(board, 0, sizeof(*board));

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

@@ -46,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_from_FEN(Board board, char *fen);
Board board_from_FEN(Board board, const char *fen);
int board_isCheck(Board self);
void board_print(Board self);
void board_side_switch(Board self);

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

@@ -3,7 +3,22 @@

#include "board.h"
void perft_test_threaded(Board board, int depth);
void perft_test(Board board, int depth);
typedef unsigned long long ull;
typedef struct PerftResult PerftResult;
struct PerftResult {
ull node;
#ifdef USE_FULL_COUNT
ull capture;
ull enpassant;
ull castle;
ull promote;
ull check;
// ull checkDiscovered;
// ull checkDouble;
// ull checkmate;
#endif
};
PerftResult perft_test(const char *fen, int depth, int thread_num);
#endif

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

@@ -45,7 +45,7 @@ enum enumSquare {

typedef enum enumSquare Square;
extern const char *square_to_coordinates[];
Square coordinates_to_square(char *cord);
Square coordinates_to_square(const char *cord);
// board moving
typedef U64 (*direction_f)(U64);

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

@@ -1,5 +1,21 @@

project(
perft
VERSION 1.0.0
DESCRIPTION "Performance Test"
LANGUAGES C
)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
add_executable(perft perft.c)
option(WITH_FULL_COUNT "Make count on types of moves" OFF)
if(WITH_FULL_COUNT)
add_definitions(-DUSE_FULL_COUNT)
endif()
target_link_libraries(perft
PRIVATE attacks
PRIVATE board

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

@@ -1,6 +1,11 @@

#include <pthread.h>
#include <semaphore.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <cul/assert.h>
#include "attacks.h"
#include "board.h"

@@ -8,13 +13,47 @@

#include "perft.h"
#include "utils.h"
struct MoveList moveList[10];
long nodes;
// 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");
printf(" Nodes: %llu\n", res.node);
#ifdef USE_FULL_COUNT
printf(" Captures: %llu\n", res.capture);
printf(" Enpassants: %llu\n", res.enpassant);
printf(" Castles: %llu\n", res.castle);
printf(" Promotions: %llu\n", res.promote);
printf(" Checks: %llu\n", res.check);
// printf("Discovered Checks: %llu\n", res.checkDiscovered);
// printf(" Dobule Checks: %llu\n", res.checkDouble);
// printf(" Checkmates: %llu\n", res.checkmate);
#endif
}
void perft_result_add(PerftResult *base, PerftResult *add) {
base->node += add->node;
#ifdef USE_FULL_COUNT
base->capture += add->capture;
base->enpassant += add->enpassant;
base->castle += add->castle;
base->promote += add->promote;
base->check += add->check;
// base->checkDiscovered += add->checkDiscovered;
// base->checkDouble += add->checkDouble;
// base->checkmate += add->checkmate;
#endif
}
void perft_driver(Board board, struct MoveList *moveList, int depth,
unsigned long long *nodes) {
PerftResult *result) {
if (depth == 0) {
(*nodes)++;
result->node++;
return;
}

@@ -22,124 +61,120 @@ void perft_driver(Board board, struct MoveList *moveList, int depth,

Board copy = board_new();
for (int i = 0; i < move_list_size(list); i++) {
Move move = move_list_move(list, i);
board_copy(board, copy);
if (!move_make(move_list_move(list, i), copy, 0)) continue;
perft_driver(copy, moveList, depth - 1, nodes);
}
if (!move_make(move, copy, 0)) continue;
move_list_reset(list);
board_free(&copy);
}
#ifdef USE_FULL_COUNT
if (board_isCheck(copy)) result->check++;
if (move_capture(move)) result->capture++;
if (move_enpassant(move)) result->enpassant++;
if (move_castle(move)) result->castle++;
if (move_promote(move)) result->promote++;
#endif
void perft_test(Board board, int depth) {
MoveList list = move_list_generate(&moveList[depth], board);
Board copy = board_new();
long start = get_time_ms();
printf("\n Performance test\n\n");
nodes = 0;
for (int i = 0; i < move_list_size(list); i++) {
board_copy(board, copy);
Move move = move_list_move(list, i);
if (!move_make(move_list_move(list, i), copy, 0)) continue;
unsigned long long node = 0;
perft_driver(copy, moveList, depth - 1, &node);
printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)], node);
nodes += node;
perft_driver(copy, moveList, depth - 1, result);
}
move_list_reset(list);
board_free(&copy);
printf("\nNodes searched: %ld\n\n", nodes);
printf("\n Depth: %d\n", depth);
printf(" Nodes: %ld\n", nodes);
printf(" Time: %ld\n\n", get_time_ms() - start);
}
typedef struct perf_shared perf_shared;
struct perf_shared {
Board board;
const char *fen;
MoveList list;
int depth;
sem_t *mutex;
int *index;
sem_t *finish;
unsigned long long *total;
pthread_mutex_t mutex;
unsigned int index;
sem_t finish;
PerftResult result;
};
void *perft_thread(void *arg) {
PerftResult result = {0};
perf_shared *shared = (perf_shared *)arg;
Board board = board_new();
unsigned long long node_count = 0;
struct MoveList moveList[shared->depth + 1];
struct MoveList moveList[10];
Board board = board_from_FEN(NULL, shared->fen);
Board copy = board_new();
while (1) {
sem_wait(shared->mutex);
*shared->total += node_count;
if (*shared->index >= move_list_size(shared->list)) {
sem_post(shared->mutex);
pthread_mutex_lock(&shared->mutex);
perft_result_add(&shared->result, &result);
if (shared->index >= move_list_size(shared->list)) {
pthread_mutex_unlock(&shared->mutex);
break;
}
Move move = move_list_move(shared->list, (*shared->index)++);
sem_post(shared->mutex);
Move move = move_list_move(shared->list, shared->index++);
pthread_mutex_unlock(&shared->mutex);
result = (PerftResult){0};
board_copy(board, copy);
if (!move_make(move, copy, 0)) continue;
board_copy(shared->board, board);
if (!move_make(move, board, 0)) continue;
#ifdef USE_FULL_COUNT
if (board_isCheck(copy)) result.check++;
if (move_capture(move)) result.capture++;
if (move_enpassant(move)) result.enpassant++;
if (move_castle(move)) result.castle++;
if (move_promote(move)) result.promote++;
#endif
node_count = 0;
perft_driver(board, moveList, shared->depth, &node_count);
printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)], node_count);
perft_driver(copy, moveList, shared->depth, &result);
}
board_free(&board);
sem_post(shared->finish);
sem_post(&shared->finish);
return NULL;
}
void perft_test_threaded(Board board, int depth) {
MoveList list = move_list_generate(NULL, board);
int size = 8;
unsigned long long total = 0;
perf_shared shared[size];
pthread_t threads[size];
sem_t mutex, finish;
int index = 0;
sem_init(&mutex, 0, 1);
sem_init(&finish, 0, 0);
for (int i = 0; i < size; i++) {
shared[i].board = board;
shared[i].list = list;
shared[i].depth = depth - 1;
shared[i].mutex = &mutex;
shared[i].index = &index;
shared[i].finish = &finish;
shared[i].total = &total;
pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
}
PerftResult perft_test(const char *fen, int depth, int thread_num) {
assert(fen);
assert(depth > 0);
pthread_t threads[thread_num];
perf_shared shared = (perf_shared){
.list = move_list_generate(NULL, board_from_FEN(NULL, fen)),
.depth = depth - 1,
.fen = fen,
};
for (int i = 0; i < size; i++)
sem_wait(&finish);
move_list_free(&list);
sem_init(&shared.finish, 0, 0);
pthread_mutex_init(&shared.mutex, NULL);
for (int i = 0; i < thread_num; i++)
pthread_create(threads + i, NULL, perft_thread, (void *)(&shared));
printf("Nodes processed: %llu\n", total);
for (int i = 0; i < thread_num; i++)
sem_wait(&shared.finish);
move_list_free(&shared.list);
return shared.result;
}
// 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 "
int main(int argc, char *argv[]) {
int c, depth = 1, thread_num = 1;
char *fen = start_position;
while ((c = getopt(argc, argv, "t:f:d:")) != -1) {
switch (c) {
case 't':
thread_num = atoi(optarg);
if (thread_num <= 0) abort();
break;
case 'f':
fen = optarg;
break;
case 'd':
depth = atoi(optarg);
if (depth <= 0) abort();
break;
default:
abort();
}
}
int main(void) {
attacks_init();
Board board = board_new();
board_from_FEN(board, tricky_position);
perft_test_threaded(board, 5);
PerftResult res = perft_test(fen, depth, thread_num);
perft_result_print(res);
}

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

@@ -36,8 +36,8 @@ const char *square_to_coordinates[]={

"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", " "
};
// clang-format on
//
Square coordinates_to_square(char *cord) {
Square coordinates_to_square(const char *cord) {
return (cord[1] - '1') * 8 + (cord[0] - 'a');
}