stellar

Stellar - Chess engine written in C
Log | Files | Refs

perft.c (5549B)


      1 #include <pthread.h>
      2 #include <stdbool.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <unistd.h>
      6 
      7 #include <cul/assert.h>
      8 
      9 #include "attacks.h"
     10 #include "board.h"
     11 #include "moves.h"
     12 #include "perft.h"
     13 #include "utils.h"
     14 #include "zobrist.h"
     15 
     16 // FEN debug positions
     17 #define tricky_position                                                        \
     18     "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
     19 #define killer_position                                                        \
     20     "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1"
     21 #define cmk_position                                                           \
     22     "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
     23 
     24 void perft_result_print(PerftResult res) {
     25     printf("           - Perft Results -\n\n");
     26     printf("            Nodes: %llu\n", res.node);
     27 #ifdef USE_FULL_COUNT
     28     printf("         Captures: %llu\n", res.capture);
     29     printf("       Enpassants: %llu\n", res.enpassant);
     30     printf("          Castles: %llu\n", res.castle);
     31     printf("       Promotions: %llu\n", res.promote);
     32     printf("           Checks: %llu\n", res.check);
     33     //     printf("Discovered Checks: %llu\n", res.checkDiscovered);
     34     //     printf("    Dobule Checks: %llu\n", res.checkDouble);
     35     //     printf("       Checkmates: %llu\n", res.checkmate);
     36 #else
     37     printf("Other stats are disabled in this build...\n");
     38 #endif
     39 }
     40 
     41 void perft_result_add(PerftResult *base, PerftResult *add) {
     42     base->node += add->node;
     43 #ifdef USE_FULL_COUNT
     44     base->capture += add->capture;
     45     base->enpassant += add->enpassant;
     46     base->castle += add->castle;
     47     base->promote += add->promote;
     48     base->check += add->check;
     49     //     base->checkDiscovered += add->checkDiscovered;
     50     //     base->checkDouble += add->checkDouble;
     51     //     base->checkmate += add->checkmate;
     52 #endif
     53 }
     54 
     55 void perft_driver(Board *board, struct MoveList *moveList, int depth,
     56                   PerftResult *result) {
     57     MoveList *list = move_list_generate(&moveList[depth], board);
     58     Board *copy = board_new();
     59 
     60     for (int i = 0; i < move_list_size(list); i++) {
     61         Move move = move_list_index_move(list, i);
     62         board_copy(board, copy);
     63         if (!move_make(move, copy, 0)) continue;
     64 
     65         if (depth != 1) {
     66             perft_driver(copy, moveList, depth - 1, result);
     67         } else {
     68             result->node++;
     69 #ifdef USE_FULL_COUNT
     70             if (board_isCheck(copy)) result->check++;
     71             if (move_capture(move)) result->capture++;
     72             if (move_enpassant(move)) result->enpassant++;
     73             if (move_castle(move)) result->castle++;
     74             if (move_promote(move)) result->promote++;
     75 #endif
     76         }
     77     }
     78 
     79     move_list_reset(list);
     80     board_free(&copy);
     81 }
     82 
     83 typedef struct perf_shared perf_shared;
     84 struct perf_shared {
     85     const char *fen;
     86     MoveList *list;
     87     int depth;
     88     pthread_mutex_t mutex;
     89     unsigned int index;
     90     PerftResult result;
     91 };
     92 
     93 void *perft_thread(void *arg) {
     94     PerftResult result = {0};
     95     perf_shared *shared = (perf_shared *)arg;
     96     struct MoveList moveList[shared->depth + 1];
     97 
     98     Board *board = board_from_FEN(NULL, shared->fen);
     99     Board *copy = board_new();
    100 
    101     while (1) {
    102         pthread_mutex_lock(&shared->mutex);
    103         perft_result_add(&shared->result, &result);
    104         if (shared->index >= move_list_size(shared->list)) {
    105             pthread_mutex_unlock(&shared->mutex);
    106             break;
    107         }
    108         Move move = move_list_index_move(shared->list, shared->index++);
    109         pthread_mutex_unlock(&shared->mutex);
    110 
    111         result = (PerftResult){0};
    112 
    113         board_copy(board, copy);
    114         if (!move_make(move, copy, 0)) continue;
    115 
    116         if (shared->depth != 1) {
    117             perft_driver(copy, moveList, shared->depth - 1, &result);
    118         } else {
    119             result.node++;
    120 #ifdef USE_FULL_COUNT
    121             if (board_isCheck(copy)) result.check++;
    122             if (move_capture(move)) result.capture++;
    123             if (move_enpassant(move)) result.enpassant++;
    124             if (move_castle(move)) result.castle++;
    125             if (move_promote(move)) result.promote++;
    126 #endif
    127         }
    128     }
    129     board_free(&board);
    130     return NULL;
    131 }
    132 
    133 PerftResult perft_test(const char *fen, int depth, int thread_num) {
    134     assert(fen);
    135     assert(depth > 0);
    136 
    137     pthread_t threads[thread_num];
    138     perf_shared shared = (perf_shared){
    139         .list = move_list_generate(NULL, board_from_FEN(NULL, fen)),
    140         .depth = depth,
    141         .fen = fen,
    142     };
    143 
    144     pthread_mutex_init(&shared.mutex, NULL);
    145     for (int i = 0; i < thread_num; i++)
    146         pthread_create(threads + i, NULL, perft_thread, (void *)(&shared));
    147 
    148     for (int i = 0; i < thread_num; i++)
    149         pthread_join(threads[i], NULL);
    150 
    151     move_list_free(&shared.list);
    152     return shared.result;
    153 }
    154 
    155 int main(int argc, char *argv[]) {
    156 
    157     int c, depth = 1, thread_num = 1;
    158     char *fen = start_position;
    159     while ((c = getopt(argc, argv, "t:f:d:")) != -1) {
    160         switch (c) {
    161         case 't':
    162             thread_num = atoi(optarg);
    163             if (thread_num <= 0) abort();
    164             break;
    165         case 'f':
    166             fen = optarg;
    167             break;
    168         case 'd':
    169             depth = atoi(optarg);
    170             if (depth <= 0) abort();
    171             break;
    172         default:
    173             abort();
    174         }
    175     }
    176 
    177     attacks_init();
    178     zobrist_init();
    179 
    180     board_print(board_from_FEN(NULL, fen));
    181     PerftResult res = perft_test(fen, depth, thread_num);
    182     perft_result_print(res);
    183 }