summaryrefslogtreecommitdiff
path: root/src/strategy/monte_carlo.rs
blob: 8fbf0a3e584590c6b00c5aaae574512baa425c1d (plain)
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use engine::settings::GameSettings;
use engine::command::*;
use engine::{GameState, GameStatus};

use rand::{thread_rng, Rng};

const MAX_MOVES: u16 = 400;

// TODO Round start time here
pub fn choose_move(settings: &GameSettings, state: &GameState) -> Command {
    println!("Using MONTE_CARLO strategy");
    
    let mut rng = thread_rng();
    let mut command_scores = CommandScore::init_command_scores(settings, state);

    // TODO Repeat this until time is out
    for _ in 0..1000 {
        for mut score in &mut command_scores {
            if simulate_to_endstate(settings, state, score.command, &mut rng) {
                score.add_victory();
            } else {
                score.add_defeat();
            }
        }
    }

    let command = command_scores.iter().max_by_key(|&c| c.win_ratio());
    
    match command {
        Some(ref command) => command.command,
        _ => Command::Nothing
    }
}

fn simulate_to_endstate<R: Rng>(settings: &GameSettings, state: &GameState, command: Command, rng: &mut R) -> bool {
    let opponent_first = random_opponent_move(settings, state, rng);
    let mut state_mut = state.simulate(settings, command, opponent_first);
    
    for _ in 0..MAX_MOVES {
        if state_mut.status != GameStatus::Continue {
            break;
        }

        let player_command = random_player_move(settings, state, rng);
        let opponent_command = random_opponent_move(settings, state, rng);
        state_mut.simulate_mut(settings, player_command, opponent_command);
        
    }
    
    state_mut.status == GameStatus::PlayerWon
}

fn random_player_move<R: Rng>(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command {
    let all_commands = enumerate_player_commands(settings, state);
    rng.choose(&all_commands).cloned().unwrap_or(Command::Nothing)
}
fn random_opponent_move<R: Rng>(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command {
    let all_commands = enumerate_opponent_commands(settings, state);
    rng.choose(&all_commands).cloned().unwrap_or(Command::Nothing)
}


struct CommandScore {
    command: Command,
    victories: u32,
    attempts: u32
}

impl CommandScore {
    fn new(command: Command) -> CommandScore {
        CommandScore {
            command: command,
            victories: 0,
            attempts: 0
        }
    }

    fn add_victory(&mut self) {
        self.victories += 1;
        self.attempts += 1;
    }

    fn add_defeat(&mut self) {
        self.attempts += 1;
    }

    fn win_ratio(&self) -> u32 {
        self.victories * 1000 / self.attempts
    }
    
    fn init_command_scores(settings: &GameSettings, state: &GameState) -> Vec<CommandScore> {
        enumerate_player_commands(settings, state).iter()
            .map(|&c| CommandScore::new(c))
            .collect()
    }
}

fn enumerate_player_commands(settings: &GameSettings, state: &GameState) -> Vec<Command> {
    let all_positions = state.unoccupied_player_cells(settings);
    let all_buildings = state.player_affordable_buildings(settings);
    
    let build_commands = all_positions.iter()
        .flat_map(|&pos| all_buildings.iter()
                  .map(|&building| Command::Build(pos, building)).collect::<Vec<_>>()
        );
    let other_commands = vec!(Command::Nothing);

    build_commands.chain(other_commands)
        .collect()
}

fn enumerate_opponent_commands(settings: &GameSettings, state: &GameState) -> Vec<Command> {
    let all_positions = state.unoccupied_opponent_cells(settings);
    let all_buildings = state.opponent_affordable_buildings(settings);
    
    let build_commands = all_positions.iter()
        .flat_map(|&pos| all_buildings.iter()
                  .map(|&building| Command::Build(pos, building)).collect::<Vec<_>>()
        );
    let other_commands = vec!(Command::Nothing);

    build_commands.chain(other_commands)
        .collect()
}