1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use std::fmt::{Debug, Formatter};

use internal_iterator::InternalIterator;
use rand::Rng;

use crate::ai::Bot;
use crate::board::Board;
use crate::wdl::POV;

pub struct RandomBot<R: Rng> {
    rng: R,
}

impl<R: Rng> Debug for RandomBot<R> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "RandomBot")
    }
}

impl<R: Rng> RandomBot<R> {
    pub fn new(rng: R) -> Self {
        RandomBot { rng }
    }
}

impl<B: Board, R: Rng> Bot<B> for RandomBot<R> {
    fn select_move(&mut self, board: &B) -> B::Move {
        board.random_available_move(&mut self.rng)
    }
}

pub struct RolloutBot<R: Rng> {
    rollouts: u32,
    rng: R,
}

impl<R: Rng> Debug for RolloutBot<R> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "RolloutBot {{ rollouts: {} }}", self.rollouts)
    }
}

impl<R: Rng> RolloutBot<R> {
    pub fn new(rollouts: u32, rng: R) -> Self {
        RolloutBot { rollouts, rng }
    }
}

impl<B: Board, R: Rng> Bot<B> for RolloutBot<R> {
    fn select_move(&mut self, board: &B) -> B::Move {
        let rollouts_per_move = self.rollouts / board.available_moves().count() as u32;

        board.available_moves().max_by_key(|&mv| {
            let child = board.clone_and_play(mv);

            let score: i64 = (0..rollouts_per_move).map(|_| {
                let mut copy = child.clone();
                while !copy.is_done() {
                    copy.play(copy.random_available_move(&mut self.rng))
                }
                copy.outcome().unwrap().pov(board.next_player()).sign::<i64>()
            }).sum();

            score
        }).unwrap()
    }
}