use crate::{ agent::{Agent, RandomAgent}, complexagent::ComplexAgent, game_inner::GameInner, logic::FutureMoveConfig, repr::{Piece, Winner}, }; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use skillratings::{ elo::{elo, EloConfig, EloRating}, Outcomes, Rating, }; pub fn run() { const FMV_BASE: FutureMoveConfig = FutureMoveConfig { max_depth: 20, min_arena_depth: 14, top_k_children: 2, up_to_minus: 10, max_arena_size: 5_000_000, do_not_prune: false, print: false, }; let mut arena = PlayerArena::new(vec![ ( "RandomAgent".into(), Box::new(|piece| Box::new(RandomAgent::new(piece))), ), ( "ComplexAgentD5".into(), Box::new(|piece| { Box::new(ComplexAgent::new( piece, FutureMoveConfig { max_depth: 5, ..FMV_BASE }, )) }), ), ( "ComplexAgentD6".into(), Box::new(|piece| { Box::new(ComplexAgent::new( piece, FutureMoveConfig { max_depth: 6, ..FMV_BASE }, )) }), ), ( "ComplexAgentD7".into(), Box::new(|piece| { Box::new(ComplexAgent::new( piece, FutureMoveConfig { max_depth: 7, ..FMV_BASE }, )) }), ), ]); arena.play( &(0..arena.players.len()) .zip([0].into_iter().cycle()) .filter(|(i, j)| i != j) .collect::>() .repeat(20), ); println!("{}", arena); } pub struct PlayerArena { /// Name, Creator, Elo players: Vec<(String, Box Box>, EloRating)>, } impl std::fmt::Display for PlayerArena { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut players_i: Vec = (0..self.players.len()).collect(); players_i.sort_by_key(|&i| -(self.players[i].2.rating() * 100.0) as i64); for i in players_i { writeln!( f, "({:.2}): {}", self.players[i].2.rating(), self.players[i].0 )?; } Ok(()) } } impl PlayerArena { pub fn new(players: Vec<(String, Box Box>)>) -> Self { Self { players: players .into_iter() .zip([EloRating::new()].into_iter().cycle()) .map(|((a, b), c)| (a, b, c)) .collect(), } } fn play(&mut self, pairs: &[(usize, usize)]) { pairs .into_iter() .map(|&(i, j)| { ( (i, j), Self::create_agents(&self.players[i].1, &self.players[j].1), ) }) .collect::>() // after the agents are created, we can multithread the games being played .into_par_iter() .map(|((i, j), (p1, p2))| (i, j, Self::play_two_inner(p1, p2))) .collect::>() // collect and process the outcomes of all the games .into_iter() .for_each(|(i, j, o)| self.process_outcome(i, j, &o)); } fn prop_arena(&mut self) { self.play( &(0..self.players.len()) .flat_map(|i| { (0..self.players.len()) .map(move |j| (i, j)) .filter(|(i, j)| i != j) .collect::>() }) .collect::>(), ); } fn process_outcome(&mut self, player1: usize, player2: usize, outcome: &Outcomes) { let (np1, np2) = elo( &self.players[player1].2, &self.players[player2].2, outcome, &EloConfig::new(), ); self.players[player1].2 = np1; self.players[player2].2 = np2; } fn create_agents( player_1_fn: &Box Box>, player_2_fn: &Box Box>, ) -> (Box, Box) { (player_1_fn(Piece::Black), player_2_fn(Piece::White)) } fn play_two_inner(player_1: Box, player_2: Box) -> Outcomes { let result = GameInner::new(player_1, player_2, false).loop_until_result(); match result { Winner::Player(piece) => match piece { Piece::Black => Outcomes::WIN, Piece::White => Outcomes::LOSS, }, Winner::Tie => Outcomes::DRAW, Winner::None => panic!("somehow met None"), } } }