From ea78e266cff3f57c39442aefc21295a758419e69 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Thu, 9 Aug 2018 20:40:03 +0200 Subject: Removed dynamic settings It worked really well for round 2 to set constants --- src/bin/depth-first-search.rs | 105 ------------------------------------------ src/bin/perf-test.rs | 4 +- src/engine/bitwise_engine.rs | 30 +++++++----- src/engine/mod.rs | 35 +------------- src/engine/settings.rs | 44 ------------------ src/engine/status.rs | 7 +++ src/input/json.rs | 82 ++------------------------------- src/main.rs | 11 ++--- src/strategy/monte_carlo.rs | 96 +++++++++++++++++++++----------------- tests/live_comparison.rs | 17 ++++--- tests/monte_carlo_test.rs | 4 +- 11 files changed, 99 insertions(+), 336 deletions(-) delete mode 100644 src/bin/depth-first-search.rs delete mode 100644 src/engine/settings.rs create mode 100644 src/engine/status.rs diff --git a/src/bin/depth-first-search.rs b/src/bin/depth-first-search.rs deleted file mode 100644 index c04061d..0000000 --- a/src/bin/depth-first-search.rs +++ /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(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 { - 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 -} diff --git a/src/bin/perf-test.rs b/src/bin/perf-test.rs index 8c93f5a..415cd61 100644 --- a/src/bin/perf-test.rs +++ b/src/bin/perf-test.rs @@ -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); } diff --git a/src/engine/bitwise_engine.rs b/src/engine/bitwise_engine.rs index 8a4ea91..6b9ccab 100644 --- a/src/engine/bitwise_engine.rs +++ b/src/engine/bitwise_engine.rs @@ -1,12 +1,18 @@ 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, @@ -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); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index c205d72..f98ef6b 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -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 index 18bdde0..0000000 --- a/src/engine/settings.rs +++ /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 index 0000000..1fa7ac0 --- /dev/null +++ b/src/engine/status.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GameStatus { + Continue, + PlayerWon, + OpponentWon, + Draw +} diff --git a/src/input/json.rs b/src/input/json.rs index 200252a..9bc0518 100644 --- a/src/input/json.rs +++ b/src/input/json.rs @@ -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> { +pub fn read_bitwise_state_from_file(filename: &str) -> Result> { 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, game_map: Vec>, } -#[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, missiles: Vec, - //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 diff --git a/src/main.rs b/src/main.rs index 0235d5f..cdafaba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,12 +16,6 @@ use std::fs::File; use std::io::prelude::*; use std::process; -fn choose_move(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 > { let mut file = File::create(filename)?; write!(file, "{}", command)?; @@ -31,15 +25,16 @@ fn write_command(filename: &str, command: Command) -> Result<(), Box > { 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(()) => {} diff --git a/src/strategy/monte_carlo.rs b/src/strategy/monte_carlo.rs index d4003bb..cf0d77c 100644 --- a/src/strategy/monte_carlo.rs +++ b/src/strategy/monte_carlo.rs @@ -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(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(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, settings: &GameSettings, state: &GS, start_time: &PreciseTime, max_time: Duration) -> Option<&'a CommandScore> { +fn simulate_options_to_timeout(command_scores: &'a mut Vec, 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(command_scores: &'a mut Vec, settings: &GameSettings, state: &GS, start_time: &PreciseTime, max_time: Duration) -> Option<&'a CommandScore> { +fn simulate_options_to_timeout<'a>(command_scores: &'a mut Vec, 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 max { break; } @@ -71,37 +72,37 @@ fn simulate_options_to_timeout<'a, GS: GameState>(command_scores: &'a mut Vec(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(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(command_score: &mut CommandScore, settings: &GameSettings, state: &GS, rng: &mut R) { +fn simulate_to_endstate(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(command_score: &mut CommandScore, } } -fn random_player_move(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(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(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(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(settings: &GameSettings, state: &GS) -> Vec { - let all_buildings = sensible_buildings(settings, &state.player(), state.player_has_max_teslas()); + fn init_command_scores(state: &BitwiseGameState) -> Vec { + 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 { +fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec { 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 { +fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec { 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 } diff --git a/tests/live_comparison.rs b/tests/live_comparison.rs index 23beaec..5761454 100644 --- a/tests/live_comparison.rs +++ b/tests/live_comparison.rs @@ -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 )), } diff --git a/tests/monte_carlo_test.rs b/tests/monte_carlo_test.rs index 832cdb3..71e0b07 100644 --- a/tests/monte_carlo_test.rs +++ b/tests/monte_carlo_test.rs @@ -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)) } -- cgit v1.2.3