1use std::ops::{Add, AddAssign};
9use std::sync::{Arc, Mutex};
10use std::thread;
11
12use crate::coretypes::PlyKind;
13use crate::movelist::MoveList;
14use crate::position::Position;
15
16#[derive(Copy, Clone, Debug, Eq, PartialEq)]
19pub struct PerftInfo {
20 pub nodes: u64,
21}
22
23impl PerftInfo {
24 fn new(nodes: u64) -> Self {
25 PerftInfo { nodes }
26 }
27}
28
29impl Add for PerftInfo {
30 type Output = Self;
31 fn add(self, rhs: Self) -> Self::Output {
32 PerftInfo {
33 nodes: self.nodes + rhs.nodes,
34 }
35 }
36}
37
38impl AddAssign for PerftInfo {
39 fn add_assign(&mut self, rhs: Self) {
40 self.nodes += rhs.nodes;
41 }
42}
43
44pub fn perft(mut position: Position, ply: PlyKind, threads: usize) -> PerftInfo {
48 if ply == 0 {
50 return PerftInfo::new(1);
52 } else if ply <= 2 || threads <= 1 {
53 return perft_recurse(&mut position, ply);
55 }
56 debug_assert!(ply > 2);
57 debug_assert!(threads > 1);
58
59 let legal_moves = position.get_legal_moves();
60 if legal_moves.len() == 0 {
62 return PerftInfo::new(0);
63 }
64
65 let legal_moves = Arc::new(Mutex::new(legal_moves));
66 let total_perft_info = Arc::new(Mutex::new(PerftInfo::new(0)));
67 let mut handles = Vec::new();
68
69 for _ in 0..threads {
71 let position = position.clone();
73 let legal_moves = legal_moves.clone();
74 let total_perft_info = total_perft_info.clone();
75
76 let handle = thread::spawn(move || {
77 perft_executor(position, ply, legal_moves, total_perft_info);
78 });
79
80 handles.push(handle);
81 }
82
83 for handle in handles {
85 handle.join().unwrap();
86 }
87
88 Arc::try_unwrap(total_perft_info)
90 .unwrap()
91 .into_inner()
92 .unwrap()
93}
94
95#[inline(always)]
103fn perft_executor(
104 mut position: Position,
105 ply: PlyKind,
106 moves: Arc<Mutex<MoveList>>,
107 total_perft_info: Arc<Mutex<PerftInfo>>,
108) {
109 debug_assert!(ply > 1);
110 let mut perft_info = PerftInfo::new(0);
111 let mut maybe_move = { moves.lock().unwrap().pop() };
112 let cache = position.cache();
113
114 while let Some(move_) = maybe_move {
115 let move_info = position.do_move(move_);
116 perft_info += perft_recurse(&mut position, ply - 1);
117 position.undo_move(move_info, cache);
118 maybe_move = moves.lock().unwrap().pop();
119 }
120
121 *total_perft_info.lock().unwrap() += perft_info;
122}
123
124fn perft_recurse(position: &mut Position, ply: PlyKind) -> PerftInfo {
126 debug_assert_ne!(ply, 0);
127 let cache = position.cache();
128 if ply == 1 {
129 PerftInfo::new(position.get_legal_moves().len() as u64)
132 } else {
133 let legal_moves = position.get_legal_moves();
134 let mut perft_info = PerftInfo::new(0);
135 for legal_move in legal_moves {
136 let move_info = position.do_move(legal_move);
137 perft_info += perft_recurse(position, ply - 1);
138 position.undo_move(move_info, cache);
139 }
140 perft_info
141 }
142}