stellar

UCI 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>
4 #include "attack.hpp"
5 #include "board.hpp"
6 #include "move.hpp"
7 #include "movelist.hpp"
8 #include "utils.hpp"
10 // FEN debug positions
11 #define tricky_position "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
13 enum {
14 THREAD_MAX = 64
15 };
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);
27 if (depth > 1) {
28 test(board, depth - 1);
29 } else {
30 score(board, move);
31 }
33 mutex.acquire();
34 result += local;
35 mutex.release();
36 sem.release();
37 }
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 };
61 static result_t result;
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 }
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 }
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 };
97 std::binary_semaphore Perft::mutex{1};
98 Perft::result_t Perft::result;
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());
105 Perft::semaphore_t sem(thread_num);
107 int index = 0;
108 for (int i = 0; i < list.size(); i++)
109 threads[index++] = std::thread(Perft(sem), board, list[i], depth);
111 for (auto &thread : threads)
112 thread.join();
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
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;
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();
153 attack::init();
154 zobrist::init();
155 perft_test(fen, depth, thread_num);
156 return 0;