stellarUCI Chess engine written in C++20 |
git clone git://git.dimitrijedobrota.com/stellar.git |
Log | Files | Refs | README | LICENSE |
perft.cpp (4402B)
0 #include <iomanip> 1 #include <semaphore> 2 #include <thread> 3 4 #include "attack.hpp" 5 #include "board.hpp" 6 #include "move.hpp" 7 #include "movelist.hpp" 8 #include "utils.hpp" 9 10 // FEN debug positions 11 #define tricky_position "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 " 12 13 enum { 14 THREAD_MAX = 64 15 }; 16 17 class Perft { 18 public: 19 using semaphore_t = std::counting_semaphore<THREAD_MAX>; 20 Perft(semaphore_t &sem) : sem(sem) {} 21 void operator()(const Board &board_start, Move move, int depth) { 22 Board board = board_start; 23 if (!move.make(board)) return; 24 sem.acquire(); 25 // debug(board_start, move, board); 26 27 if (depth > 1) { 28 test(board, depth - 1); 29 } else { 30 score(board, move); 31 } 32 33 mutex.acquire(); 34 result += local; 35 mutex.release(); 36 sem.release(); 37 } 38 39 struct result_t { 40 U64 node = 0; 41 #ifdef USE_FULL_COUNT 42 U64 check = 0; 43 U64 castle = 0; 44 U64 promote = 0; 45 U64 capture = 0; 46 U64 enpassant = 0; 47 #endif 48 result_t &operator+=(const result_t res) { 49 node += res.node; 50 #ifdef USE_FULL_COUNT 51 check += res.check; 52 castle += res.castle; 53 promote += res.promote; 54 capture += res.capture; 55 enpassant += res.enpassant; 56 #endif 57 return *this; 58 } 59 }; 60 61 static result_t result; 62 63 private: 64 void test(const Board &board, int depth) { 65 const MoveList list(board); 66 for (int i = 0; i < list.size(); i++) { 67 Board copy = board; 68 if (!list[i].make(copy)) continue; 69 // debug(board, list[i], copy); 70 if (depth != 1) test(copy, depth - 1); 71 else 72 score(copy, list[i]); 73 } 74 } 75 76 void debug(const Board &before, Move move, const Board &after) { 77 std::cout << std::setw(16) << std::hex << before.get_hash() << " "; 78 std::cout << move << " "; 79 std::cout << std::setw(16) << std::hex << after.get_hash() << "\n"; 80 } 81 82 void score(const Board &board, Move move) { 83 local.node++; 84 #ifdef USE_FULL_COUNT 85 if (board.is_check()) local.check++; 86 if (move.is_capture()) local.capture++; 87 if (move.is_enpassant()) local.enpassant++; 88 if (move.is_castle()) local.castle++; 89 if (move.is_promote()) local.promote++; 90 #endif 91 } 92 result_t local; 93 semaphore_t &sem; 94 static std::binary_semaphore mutex; 95 }; 96 97 std::binary_semaphore Perft::mutex{1}; 98 Perft::result_t Perft::result; 99 100 void perft_test(const char *fen, int depth, int thread_num) { 101 const Board board = Board(fen); 102 const MoveList list = MoveList(board); 103 std::vector<std::thread> threads(list.size()); 104 105 Perft::semaphore_t sem(thread_num); 106 107 int index = 0; 108 for (int i = 0; i < list.size(); i++) 109 threads[index++] = std::thread(Perft(sem), board, list[i], depth); 110 111 for (auto &thread : threads) 112 thread.join(); 113 114 std::cout << std::dec; 115 std::cout << " Nodes: " << Perft::result.node << "\n"; 116 #ifdef USE_FULL_COUNT 117 std::cout << " Captures: " << Perft::result.capture << "\n"; 118 std::cout << "Enpassants: " << Perft::result.enpassant << "\n"; 119 std::cout << " Castles: " << Perft::result.castle << "\n"; 120 std::cout << "Promotions: " << Perft::result.promote << "\n"; 121 std::cout << " Checks: " << Perft::result.check << "\n"; 122 #endif 123 } 124 125 void usage(const char *program) { 126 std::cout << "Usage: " << program; 127 std::cout << " [-h]"; 128 std::cout << " [-t thread number]"; 129 std::cout << " [-d depth]"; 130 std::cout << " [-f fen]" << std::endl; 131 } 132 133 int main(int argc, char *argv[]) { 134 int c = 0, depth = 1, thread_num = 1; 135 std::string s(start_position); 136 const char *fen = s.data(); 137 while ((c = getopt(argc, argv, "ht:f:d:")) != -1) { 138 switch (c) { 139 case 't': 140 thread_num = atoi(optarg); 141 if (thread_num <= 0 && thread_num > THREAD_MAX) abort(); 142 break; 143 case 'f': fen = optarg; break; 144 case 'd': 145 depth = atoi(optarg); 146 if (depth <= 0) abort(); 147 break; 148 case 'h': usage(argv[0]); return 1; 149 default: usage(argv[0]); abort(); 150 } 151 } 152 153 attack::init(); 154 zobrist::init(); 155 perft_test(fen, depth, thread_num); 156 return 0; 157 }