stellar

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

commit 6056b66a9b9eac41ea5d442fc9f877598e9a6eed
parent bb400a401c05a3cdd711086350996316a3a428ba
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Mon, 31 Jul 2023 00:25:51 +0200

Improve perft binary, optional move counting

Diffstat:
MCMakeLists.txt | 2+-
Msrc/board/board.c | 2+-
Msrc/include/board.h | 2+-
Msrc/include/perft.h | 19+++++++++++++++++--
Msrc/include/utils.h | 2+-
Msrc/perft/CMakeLists.txt | 16++++++++++++++++
Msrc/perft/perft.c | 215++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/utils/utils.c | 4++--
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'); }