Removed dynamic settings
authorJustin Worthe <justin@worthe-it.co.za>
Thu, 9 Aug 2018 18:40:03 +0000 (20:40 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Thu, 9 Aug 2018 18:40:03 +0000 (20:40 +0200)
It worked really well for round 2 to set constants

src/bin/depth-first-search.rs [deleted file]
src/bin/perf-test.rs
src/engine/bitwise_engine.rs
src/engine/mod.rs
src/engine/settings.rs [deleted file]
src/engine/status.rs [new file with mode: 0644]
src/input/json.rs
src/main.rs
src/strategy/monte_carlo.rs
tests/live_comparison.rs
tests/monte_carlo_test.rs

diff --git a/src/bin/depth-first-search.rs b/src/bin/depth-first-search.rs
deleted file mode 100644 (file)
index c04061d..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-extern crate zombot;
-extern crate time;
-use time::PreciseTime;
-
-use zombot::*;
-use zombot::engine::*;
-use zombot::engine::settings::*;
-use zombot::engine::command::*;
-
-const STATE_PATH: &str = "tests/state0.json";
-
-use std::process;
-
-fn main() {
-    println!("Performing an exhaustive depth-first walk of the game states");
-    let start_time = PreciseTime::now();
-    let (settings, state) = match input::json::read_bitwise_state_from_file(STATE_PATH) {
-        Ok(ok) => ok,
-        Err(error) => {
-            println!("Error while parsing JSON file: {}", error);
-            process::exit(1);
-        }
-    };
-
-    walk_states(&settings, &state, 0);
-
-    println!("Total running time: {}", start_time.to(PreciseTime::now()));
-}
-
-fn walk_states<GS: GameState>(settings: &GameSettings, state: &GS, depth: u32) {
-    if depth >= 200 {
-        return;
-    }
-
-    let player_buildings = valid_buildings(settings, &state.player(), state.player_has_max_teslas());
-    let opponent_buildings = valid_buildings(settings, &state.opponent(), state.opponent_has_max_teslas());
-
-    for &player_building in &player_buildings {
-        for player_i in 0..state.unoccupied_player_cell_count() {
-            for &opponent_building in &opponent_buildings {
-                for opponent_i in 0..state.unoccupied_opponent_cell_count() {
-                    let player_point = state.location_of_unoccupied_player_cell(player_i);
-                    let player_move = Command::Build(player_point, player_building);
-                    let opponent_point = state.location_of_unoccupied_player_cell(opponent_i);
-                    let opponent_move = Command::Build(opponent_point, opponent_building);
-
-                    let mut after_move = state.clone();
-                    let status = after_move.simulate(settings, player_move, opponent_move);
-                    if status == GameStatus::Continue {
-                        walk_states(settings, &after_move, depth+1);
-                    }
-                }
-            }
-        }
-    }
-    for player_building in player_buildings {
-        for player_i in 0..state.unoccupied_player_cell_count() {
-            let player_point = state.location_of_unoccupied_player_cell(player_i);
-            let player_move = Command::Build(player_point, player_building);
-            let opponent_move = Command::Nothing;
-
-            let mut after_move = state.clone();
-            let status = after_move.simulate(settings, player_move, opponent_move);
-            if status == GameStatus::Continue {
-                walk_states(settings, &after_move, depth+1);
-            }
-        }
-    }
-    for opponent_building in opponent_buildings {
-        for opponent_i in 0..state.unoccupied_opponent_cell_count() {
-            let player_move = Command::Nothing;
-            let opponent_point = state.location_of_unoccupied_player_cell(opponent_i);
-            let opponent_move = Command::Build(opponent_point, opponent_building);
-
-            let mut after_move = state.clone();
-            let status = after_move.simulate(settings, player_move, opponent_move);
-            if status == GameStatus::Continue {
-                walk_states(settings, &after_move, depth+1);
-            }
-        }
-    }
-    let player_move = Command::Nothing;
-    let opponent_move = Command::Nothing;
-    let mut after_move = state.clone();
-    let status = after_move.simulate(settings, player_move, opponent_move);
-    if status == GameStatus::Continue {
-        walk_states(settings, &after_move, depth+1);
-    }
-    if depth < 10 {
-        print!(".");
-    }
-}
-
-fn valid_buildings(settings: &GameSettings, player: &Player, has_max_teslas: bool) -> Vec<BuildingType> {
-    let mut result = Vec::with_capacity(4);
-    for b in BuildingType::all().iter() {
-        let building_setting = settings.building_settings(*b);
-        let affordable = building_setting.price <= player.energy;
-        let is_tesla = *b == BuildingType::Tesla;
-        if affordable && (!is_tesla || !has_max_teslas) {
-            result.push(*b);
-        }
-    }
-    result
-}
index 8c93f5a..415cd61 100644 (file)
@@ -16,7 +16,7 @@ fn main() {
 fn bitwise() {
     println!("Running bitwise engine");
     let start_time = PreciseTime::now();
-    let (settings, state) = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+    let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
         Ok(ok) => ok,
         Err(error) => {
             println!("Error while parsing JSON file: {}", error);
@@ -24,5 +24,5 @@ fn bitwise() {
         }
     };
     let max_time = Duration::milliseconds(MAX_TIME_MILLIS);
-    strategy::monte_carlo::choose_move(&settings, &state, &start_time, max_time);
+    strategy::monte_carlo::choose_move(&state, &start_time, max_time);
 }
index 8a4ea91..6b9ccab 100644 (file)
@@ -1,13 +1,19 @@
 use engine::command::{Command, BuildingType};
 use engine::geometry::Point;
-use engine::settings::{GameSettings};
 use engine::constants::*;
-use engine::{GameStatus, Player, GameState};
+use engine::status::GameStatus;
 
 const LEFT_COL_MASK: u64 = 0x0101010101010101;
 const RIGHT_COL_MASK: u64 = 0x8080808080808080;
 
 #[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Player {
+    pub energy: u16,
+    pub health: u8,
+    pub energy_generated: u16,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct BitwiseGameState {
     pub status: GameStatus,
     pub player: Player,
@@ -48,8 +54,8 @@ pub struct TeslaCooldown {
 }
 
 
-impl GameState for BitwiseGameState {
-    fn simulate(&mut self, _settings: &GameSettings, player_command: Command, opponent_command: Command) -> GameStatus {
+impl BitwiseGameState {
+    pub fn simulate(&mut self, player_command: Command, opponent_command: Command) -> GameStatus {
         BitwiseGameState::perform_command(&mut self.player, &mut self.player_buildings, player_command);
         BitwiseGameState::perform_command(&mut self.opponent, &mut self.opponent_buildings, opponent_command);
 
@@ -71,20 +77,20 @@ impl GameState for BitwiseGameState {
         self.status
     }
 
-    fn player(&self) -> &Player { &self.player }
-    fn opponent(&self) -> &Player { &self.opponent }
-    fn player_has_max_teslas(&self) -> bool { self.player_buildings.count_teslas() >= TESLA_MAX }
-    fn opponent_has_max_teslas(&self) -> bool { self.opponent_buildings.count_teslas() >= TESLA_MAX }
+    pub fn player(&self) -> &Player { &self.player }
+    pub fn opponent(&self) -> &Player { &self.opponent }
+    pub fn player_has_max_teslas(&self) -> bool { self.player_buildings.count_teslas() >= TESLA_MAX }
+    pub fn opponent_has_max_teslas(&self) -> bool { self.opponent_buildings.count_teslas() >= TESLA_MAX }
 
-    fn unoccupied_player_cell_count(&self) -> usize { self.player_buildings.occupied.count_zeros() as usize }
-    fn unoccupied_opponent_cell_count(&self) -> usize { self.opponent_buildings.occupied.count_zeros() as usize }
-    fn location_of_unoccupied_player_cell(&self, i: usize) -> Point  {
+    pub fn unoccupied_player_cell_count(&self) -> usize { self.player_buildings.occupied.count_zeros() as usize }
+    pub fn unoccupied_opponent_cell_count(&self) -> usize { self.opponent_buildings.occupied.count_zeros() as usize }
+    pub fn location_of_unoccupied_player_cell(&self, i: usize) -> Point  {
         let bit = find_bit_index_from_rank(self.player_buildings.occupied, i as u64);
         let point = Point::new(bit%SINGLE_MAP_WIDTH, bit/SINGLE_MAP_WIDTH);
         debug_assert!(point.to_either_bitfield() & self.player_buildings.occupied == 0);
         point
     }
-    fn location_of_unoccupied_opponent_cell(&self, i: usize) -> Point {
+    pub fn location_of_unoccupied_opponent_cell(&self, i: usize) -> Point {
         let bit = find_bit_index_from_rank(self.opponent_buildings.occupied, i as u64);
         let point = Point::new(FULL_MAP_WIDTH - bit%SINGLE_MAP_WIDTH - 1, bit/SINGLE_MAP_WIDTH);
         debug_assert!(point.to_either_bitfield() & self.opponent_buildings.occupied == 0);
index c205d72..f98ef6b 100644 (file)
@@ -1,38 +1,5 @@
 pub mod command;
 pub mod geometry;
-pub mod settings;
 pub mod bitwise_engine;
 pub mod constants;
-
-use self::command::{Command};
-use self::geometry::Point;
-use self::settings::{GameSettings};
-
-pub trait GameState: Clone + Sync {
-    fn simulate(&mut self, settings: &GameSettings, player_command: Command, opponent_command: Command) -> GameStatus;
-    
-    fn player(&self) -> &Player;
-    fn opponent(&self) -> &Player;
-    fn player_has_max_teslas(&self) -> bool;
-    fn opponent_has_max_teslas(&self) -> bool;
-
-    fn unoccupied_player_cell_count(&self) -> usize;
-    fn unoccupied_opponent_cell_count(&self) -> usize;
-    fn location_of_unoccupied_player_cell(&self, i: usize) -> Point;
-    fn location_of_unoccupied_opponent_cell(&self, i: usize) -> Point;
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum GameStatus {
-    Continue,
-    PlayerWon,
-    OpponentWon,
-    Draw
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Player {
-    pub energy: u16,
-    pub health: u8,
-    pub energy_generated: u16,
-}
+pub mod status;
diff --git a/src/engine/settings.rs b/src/engine/settings.rs
deleted file mode 100644 (file)
index 18bdde0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-use super::geometry::Point;
-use super::command::BuildingType;
-use std::cmp;
-
-#[derive(Debug)]
-pub struct GameSettings {
-    pub size: Point,
-    pub energy_income: u16,
-    pub max_building_price: u16,
-    pub energy: BuildingSettings,
-    pub defence: BuildingSettings,
-    pub attack: BuildingSettings,
-    pub tesla: BuildingSettings,
-}
-
-#[derive(Debug)]
-pub struct BuildingSettings {
-    pub price: u16,
-    pub health: u8,
-    pub construction_time: u8,
-    pub weapon_damage: u8,
-    pub weapon_speed: u8,
-    pub weapon_cooldown_period: u8,
-    pub energy_generated_per_turn: u16
-}
-
-impl GameSettings {
-    pub fn new(size: Point, energy_income: u16, energy: BuildingSettings, defence: BuildingSettings, attack: BuildingSettings, tesla: BuildingSettings) -> GameSettings {
-        let max_building_price = cmp::max(cmp::max(cmp::max(energy.price, defence.price), attack.price), tesla.price);
-        GameSettings {
-            size, energy_income, max_building_price,
-            energy, defence, attack, tesla
-        }
-    }
-    pub fn building_settings(&self, building: BuildingType) -> &BuildingSettings {
-        match building {
-            BuildingType::Defence => &self.defence,
-            BuildingType::Attack => &self.attack,
-            BuildingType::Energy => &self.energy,
-            BuildingType::Tesla => &self.tesla,
-        }
-    }
-    
-}
diff --git a/src/engine/status.rs b/src/engine/status.rs
new file mode 100644 (file)
index 0000000..1fa7ac0
--- /dev/null
@@ -0,0 +1,7 @@
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum GameStatus {
+    Continue,
+    PlayerWon,
+    OpponentWon,
+    Draw
+}
index 200252a..9bc0518 100644 (file)
@@ -8,67 +8,29 @@ use engine::command;
 use engine::bitwise_engine;
 use engine::constants::*;
 
-pub fn read_bitwise_state_from_file(filename: &str) -> Result<(engine::settings::GameSettings, bitwise_engine::BitwiseGameState), Box<Error>> {
+pub fn read_bitwise_state_from_file(filename: &str) -> Result<bitwise_engine::BitwiseGameState, Box<Error>> {
     let mut file = File::open(filename)?;
     let mut content = String::new();
     file.read_to_string(&mut content)?;
     let state: State = serde_json::from_str(content.as_ref())?;
 
-    let engine_settings = state.to_engine_settings();
     let engine_state = state.to_bitwise_engine();
-    Ok((engine_settings, engine_state))
+    Ok(engine_state)
 }
 
 #[derive(Deserialize)]
 #[serde(rename_all = "camelCase")]
 struct State {
-    game_details: GameDetails,
     players: Vec<Player>,
     game_map: Vec<Vec<GameCell>>,
 }
 
 #[derive(Deserialize)]
 #[serde(rename_all = "camelCase")]
-struct GameDetails {
-    //round: u16,
-    //max_rounds: u16,
-    map_width: u8,
-    map_height: u8,
-    round_income_energy: u16,
-    buildings_stats: BuildingStats
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
-struct BuildingStats {
-    energy: BuildingBlueprint,
-    defense: BuildingBlueprint,
-    attack: BuildingBlueprint,
-    tesla: BuildingBlueprint,
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-struct BuildingBlueprint {
-    price: u16,
-    health: u8,
-    construction_time: u8,
-    weapon_damage: u8,
-    weapon_speed: u8,
-    weapon_cooldown_period: u8,
-    energy_generated_per_turn: u16,
-//    destroy_multiplier: u16,
-//    construction_score: u16
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
 struct Player {
     player_type: char,
     energy: u16,
     health: u8,
-    //hits_taken: u32,
-    //score: u32
 }
 
 #[derive(Deserialize)]
@@ -78,7 +40,6 @@ struct GameCell {
     y: u8,
     buildings: Vec<BuildingState>,
     missiles: Vec<MissileState>,
-    //cell_owner: char
 }
 
 #[derive(Deserialize)]
@@ -86,13 +47,7 @@ struct GameCell {
 struct BuildingState {
     health: u8,
     construction_time_left: i16,
-    //price: u16,
-    //weapon_damage: u8,
-    //weapon_speed: u8,
     weapon_cooldown_time_left: u8,
-    //weapon_cooldown_period: u8,
-    //destroy_multiplier: u32,
-    //construction_score: u32,
     energy_generated_per_turn: u16,
     building_type: String,
     x: u8,
@@ -103,26 +58,11 @@ struct BuildingState {
 #[derive(Deserialize)]
 #[serde(rename_all = "camelCase")]
 struct MissileState {
-    //damage: u8,
-    //speed: u8,
-    //x: u8,
-    //y: u8,
     player_type: char
 }
 
 
 impl State {
-    fn to_engine_settings(&self) -> engine::settings::GameSettings {
-        engine::settings::GameSettings::new(
-            engine::geometry::Point::new(self.game_details.map_width, self.game_details.map_height),
-            self.game_details.round_income_energy,
-            self.game_details.buildings_stats.energy.to_engine(),
-            self.game_details.buildings_stats.defense.to_engine(),
-            self.game_details.buildings_stats.attack.to_engine(),
-            self.game_details.buildings_stats.tesla.to_engine(),
-        )
-    }
-    
     fn to_bitwise_engine(&self) -> bitwise_engine::BitwiseGameState {
         let mut player = self.player().to_bitwise_engine();
         let mut opponent = self.opponent().to_bitwise_engine();
@@ -211,23 +151,9 @@ impl State {
     }
 }
 
-impl BuildingBlueprint {
-    fn to_engine(&self) -> engine::settings::BuildingSettings {
-        engine::settings::BuildingSettings {
-            price: self.price,
-            health: self.health,
-            construction_time: self.construction_time-1,
-            weapon_damage: self.weapon_damage,
-            weapon_speed: self.weapon_speed,
-            weapon_cooldown_period: self.weapon_cooldown_period,
-            energy_generated_per_turn: self.energy_generated_per_turn,
-        }
-    }
-}
-
 impl Player {
-    fn to_bitwise_engine(&self) -> engine::Player {
-        engine::Player {
+    fn to_bitwise_engine(&self) -> engine::bitwise_engine::Player {
+        engine::bitwise_engine::Player {
             energy: self.energy,
             health: self.health,
             energy_generated: ENERGY_GENERATED_BASE
index 0235d5f..cdafaba 100644 (file)
@@ -16,12 +16,6 @@ use std::fs::File;
 use std::io::prelude::*;
 use std::process;
 
-fn choose_move<GS:engine::GameState>(settings: &engine::settings::GameSettings, state: &GS, start_time: &PreciseTime) -> Command {
-    let max_time = Duration::milliseconds(MAX_TIME_MILLIS);
-    strategy::monte_carlo::choose_move(settings, state, start_time, max_time)
-}
-
-
 fn write_command(filename: &str, command: Command) -> Result<(), Box<Error> > {
     let mut file = File::create(filename)?;
     write!(file, "{}", command)?;
@@ -31,15 +25,16 @@ fn write_command(filename: &str, command: Command) -> Result<(), Box<Error> > {
 
 fn main() {
     let start_time = PreciseTime::now();
+    let max_time = Duration::milliseconds(MAX_TIME_MILLIS);
     
-    let (settings, state) = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+    let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
         Ok(ok) => ok,
         Err(error) => {
             println!("Error while parsing JSON file: {}", error);
             process::exit(1);
         }
     };
-    let command = choose_move(&settings, &state, &start_time);
+    let command = strategy::monte_carlo::choose_move(&state, &start_time, max_time);
 
     match write_command(COMMAND_PATH, command) {
         Ok(()) => {}
index d4003bb..cf0d77c 100644 (file)
@@ -1,7 +1,8 @@
-use engine::settings::GameSettings;
 use engine::command::*;
 use engine::geometry::*;
-use engine::{GameState, GameStatus, Player};
+use engine::status::GameStatus;
+use engine::bitwise_engine::{Player, BitwiseGameState};
+use engine::constants::*;
 
 use rand::{Rng, XorShiftRng, SeedableRng};
 
@@ -15,9 +16,9 @@ use rayon::prelude::*;
 #[cfg(feature = "energy-cutoff")] pub const ENERGY_PRODUCTION_CUTOFF: u16 = 30;
 #[cfg(feature = "energy-cutoff")] pub const ENERGY_STORAGE_CUTOFF: u16 = 45;
 
-pub fn choose_move<GS: GameState>(settings: &GameSettings, state: &GS, start_time: &PreciseTime, max_time: Duration) -> Command {
-    let mut command_scores = CommandScore::init_command_scores(settings, state);
-    let command = simulate_options_to_timeout(&mut command_scores, settings, state, start_time, max_time);
+pub fn choose_move(state: &BitwiseGameState, start_time: &PreciseTime, max_time: Duration) -> Command {
+    let mut command_scores = CommandScore::init_command_scores(state);
+    let command = simulate_options_to_timeout(&mut command_scores, state, start_time, max_time);
     
     match command {
         Some(command) => command.command,
@@ -26,7 +27,7 @@ pub fn choose_move<GS: GameState>(settings: &GameSettings, state: &GS, start_tim
 }
 
 #[cfg(not(feature = "discard-poor-performers"))]
-fn simulate_options_to_timeout<'a, GS: GameState>(command_scores: &'a mut Vec<CommandScore>, settings: &GameSettings, state: &GS, start_time: &PreciseTime, max_time: Duration) -> Option<&'a CommandScore> {
+fn simulate_options_to_timeout(command_scores: &'a mut Vec<CommandScore>, settings: &GameSettings, state: &BitwiseGameState, start_time: &PreciseTime, max_time: Duration) -> Option<&'a CommandScore> {
     loop {
         simulate_all_options_once(command_scores, settings, state);
         if start_time.to(PreciseTime::now()) > max_time {
@@ -44,7 +45,7 @@ fn simulate_options_to_timeout<'a, GS: GameState>(command_scores: &'a mut Vec<Co
 }
 
 #[cfg(feature = "discard-poor-performers")]
-fn simulate_options_to_timeout<'a, GS: GameState>(command_scores: &'a mut Vec<CommandScore>, settings: &GameSettings, state: &GS, start_time: &PreciseTime, max_time: Duration) -> Option<&'a CommandScore> {
+fn simulate_options_to_timeout<'a>(command_scores: &'a mut Vec<CommandScore>, state: &BitwiseGameState, start_time: &PreciseTime, max_time: Duration) -> Option<&'a CommandScore> {
     use std::cmp;
     let min_options = cmp::min(command_scores.len(), 5);
     
@@ -53,7 +54,7 @@ fn simulate_options_to_timeout<'a, GS: GameState>(command_scores: &'a mut Vec<Co
         let new_length = cmp::max(min_options, command_scores.len() / (2usize.pow(i as u32)));
         let active_scores = &mut command_scores[0..new_length];
         loop {
-            simulate_all_options_once(active_scores, settings, state);
+            simulate_all_options_once(active_scores, state);
             if start_time.to(PreciseTime::now()) > max {
                 break;
             }
@@ -71,37 +72,37 @@ fn simulate_options_to_timeout<'a, GS: GameState>(command_scores: &'a mut Vec<Co
 }
 
 #[cfg(feature = "single-threaded")]
-fn simulate_all_options_once<GS: GameState>(command_scores: &mut[CommandScore], settings: &GameSettings, state: &GS) {
+fn simulate_all_options_once(command_scores: &mut[CommandScore], state: &BitwiseGameState) {
     command_scores.iter_mut()
         .for_each(|score| {
             let mut rng = XorShiftRng::from_seed(score.next_seed);
-            simulate_to_endstate(score, settings, state, &mut rng);
+            simulate_to_endstate(score, state, &mut rng);
         });
 }
 
 #[cfg(not(feature = "single-threaded"))]
-fn simulate_all_options_once<GS: GameState>(command_scores: &mut[CommandScore], settings: &GameSettings, state: &GS) {
+fn simulate_all_options_once(command_scores: &mut[CommandScore], state: &BitwiseGameState) {
     command_scores.par_iter_mut()
         .for_each(|score| {
             let mut rng = XorShiftRng::from_seed(score.next_seed);
-            simulate_to_endstate(score, settings, state, &mut rng);
+            simulate_to_endstate(score, state, &mut rng);
         });
 }
 
-fn simulate_to_endstate<R: Rng, GS: GameState>(command_score: &mut CommandScore, settings: &GameSettings, state: &GS, rng: &mut R) {
+fn simulate_to_endstate<R: Rng>(command_score: &mut CommandScore, state: &BitwiseGameState, rng: &mut R) {
     let mut state_mut = state.clone();
     
-    let opponent_first = random_opponent_move(settings, &state_mut, rng);
-    let mut status = state_mut.simulate(settings, command_score.command, opponent_first);
+    let opponent_first = random_opponent_move(&state_mut, rng);
+    let mut status = state_mut.simulate(command_score.command, opponent_first);
     
     for _ in 0..MAX_MOVES {
         if status != GameStatus::Continue {
             break;
         }
 
-        let player_command = random_player_move(settings, &state_mut, rng);
-        let opponent_command = random_opponent_move(settings, &state_mut, rng);
-        status = state_mut.simulate(settings, player_command, opponent_command);
+        let player_command = random_player_move(&state_mut, rng);
+        let opponent_command = random_opponent_move(&state_mut, rng);
+        status = state_mut.simulate(player_command, opponent_command);
     }
 
     let next_seed = [rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32()];
@@ -113,13 +114,13 @@ fn simulate_to_endstate<R: Rng, GS: GameState>(command_score: &mut CommandScore,
     }
 }
 
-fn random_player_move<R: Rng, GS: GameState>(settings: &GameSettings, state: &GS, rng: &mut R) -> Command {
-    let all_buildings = sensible_buildings(settings, &state.player(), state.player_has_max_teslas());
+fn random_player_move<R: Rng>(state: &BitwiseGameState, rng: &mut R) -> Command {
+    let all_buildings = sensible_buildings(&state.player(), state.player_has_max_teslas());
     random_move(&all_buildings, rng, state.unoccupied_player_cell_count(), |i| state.location_of_unoccupied_player_cell(i))
 }
 
-fn random_opponent_move<R: Rng, GS: GameState>(settings: &GameSettings, state: &GS, rng: &mut R) -> Command {
-    let all_buildings = sensible_buildings(settings, &state.opponent(), state.opponent_has_max_teslas());
+fn random_opponent_move<R: Rng>(state: &BitwiseGameState, rng: &mut R) -> Command {
+    let all_buildings = sensible_buildings(&state.opponent(), state.opponent_has_max_teslas());
     random_move(&all_buildings, rng, state.unoccupied_opponent_cell_count(), |i| state.location_of_unoccupied_opponent_cell(i))
 }
 
@@ -197,8 +198,8 @@ impl CommandScore {
     }
 
     //TODO: Devalue nothing so that it doesn't stand and do nothing when it can do things
-    fn init_command_scores<GS: GameState>(settings: &GameSettings, state: &GS) -> Vec<CommandScore> {
-        let all_buildings = sensible_buildings(settings, &state.player(), state.player_has_max_teslas());
+    fn init_command_scores(state: &BitwiseGameState) -> Vec<CommandScore> {
+        let all_buildings = sensible_buildings(&state.player(), state.player_has_max_teslas());
 
         let unoccupied_cells = (0..state.unoccupied_player_cell_count()).map(|i| state.location_of_unoccupied_player_cell(i));
 
@@ -219,36 +220,47 @@ impl CommandScore {
 }
 
 #[cfg(not(feature = "energy-cutoff"))]
-fn sensible_buildings(settings: &GameSettings, player: &Player, has_max_teslas: bool) -> Vec<BuildingType> {
+fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec<BuildingType> {
     let mut result = Vec::with_capacity(4);
-    for b in BuildingType::all().iter() {
-        let building_setting = settings.building_settings(*b);
-        let affordable = building_setting.price <= player.energy;
-        let is_tesla = *b == BuildingType::Tesla;
-        if affordable && (!is_tesla || !has_max_teslas) {
-            result.push(*b);
-        }
+
+    if DEFENCE_PRICE <= player.energy {
+        result.push(BuildingType::Defence);
+    }
+    if MISSILE_PRICE <= player.energy {
+        result.push(BuildingType::Attack);
+    }
+    if ENERGY_PRICE <= player.energy {
+        result.push(BuildingType::Energy);
     }
+    if TESLA_PRICE <= player.energy && !has_max_teslas {
+        result.push(BuildingType::Tesla);
+    }
+
     result
 }
 
 
 //TODO: Heuristic that avoids building the initial energy towers all in the same row?
+//TODO: Update cutoff to maybe build iron curtains
 #[cfg(feature = "energy-cutoff")]
-fn sensible_buildings(settings: &GameSettings, player: &Player, has_max_teslas: bool) -> Vec<BuildingType> {
+fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec<BuildingType> {
     let mut result = Vec::with_capacity(4);
     let needs_energy = player.energy_generated <= ENERGY_PRODUCTION_CUTOFF ||
         player.energy <= ENERGY_STORAGE_CUTOFF;
-    
-    for b in BuildingType::all().iter() {
-        let building_setting = settings.building_settings(*b);
-        let affordable = building_setting.price <= player.energy;
-        let energy_producing = building_setting.energy_generated_per_turn > 0;
-        let is_tesla = *b == BuildingType::Tesla;
-        if affordable && (!energy_producing || needs_energy) && (!is_tesla || !has_max_teslas) {
-            result.push(*b);
-        }
+
+    if DEFENCE_PRICE <= player.energy {
+        result.push(BuildingType::Defence);
+    }
+    if MISSILE_PRICE <= player.energy {
+        result.push(BuildingType::Attack);
     }
+    if ENERGY_PRICE <= player.energy && needs_energy {
+        result.push(BuildingType::Energy);
+    }
+    if TESLA_PRICE <= player.energy && !has_max_teslas {
+        result.push(BuildingType::Tesla);
+    }
+    
     result
 }
 
index 23beaec..5761454 100644 (file)
@@ -3,8 +3,7 @@ extern crate zombot;
 use zombot::input::json;
 use zombot::engine::command::{Command, BuildingType};
 use zombot::engine::geometry::Point;
-use zombot::engine::settings::GameSettings;
-use zombot::engine::GameState;
+use zombot::engine::constants::*;
 
 use std::fs::File;
 use std::io::prelude::*;
@@ -20,14 +19,14 @@ fn it_successfully_simulates_replay_with_teslas() {
 }
 
 fn test_from_replay(replay_folder: &str, length: usize) {
-    let (settings, mut state) = json::read_bitwise_state_from_file(&format!("{}/Round 000/state.json", replay_folder)).unwrap();
+    let  mut state = json::read_bitwise_state_from_file(&format!("{}/Round 000/state.json", replay_folder)).unwrap();
     
     for i in 0..length {
         let player = read_player_command(&format!("{}/Round {:03}/PlayerCommand.txt", replay_folder, i));
-        let opponent = read_opponent_command(&format!("{}/Round {:03}/OpponentCommand.txt", replay_folder, i), &settings);
-        let (_, mut expected_state) = json::read_bitwise_state_from_file(&format!("{}/Round {:03}/state.json", replay_folder, i+1)).unwrap();
+        let opponent = read_opponent_command(&format!("{}/Round {:03}/OpponentCommand.txt", replay_folder, i));
+        let mut expected_state = json::read_bitwise_state_from_file(&format!("{}/Round {:03}/state.json", replay_folder, i+1)).unwrap();
         
-        state.simulate(&settings, player, opponent);
+        state.simulate(player, opponent);
         state.sort();
         expected_state.sort();
 
@@ -56,15 +55,15 @@ fn read_player_command(filename: &str) -> Command {
     }
 }
 
-fn read_opponent_command(filename: &str, settings: &GameSettings) -> Command {
+fn read_opponent_command(filename: &str) -> Command {
     match read_player_command(filename) {
         Command::Nothing => Command::Nothing,
         Command::Build(p, b) => Command::Build(Point::new(
-            settings.size.x - p.x - 1,
+            FULL_MAP_WIDTH - p.x - 1,
             p.y
         ), b),
         Command::Deconstruct(p) => Command::Deconstruct(Point::new(
-            settings.size.x - p.x - 1,
+            FULL_MAP_WIDTH - p.x - 1,
             p.y
         )),
     }
index 832cdb3..71e0b07 100644 (file)
@@ -10,12 +10,12 @@ const STATE_PATH: &str = "tests/state0.json";
 #[test]
 fn it_does_a_normal_turn_successfully() {
     let start_time = PreciseTime::now();
-    let (settings, state) = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+    let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
         Ok(ok) => ok,
         Err(error) => panic!("Error while parsing JSON file: {}", error)
     };
     let max_time = Duration::milliseconds(200);
-    strategy::monte_carlo::choose_move(&settings, &state, &start_time, max_time);
+    strategy::monte_carlo::choose_move(&state, &start_time, max_time);
 
     assert!(start_time.to(PreciseTime::now()) < max_time + Duration::milliseconds(50))
 }