stellar

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

commitfefa1caf09190a7a6c45541e6e5496e73d9ca9f3
parent37f488a52d4d9bd520d73e94fe8a8f21f6348e28
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateSun, 13 Aug 2023 12:52:24 +0200

Rework perft to use std::thread

Diffstat:
Dsrc/include/perft.hpp|------------------------
Msrc/perft/perft.cpp|++++++++++++++++++++++++++++++++++++----------------------------------------------

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