use std::ops::{Add, AddAssign};
use std::sync::{Arc, Mutex};
use std::thread;
use crate::coretypes::PlyKind;
use crate::movelist::MoveList;
use crate::position::Position;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PerftInfo {
pub nodes: u64,
}
impl PerftInfo {
fn new(nodes: u64) -> Self {
PerftInfo { nodes }
}
}
impl Add for PerftInfo {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
PerftInfo {
nodes: self.nodes + rhs.nodes,
}
}
}
impl AddAssign for PerftInfo {
fn add_assign(&mut self, rhs: Self) {
self.nodes += rhs.nodes;
}
}
pub fn perft(mut position: Position, ply: PlyKind, threads: usize) -> PerftInfo {
if ply == 0 {
return PerftInfo::new(1);
} else if ply <= 2 || threads <= 1 {
return perft_recurse(&mut position, ply);
}
debug_assert!(ply > 2);
debug_assert!(threads > 1);
let legal_moves = position.get_legal_moves();
if legal_moves.len() == 0 {
return PerftInfo::new(0);
}
let legal_moves = Arc::new(Mutex::new(legal_moves));
let total_perft_info = Arc::new(Mutex::new(PerftInfo::new(0)));
let mut handles = Vec::new();
for _ in 0..threads {
let position = position.clone();
let legal_moves = legal_moves.clone();
let total_perft_info = total_perft_info.clone();
let handle = thread::spawn(move || {
perft_executor(position, ply, legal_moves, total_perft_info);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
Arc::try_unwrap(total_perft_info)
.unwrap()
.into_inner()
.unwrap()
}
#[inline(always)]
fn perft_executor(
mut position: Position,
ply: PlyKind,
moves: Arc<Mutex<MoveList>>,
total_perft_info: Arc<Mutex<PerftInfo>>,
) {
debug_assert!(ply > 1);
let mut perft_info = PerftInfo::new(0);
let mut maybe_move = { moves.lock().unwrap().pop() };
let cache = position.cache();
while let Some(move_) = maybe_move {
let move_info = position.do_move(move_);
perft_info += perft_recurse(&mut position, ply - 1);
position.undo_move(move_info, cache);
maybe_move = moves.lock().unwrap().pop();
}
*total_perft_info.lock().unwrap() += perft_info;
}
fn perft_recurse(position: &mut Position, ply: PlyKind) -> PerftInfo {
debug_assert_ne!(ply, 0);
let cache = position.cache();
if ply == 1 {
PerftInfo::new(position.get_legal_moves().len() as u64)
} else {
let legal_moves = position.get_legal_moves();
let mut perft_info = PerftInfo::new(0);
for legal_move in legal_moves {
let move_info = position.do_move(legal_move);
perft_info += perft_recurse(position, ply - 1);
position.undo_move(move_info, cache);
}
perft_info
}
}