stellar

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

perft.cpp (4343B)


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