summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2018-08-09 20:40:03 +0200
committerJustin Worthe <justin@worthe-it.co.za>2018-08-09 20:40:03 +0200
commitea78e266cff3f57c39442aefc21295a758419e69 (patch)
tree8f5f707affbd546efb43b9a638d2f9ff6bd52116
parentf41255a8dda9e2c6a18c32564a30e63eed58f6b3 (diff)
Removed dynamic settings
It worked really well for round 2 to set constants
-rw-r--r--src/bin/depth-first-search.rs105
-rw-r--r--src/bin/perf-test.rs4
-rw-r--r--src/engine/bitwise_engine.rs30
-rw-r--r--src/engine/mod.rs35
-rw-r--r--src/engine/settings.rs44
-rw-r--r--src/engine/status.rs7
-rw-r--r--src/input/json.rs82
-rw-r--r--src/main.rs11
-rw-r--r--src/strategy/monte_carlo.rs96
-rw-r--r--tests/live_comparison.rs17
-rw-r--r--tests/monte_carlo_test.rs4
11 files changed, 99 insertions, 336 deletions
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<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
-}
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,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);
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<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
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<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(()) => {}
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<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
}
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))
}