Skip to content

Commit

Permalink
Let position_finder work with any Evaluator
Browse files Browse the repository at this point in the history
Also it now memorizes all positions it encounters as possible moves, not only those actually made.
  • Loading branch information
carsten-wenderdel committed Aug 19, 2023
1 parent 9d1f6c2 commit fa8014e
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 17 deletions.
4 changes: 2 additions & 2 deletions src/bin/generate-training-data.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fs::File;
use std::io::{stdout, Write};
use wildbg::evaluator::{Evaluator, Probabilities};
use wildbg::evaluator::{Evaluator, Probabilities, RandomEvaluator};
use wildbg::inputs::Inputs;
use wildbg::position::Position;
use wildbg::position_finder::PositionFinder;
Expand All @@ -15,7 +15,7 @@ fn main() -> std::io::Result<()> {
file.write_all(csv_header().as_bytes())?;

let amount = 100_000;
let random_positions = PositionFinder::new().find_positions(amount);
let random_positions = PositionFinder::new(RandomEvaluator {}).find_positions(amount);
let evaluator = RolloutEvaluator::new_random();

for (i, position) in random_positions.iter().enumerate() {
Expand Down
9 changes: 7 additions & 2 deletions src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,18 @@ pub trait Evaluator {
/// The returned `Position` has already switches sides.
/// This means the returned position will have the *lowest* equity of possible positions.
fn best_position(&self, pos: &Position, die1: usize, die2: usize) -> Position {
pos.all_positions_after_moving(die1, die2)
self.worst_position(&pos.all_positions_after_moving(die1, die2))
.clone()
}

/// Worst position might be interesting, because when you switch sides, it's suddenly the best.
fn worst_position<'a>(&'a self, positions: &'a [Position]) -> &Position {
positions
.iter()
.map(|pos| (pos, self.eval(pos).equity()))
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
.unwrap()
.0
.clone()
}
}

Expand Down
27 changes: 14 additions & 13 deletions src/position_finder.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
use crate::dice_gen::{DiceGen, FastrandDice};
use crate::evaluator::Evaluator;
use crate::position::GameState::Ongoing;
use crate::position::{Position, STARTING};
use std::collections::HashSet;

/// Finds random positions for later rollout.
pub struct PositionFinder {
pub struct PositionFinder<T: Evaluator> {
evaluator: T,
dice_gen: FastrandDice,
rng: fastrand::Rng, // temporary, will be replaced with selection algorithm once first neural net is there.
}

impl PositionFinder {
impl<T: Evaluator> PositionFinder<T> {
/// Contains different random number generators every time it's called.
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self::with_dice_gen(FastrandDice::new())
}

fn with_dice_gen(dice_gen: FastrandDice) -> Self {
pub fn new(evaluator: T) -> Self {
PositionFinder {
dice_gen,
rng: fastrand::Rng::new(),
evaluator,
dice_gen: FastrandDice::new(),
}
}

Expand All @@ -41,12 +38,16 @@ impl PositionFinder {
let mut positions: Vec<Position> = Vec::new();
let mut pos = STARTING;
while pos.game_state() == Ongoing {
// Todo: Don't allow doubles in first move
let (die1, die2) = self.dice_gen.roll();
let new_positions = pos.all_positions_after_moving(die1, die2);
let random_index = self.rng.usize(0..new_positions.len());
// Todo: remove cloning by implementing the Copy trait -> maybe better performance
pos = new_positions[random_index].clone();
positions.push(pos.clone());
pos = self.evaluator.worst_position(&new_positions).clone();
let mut ongoing_games: Vec<Position> = new_positions
.into_iter()
.filter(|p| p.game_state() == Ongoing)
.collect();
positions.append(&mut ongoing_games);
}
positions
}
Expand Down

0 comments on commit fa8014e

Please sign in to comment.