use crate::Move;
use std::sync::atomic::Ordering;
use std::{collections::BTreeMap, sync::Mutex};
use std::{
collections::VecDeque,
sync::{atomic::AtomicU32, Arc},
};
use futures::executor::ThreadPool;
use crate::board::Board;
#[inline]
pub fn perft(board: &mut Board, depth: usize) -> usize {
let moves = board.legal_moves();
let mut final_depth = 0;
if depth == 1 {
moves.len()
} else {
for m in moves.iter() {
let mut board = board.make_move_new(m);
final_depth += perft(&mut board, depth - 1);
}
final_depth
}
}
#[inline]
pub fn perft_async(board: &Board, depth: usize, executor: &ThreadPool) -> usize {
let moves = board.legal_moves();
let final_depth = Arc::new(AtomicU32::new(0));
let mut results = VecDeque::new();
let barrier = Arc::new(AtomicU32::new(moves.len() as u32));
if depth == 1 {
moves.len()
} else {
for m in moves.iter() {
let m = *m;
let final_depth = final_depth.clone();
let mut board = board.clone();
let barrier = barrier.clone();
results.push_back(async move {
let result = async move {
let mut board = board.make_move_new(&m);
let nodes = perft(&mut board, depth - 1);
final_depth.fetch_add(nodes as u32, Ordering::Release);
barrier.fetch_sub(1, Ordering::Release);
};
executor.spawn_ok(result);
});
}
for res in results {
futures::executor::block_on(res);
}
while barrier.load(Ordering::Acquire) != 0 {
std::hint::spin_loop();
}
final_depth.load(Ordering::Acquire) as usize
}
}
#[inline]
pub fn perft_divide(board: &mut Board, depth: usize) -> (BTreeMap<Move, usize>, usize) {
let moves = board.legal_moves();
let mut nodes = BTreeMap::new();
let mut final_nodes = 0;
for m in moves.iter() {
let mut board = board.make_move_new(m);
let moves = perft(&mut board, depth - 1);
final_nodes += moves;
nodes.insert(m.to_owned(), moves);
}
(nodes, final_nodes)
}
#[inline]
pub fn perft_async_divide(
board: &Board,
depth: usize,
executor: &ThreadPool,
) -> (BTreeMap<Move, usize>, usize) {
let moves = board.legal_moves();
let final_depth = Arc::new(AtomicU32::new(0));
let mut results = VecDeque::new();
let barrier = Arc::new(AtomicU32::new(moves.len() as u32));
let nodes = Arc::new(Mutex::new(BTreeMap::new()));
for m in moves.iter() {
let m = *m;
let final_depth = final_depth.clone();
let mut board = board.clone();
let barrier = barrier.clone();
let node_list = nodes.clone();
results.push_back(async move {
let result = async move {
let mut board = board.make_move_new(&m);
let nodes = perft(&mut board, depth - 1);
final_depth.fetch_add(nodes as u32, Ordering::Release);
node_list.lock().unwrap().insert(m, nodes);
barrier.fetch_sub(1, Ordering::Release);
};
executor.spawn_ok(result);
});
}
for res in results {
futures::executor::block_on(res);
}
while barrier.load(Ordering::Acquire) != 0 {
std::hint::spin_loop();
}
let nodes = Arc::into_inner(nodes).unwrap().into_inner().unwrap();
(nodes, final_depth.load(Ordering::Acquire) as usize)
}