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:
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(©);
-}
+#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(©);
-
- 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');
}