86960eb30c113fc0b9bdd36d02afa5b16d7ba9e8
[entelect-challenge-tower-defence.git] / src / strategy / monte_carlo.rs
1 use engine::settings::GameSettings;
2 use engine::command::*;
3 use engine::{GameState, GameStatus};
4
5 use rand::{thread_rng, Rng};
6 use std::process;
7 const MAX_MOVES: u16 = 400;
8
9 use time::{Duration, PreciseTime};
10
11 // TODO Round start time here
12 pub fn choose_move(settings: &GameSettings, state: &GameState, start_time: &PreciseTime) -> Command {
13     println!("Using MONTE_CARLO strategy");
14
15     //just under the max of 2 seconds, to avoid issues like overhead in the bot being run, and we still need to write the result of this to file
16     let max_time = Duration::milliseconds(1950);
17     
18     let mut rng = thread_rng();
19     let mut command_scores = CommandScore::init_command_scores(settings, state);
20
21     loop {
22         for mut score in &mut command_scores {
23             simulate_to_endstate(score, settings, state, &mut rng);
24         }
25         if start_time.to(PreciseTime::now()) > max_time {
26             break;
27         }
28     }
29
30     println!("{:#?}", command_scores);
31     let command = command_scores.iter().max_by_key(|&c| c.win_ratio());
32     
33     match command {
34         Some(ref command) => command.command,
35         _ => Command::Nothing
36     }
37 }
38
39 fn simulate_to_endstate<R: Rng>(command_score: &mut CommandScore, settings: &GameSettings, state: &GameState, rng: &mut R) {
40     let opponent_first = random_opponent_move(settings, state, rng);
41     let mut state_mut = state.simulate(settings, command_score.command, opponent_first);
42     
43     for _ in 0..MAX_MOVES {
44         if state_mut.status != GameStatus::Continue {
45             break;
46         }
47
48         let player_command = random_player_move(settings, &state_mut, rng);
49         let opponent_command = random_opponent_move(settings, &state_mut, rng);
50         state_mut.simulate_mut(settings, player_command, opponent_command);
51     }
52
53     match state_mut.status {
54         GameStatus::PlayerWon => command_score.add_victory(),
55         GameStatus::OpponentWon => command_score.add_defeat(),
56         GameStatus::Continue => command_score.add_stalemate(),
57         GameStatus::Draw => command_score.add_draw(),
58         GameStatus::InvalidMove => {
59             println!("Invalid move made while performing simulation");
60             process::exit(0);
61         }
62     }
63 }
64
65 fn random_player_move<R: Rng>(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command {
66     let all_commands = enumerate_player_commands(settings, state);
67     rng.choose(&all_commands).cloned().unwrap_or(Command::Nothing)
68 }
69 fn random_opponent_move<R: Rng>(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command {
70     let all_commands = enumerate_opponent_commands(settings, state);
71     rng.choose(&all_commands).cloned().unwrap_or(Command::Nothing)
72 }
73
74 #[derive(Debug)]
75 struct CommandScore {
76     command: Command,
77     victories: u32,
78     defeats: u32,
79     draws: u32,
80     stalemates: u32,
81     attempts: u32
82 }
83
84 impl CommandScore {
85     fn new(command: Command) -> CommandScore {
86         CommandScore {
87             command: command,
88             victories: 0,
89             defeats: 0,
90             draws: 0,
91             stalemates: 0,
92             attempts: 0
93         }
94     }
95
96     fn add_victory(&mut self) {
97         self.victories += 1;
98         self.attempts += 1;
99     }
100
101     fn add_defeat(&mut self) {
102         self.defeats += 1;
103         self.attempts += 1;
104     }
105
106     fn add_draw(&mut self) {
107         self.draws += 1;
108         self.attempts += 1;
109     }
110
111     fn add_stalemate(&mut self) {
112         self.stalemates += 1;
113         self.attempts += 1;
114     }
115
116     fn win_ratio(&self) -> u32 {
117         self.victories * 1000 / self.attempts
118     }
119     
120     fn init_command_scores(settings: &GameSettings, state: &GameState) -> Vec<CommandScore> {
121         enumerate_player_commands(settings, state).iter()
122             .map(|&c| CommandScore::new(c))
123             .collect()
124     }
125 }
126
127 fn enumerate_player_commands(settings: &GameSettings, state: &GameState) -> Vec<Command> {
128     let all_positions = state.unoccupied_player_cells(settings);
129     let all_buildings = state.player_affordable_buildings(settings);
130     
131     let build_commands = all_positions.iter()
132         .flat_map(|&pos| all_buildings.iter()
133                   .map(|&building| Command::Build(pos, building)).collect::<Vec<_>>()
134         );
135     let other_commands = vec!(Command::Nothing);
136
137     build_commands.chain(other_commands)
138         .collect()
139 }
140
141 fn enumerate_opponent_commands(settings: &GameSettings, state: &GameState) -> Vec<Command> {
142     let all_positions = state.unoccupied_opponent_cells(settings);
143     let all_buildings = state.opponent_affordable_buildings(settings);
144     
145     let build_commands = all_positions.iter()
146         .flat_map(|&pos| all_buildings.iter()
147                   .map(|&building| Command::Build(pos, building)).collect::<Vec<_>>()
148         );
149     let other_commands = vec!(Command::Nothing);
150
151     build_commands.chain(other_commands)
152         .collect()
153 }