Skip to content

Commit

Permalink
Parallelize comparing evaluators
Browse files Browse the repository at this point in the history
  • Loading branch information
carsten-wenderdel committed Oct 10, 2023
1 parent 8f41abc commit 178d73b
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 34 deletions.
40 changes: 29 additions & 11 deletions crates/coach/src/bin/compare-evaluators.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
use coach::duel::Duel;
use engine::dice::FastrandDice;
use engine::onnx::OnnxEvaluator;
use engine::probabilities::{Probabilities, ResultCounter};
use rayon::prelude::*;
use std::io::{stdout, Write};

fn main() {
let evaluator1 = OnnxEvaluator::from_file_path("neural-nets/wildbg.onnx").unwrap();
let evaluator2 = OnnxEvaluator::from_file_path("neural-nets/wildbg01.onnx").unwrap();
let mut duel = Duel::new(evaluator1, evaluator2);
let evaluator1 = OnnxEvaluator::from_file_path("neural-nets/wildbg.onnx")
.expect("Neural net for evaluator1 could not be found");
let evaluator2 = OnnxEvaluator::from_file_path("neural-nets/wildbg01.onnx")
.expect("Neural net for evaluator2 could not be found");
let duel = Duel::new(evaluator1, evaluator2);

println!("Let two Evaluators duel each other:");
for _ in 0..100_000 {
duel.duel_once();
let probabilities = duel.probabilities();
let mut dice_gen = FastrandDice::new();
let mut global_counter = ResultCounter::default();

loop {
// If we create n seeds, than n duels are played in parallel which gives us 2*n GameResults.
// When the duels have finished, the 2*n results are reduced to a single one and then
// added to the `global_counter`.
// Those global results are printed out and the endless loop starts again.
let seeds: Vec<u64> = (0..50).map(|_| dice_gen.seed()).collect();
let counter = seeds
.into_par_iter()
.map(|seed| duel.duel(&mut FastrandDice::with_seed(seed)))
.reduce(ResultCounter::default, |a, b| a.combine(&b));

global_counter = global_counter.combine(&counter);
let probabilities = Probabilities::from(&global_counter);
let better_evaluator = if probabilities.equity() > 0.0 { 1 } else { 2 };
print!(
"\rAfter {} games is the equity {:.3}. {:?}",
duel.number_of_games(),
"\rEvaluator {} is leading. After {:.1} thousand games the equity is {:.3}. {:?}",
better_evaluator,
global_counter.sum() as f32 / 1000.0,
probabilities.equity(),
probabilities,
);
stdout().flush().unwrap()
stdout().flush().unwrap();
}
println!("\nDone");
}
33 changes: 10 additions & 23 deletions crates/coach/src/duel.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,35 @@
use engine::dice::{DiceGen, FastrandDice};
use engine::dice::DiceGen;
use engine::evaluator::Evaluator;
use engine::position::GameState::{GameOver, Ongoing};
use engine::position::STARTING;
use engine::probabilities::{Probabilities, ResultCounter};
use engine::probabilities::ResultCounter;

pub struct Duel<T: Evaluator, U: Evaluator> {
evaluator1: T,
evaluator2: U,
dice_gen: FastrandDice,
counter: ResultCounter,
}

/// Let two `Evaluator`s duel each other. A bit quick and dirty.
impl<T: Evaluator, U: Evaluator> Duel<T, U> {
#[allow(clippy::new_without_default)]
pub fn new(evaluator1: T, evaluator2: U) -> Self {
Self::with_dice_gen(evaluator1, evaluator2, FastrandDice::new())
}

fn with_dice_gen(evaluator1: T, evaluator2: U, dice_gen: FastrandDice) -> Self {
Duel {
Self {
evaluator1,
evaluator2,
dice_gen,
counter: ResultCounter::default(),
}
}

pub fn number_of_games(&self) -> u32 {
self.counter.sum()
}

pub fn probabilities(&self) -> Probabilities {
Probabilities::from(&self.counter)
}

/// The two `Evaluator`s will play twice each against each other.
/// Either `Evaluator` will start once and play with the same dice as vice versa.
pub fn duel_once(&mut self) {
pub fn duel<V: DiceGen>(&self, dice_gen: &mut V) -> ResultCounter {
let mut pos1 = STARTING;
let mut pos2 = STARTING;
let mut iteration = 0;
let mut pos1_finished = false;
let mut pos2_finished = false;
let mut counter = ResultCounter::default();
while !(pos1_finished && pos2_finished) {
let dice = self.dice_gen.roll();
let dice = dice_gen.roll();

match pos1.game_state() {
Ongoing => {
Expand All @@ -62,7 +47,7 @@ impl<T: Evaluator, U: Evaluator> Duel<T, U> {
} else {
result.reverse()
};
self.counter.add(result);
counter.add(result);
}
}
}
Expand All @@ -82,11 +67,13 @@ impl<T: Evaluator, U: Evaluator> Duel<T, U> {
} else {
result
};
self.counter.add(result);
counter.add(result);
}
}
}
iteration += 1;
}
debug_assert!(counter.sum() == 2, "Each duel should have two game results");
counter
}
}

0 comments on commit 178d73b

Please sign in to comment.