use std::time::Duration;
use crate::{board::{GameBoard, Turn}, network::Network, player::{Killers, MinimaxPlayer, ScoreCache, SearchReport, TranspositionTable}};
/// A computer player that searches like `ClaudePlayer` but scores positions with a neural network
/// trained by self-play (see `train.rs`), so it has no human idea of what a good position is.
pub struct NeuralPlayer
{
pub net: Network,
pub time_limit: Duration,
/// The most plies to search, counting the move itself; normally `time_limit` stops the search first.
pub max_depth: usize,
cache: TranspositionTable,
killers: Killers,
last_search: Option<SearchReport>,
}
impl NeuralPlayer
{
/// A player with a `1 << table_bits`-slot transposition table.
pub fn new(net: Network, time_limit: Duration, table_bits: u32) -> Self
{
Self { net, time_limit, max_depth: 64, cache: TranspositionTable::new(table_bits), killers: Killers::default(), last_search: None }
}
}
impl MinimaxPlayer for NeuralPlayer
{
/// The network's output, in -1..1; forced wins are still ±`WIN` from the search itself.
fn evaluate(&mut self, board: &GameBoard, turn: Turn) -> f32
{
self.net.forward(board, turn)
}
fn name(&self) -> String { "Neural".to_string() }
fn set_last_search(&mut self, report: Option<SearchReport>) { self.last_search = report; }
fn last_search(&self) -> Option<SearchReport> { self.last_search }
fn time_limit(&self) -> Duration { self.time_limit }
fn depth(&self) -> usize { self.max_depth }
fn cache(&mut self) -> Option<&mut dyn ScoreCache> { Some(&mut self.cache) }
fn killers(&mut self) -> Option<&mut Killers> { Some(&mut self.killers) }
}
#[cfg(test)]
mod tests
{
use super::*;
use crate::player::Player;
#[test]
fn pick_move_reports_its_search()
{
let mut player = NeuralPlayer::new(Network::random(2), Duration::from_secs(3600), 12);
player.max_depth = 3;
let board = GameBoard::default();
let m = player.pick_move(board, Turn::Sheep, &[]).unwrap();
assert_eq!(board.check_move(m), Ok(()));
let report = player.last_search.expect("a report");
assert_eq!((report.mv, report.depth), (m, 3));
assert_eq!(report.score, player.minimax(board, Turn::Sheep, 3, f32::NEG_INFINITY, f32::INFINITY, None, &mut Vec::new()).unwrap());
}
}