othello/src/elo.rs

102 lines
2.8 KiB
Rust

use crate::{
agent::{Agent, RandomAgent},
complexagent::ComplexAgent,
game_inner::GameInner,
logic::FutureMoveConfig,
repr::{Piece, Winner},
};
use skillratings::{
elo::{elo, EloConfig, EloRating},
Outcomes, Rating,
};
pub fn run() {
let mut arena = PlayerArena::new(vec![
(
"RandomAgent".into(),
Box::new(|piece| Box::new(RandomAgent::new(piece))),
),
(
"ComplexAgentBasic".into(),
Box::new(|piece| {
Box::new(ComplexAgent::new(
piece,
FutureMoveConfig {
max_depth: 20,
min_arena_depth: 14,
top_k_children: 2,
up_to_minus: 10,
max_arena_size: 100_000,
do_not_prune: false,
print: false,
},
))
}),
),
]);
for _ in 0..10 {
arena.play_two(0, 1);
}
println!("{}", arena);
}
pub struct PlayerArena {
players: Vec<(String, Box<dyn Fn(Piece) -> Box<dyn Agent>>, EloRating)>,
}
impl std::fmt::Display for PlayerArena {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut players_i: Vec<usize> = (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<dyn Fn(Piece) -> Box<dyn Agent>>)>) -> Self {
Self {
players: players
.into_iter()
.zip([EloRating::new()].into_iter().cycle())
.map(|((a, b), c)| (a, b, c))
.collect(),
}
}
fn play_two(&mut self, player1: usize, player2: usize) {
let result = GameInner::new(
self.players[player1].1(Piece::Black),
self.players[player2].1(Piece::White),
false,
)
.loop_until_result();
let outcome = 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"),
};
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;
}
}