stellar

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

commit fefa1caf09190a7a6c45541e6e5496e73d9ca9f3
parent 37f488a52d4d9bd520d73e94fe8a8f21f6348e28
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Sun, 13 Aug 2023 14:52:24 +0200

Rework perft to use std::thread

Diffstat:
Dsrc/include/perft.hpp | 24------------------------
Msrc/perft/perft.cpp | 196++++++++++++++++++++++++++++++++++---------------------------------------------
2 files changed, 85 insertions(+), 135 deletions(-)

diff --git a/src/include/perft.hpp b/src/include/perft.hpp @@ -1,24 +0,0 @@ -#ifndef STELLAR_PERFT_H -#define STELLAR_PERFT_H - -#include "board.hpp" - -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/perft/perft.cpp b/src/perft/perft.cpp @@ -1,139 +1,113 @@ #include <iomanip> -#include <pthread.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> +#include <semaphore> +#include <thread> -#include "attack.hpp" #include "board.hpp" #include "move.hpp" #include "movelist.hpp" -#include "perft.hpp" #include "utils_cpp.hpp" -#include "zobrist.hpp" // FEN debug positions #define tricky_position "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 " -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); -#else - printf("Other stats are disabled in this build...\n"); -#endif -} +#define THREAD_MAX 64 + +class Perft { + public: + typedef std::counting_semaphore<THREAD_MAX> semaphore_t; + Perft(semaphore_t &sem) : sem(sem) {} + void operator()(const Board &board_start, const Move &move, int depth) { + sem.acquire(); + Board board = board_start; + if (move.make(board, 0)) { + if (depth > 1) + test(board, depth - 1); + else + score(board, move); + } + mutex.acquire(); + result += local; + mutex.release(); + sem.release(); + } -void perft_result_add(PerftResult *base, PerftResult *add) { - base->node += add->node; + struct result_t { + U64 node = 0; #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; + U64 check = 0; + U64 castle = 0; + U64 promote = 0; + U64 capture = 0; + U64 enpassant = 0; #endif -} - -void perft_driver(Board &board, int depth, PerftResult *result) { - Board copy; - for (const auto [move, _] : MoveList(board)) { - copy = board; - if (!move.make(copy, 0)) continue; - - if (depth != 1) { - perft_driver(copy, depth - 1, result); - } else { - result->node++; + result_t &operator+=(const result_t res) { + node += res.node; #ifdef USE_FULL_COUNT - if (copy.is_check()) result->check++; - if (move.get_capture()) result->capture++; - if (move.get_enpassant()) result->enpassant++; - if (move.get_castle()) result->castle++; - if (move.get_promote()) result->promote++; + check += res.check; + castle += res.castle; + promote += res.promote; + capture += res.capture; + enpassant += res.enpassant; #endif + return *this; } - } -} + }; -typedef struct perf_shared perf_shared; -struct perf_shared { - const MoveList &list; - int depth; - const char *fen; - pthread_mutex_t mutex; - unsigned int index; - PerftResult result; -}; + static result_t result; -void *perft_thread(void *arg) { - PerftResult result = {0}; - perf_shared *shared = (perf_shared *)arg; - Board board = Board(shared->fen), copy; + private: + void test(const Board &board, int depth) { + for (const auto [move, _] : MoveList(board)) { + Board copy = board; + if (!move.make(copy, 0)) continue; - while (1) { - pthread_mutex_lock(&shared->mutex); - perft_result_add(&shared->result, &result); - if (shared->index >= shared->list.size()) { - pthread_mutex_unlock(&shared->mutex); - break; + if (depth != 1) + test(copy, depth - 1); + else + score(copy, move); } - Move move = shared->list[shared->index++].move; - pthread_mutex_unlock(&shared->mutex); - - result = {0}; - - copy = board; - if (!move.make(copy, 0)) continue; - - if (shared->depth != 1) { - perft_driver(copy, shared->depth - 1, &result); - } else { - result.node++; + } + void score(const Board &board, const Move &move) { + local.node++; #ifdef USE_FULL_COUNT - if (copy.is_check()) result.check++; - if (move.get_capture()) result.capture++; - if (move.get_enpassant()) result.enpassant++; - if (move.get_castle()) result.castle++; - if (move.get_promote()) result.promote++; + if (board.is_check()) local.check++; + if (move.is_capture()) local.capture++; + if (move.is_enpassant()) local.enpassant++; + if (move.is_castle()) local.castle++; + if (move.is_promote()) local.promote++; #endif - } } - return NULL; -} -PerftResult perft_test(const char *fen, int depth, int thread_num) { - pthread_t *threads = new pthread_t(thread_num); - MoveList *list = new MoveList(Board(fen)); + result_t local; + semaphore_t &sem; + static std::binary_semaphore mutex; +}; - perf_shared shared = { - .list = *list, - .depth = depth, - .fen = fen, - }; +std::binary_semaphore Perft::mutex{1}; +Perft::result_t Perft::result; + +void perft_test(const char *fen, int depth, int thread_num) { + const Board board = Board(fen); + const MoveList list = MoveList(board); + std::vector<std::thread> threads(list.size()); - pthread_mutex_init(&shared.mutex, NULL); - for (int i = 0; i < thread_num; i++) - pthread_create(threads + i, NULL, perft_thread, (void *)(&shared)); + Perft::semaphore_t sem(thread_num); - for (int i = 0; i < thread_num; i++) - pthread_join(threads[i], NULL); + int index = 0; + for (const auto &[move, _] : list) + threads[index++] = std::thread(Perft(sem), board, move, depth); - delete list; - delete threads; - return shared.result; + for (auto &thread : threads) + thread.join(); + + std::cout << " Nodes: " << Perft::result.node << "\n"; +#ifdef USE_FULL_COUNT + std::cout << " Captures: " << Perft::result.capture << "\n"; + std::cout << "Enpassants: " << Perft::result.enpassant << "\n"; + std::cout << " Castles: " << Perft::result.castle << "\n"; + std::cout << "Promotions: " << Perft::result.promote << "\n"; + std::cout << " Checks: " << Perft::result.check << "\n"; +#endif } int main(int argc, char *argv[]) { @@ -144,7 +118,7 @@ int main(int argc, char *argv[]) { switch (c) { case 't': thread_num = atoi(optarg); - if (thread_num <= 0) abort(); + if (thread_num <= 0 && thread_num > THREAD_MAX) abort(); break; case 'f': fen = optarg; @@ -158,6 +132,6 @@ int main(int argc, char *argv[]) { } } - PerftResult res = perft_test(fen, depth, thread_num); - perft_result_print(res); + perft_test(fen, depth, thread_num); + return 0; }