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(©); 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 }