Added rule on maximum two tesla towers
authorJustin Worthe <justin@worthe-it.co.za>
Mon, 25 Jun 2018 19:17:11 +0000 (21:17 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Mon, 25 Jun 2018 19:17:11 +0000 (21:17 +0200)
src/engine/command.rs
src/engine/mod.rs
src/engine/settings.rs
src/strategy/monte_carlo.rs
tests/live-comparison.rs

index bcfc352..3ea0fbe 100644 (file)
@@ -19,7 +19,7 @@ impl fmt::Display for Command {
 }
 
 #[repr(u8)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum BuildingType {
     Defence = 0,
     Attack = 1,
@@ -35,7 +35,7 @@ impl BuildingType {
 
     pub fn from_u8(id: u8) -> Option<BuildingType> {
         use std::mem;
-        if id < 4 && id != 3 { Some(unsafe { mem::transmute(id) }) } else { None }
+        if id <= 4 && id != 3 { Some(unsafe { mem::transmute(id) }) } else { None }
     }
 
 }
index a04f875..7a39214 100644 (file)
@@ -156,6 +156,9 @@ impl GameState {
             debug_assert!(!buildings.iter().any(|b| b.pos == p));
             debug_assert!(p.x < size.x && p.y < size.y);
             debug_assert!(player.energy >= blueprint.price);
+            debug_assert!(b != BuildingType::Tesla ||
+                          unconstructed_buildings.iter().filter(|b| b.weapon_damage == 20).count() +
+                          buildings.iter().filter(|b| b.weapon_damage == 20).count() < 2);
 
             player.energy -= blueprint.price;
             unconstructed_buildings.push(UnconstructedBuilding::new(p, blueprint));
@@ -346,6 +349,16 @@ impl GameState {
             .chain(self.opponent_buildings.iter().map(|b| b.pos))
             .collect()
     }
+
+    pub fn count_player_teslas(&self) -> usize {
+        self.player_unconstructed_buildings.iter().filter(|b| b.weapon_damage == 20).count() +
+            self.player_buildings.iter().filter(|b| b.weapon_damage == 20).count()
+    }
+
+    pub fn count_opponent_teslas(&self) -> usize {
+        self.opponent_unconstructed_buildings.iter().filter(|b| b.weapon_damage == 20).count() +
+            self.opponent_buildings.iter().filter(|b| b.weapon_damage == 20).count()
+    }
 }
 
 impl GameStatus {
@@ -364,10 +377,13 @@ impl Player {
     }
 
     #[cfg(not(feature = "energy-cutoff"))]
-    pub fn sensible_buildings(&self, settings: &GameSettings) -> Vec<BuildingType> {
+    pub fn sensible_buildings(&self, tesla_allowed: bool, settings: &GameSettings) -> Vec<BuildingType> {
         let mut result = Vec::with_capacity(3);
         for b in BuildingType::all().iter() {
-            if settings.building_settings(*b).price <= self.energy {
+            let building_setting = settings.building_settings(*b);
+            let affordable = building_setting.price <= self.energy;
+            let is_tesla = building_setting.weapon_damage == 20;
+            if affordable && (!is_tesla || tesla_allowed) {
                 result.push(*b);
             }
         }
@@ -375,7 +391,7 @@ impl Player {
     }
 
     #[cfg(feature = "energy-cutoff")]
-    pub fn sensible_buildings(&self, settings: &GameSettings) -> Vec<BuildingType> {
+    pub fn sensible_buildings(&self, tesla_allowed: bool, settings: &GameSettings) -> Vec<BuildingType> {
         let mut result = Vec::with_capacity(3);
         let needs_energy = self.energy_generated as f32 <= ENERGY_PRODUCTION_CUTOFF * settings.max_building_price as f32 &&
             self.energy as f32 <= ENERGY_STORAGE_CUTOFF * settings.max_building_price as f32;
@@ -384,7 +400,8 @@ impl Player {
             let building_setting = settings.building_settings(*b);
             let affordable = building_setting.price <= self.energy;
             let energy_producing = building_setting.energy_generated_per_turn > 0;
-            if affordable && (!energy_producing || needs_energy) {
+            let is_tesla = building_setting.weapon_damage == 20;
+            if affordable && (!energy_producing || needs_energy) && (!is_tesla || tesla_allowed) {
                 result.push(*b);
             }
         }
index 3657cb2..18bdde0 100644 (file)
@@ -26,7 +26,7 @@ pub struct BuildingSettings {
 
 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(energy.price, defence.price), attack.price);
+        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
index 18b8acc..dbeac2f 100644 (file)
@@ -75,36 +75,31 @@ fn simulate_to_endstate<R: Rng>(command_score: &mut CommandScore, settings: &Gam
 }
 
 fn random_player_move<R: Rng>(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command {
-    let all_buildings = state.player.sensible_buildings(settings);
-    random_move(&state.unoccupied_player_cells, &state.occupied_player_cells(), &all_buildings, rng)
+    let all_buildings = state.player.sensible_buildings(state.count_player_teslas() < 2, settings);
+    random_move(&state.unoccupied_player_cells, &all_buildings, rng)
 }
 
 fn random_opponent_move<R: Rng>(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command {
-    let all_buildings = state.opponent.sensible_buildings(settings);
-    random_move(&state.unoccupied_opponent_cells, &state.occupied_opponent_cells(), &all_buildings, rng)
+    let all_buildings = state.opponent.sensible_buildings(state.count_opponent_teslas() < 2, settings);
+    random_move(&state.unoccupied_opponent_cells, &all_buildings, rng)
 }
 
-fn random_move<R: Rng>(free_positions: &[Point], occupied_positions: &[Point], all_buildings: &[BuildingType], rng: &mut R) -> Command {
+fn random_move<R: Rng>(free_positions: &[Point], all_buildings: &[BuildingType], rng: &mut R) -> Command {
     
     let building_command_count = free_positions.len()*all_buildings.len();
-    let deconstruct_count = occupied_positions.len();
     let nothing_count = 1;
 
-    let number_of_commands = building_command_count + deconstruct_count + nothing_count;
+    let number_of_commands = building_command_count + nothing_count;
     
     let choice_index = rng.gen_range(0, number_of_commands);
 
     if choice_index == number_of_commands - 1 {
         Command::Nothing
-    } else if choice_index < building_command_count {
+    } else {
         Command::Build(
             free_positions[choice_index/all_buildings.len()],
             all_buildings[choice_index%all_buildings.len()]
         )
-    } else {
-        Command::Deconstruct(
-            occupied_positions[choice_index-building_command_count]
-        )
     }
 }
 
@@ -161,7 +156,7 @@ impl CommandScore {
     }
     
     fn init_command_scores(settings: &GameSettings, state: &GameState) -> Vec<CommandScore> {
-        let all_buildings = state.player.sensible_buildings(settings);
+        let all_buildings = state.player.sensible_buildings(state.count_player_teslas() < 2, settings);
 
         let building_command_count = state.unoccupied_player_cells.len()*all_buildings.len();
         let deconstruct_count = (settings.size.x as usize *settings.size.y as usize / 2) - state.unoccupied_player_cells.len();
index e063959..711193e 100644 (file)
@@ -10,7 +10,7 @@ use std::io::prelude::*;
 
 #[test]
 fn it_successfully_simulates_replay() {
-    test_from_replay("tests/after_200", 62);
+    test_from_replay("tests/after_200", 64);
 }
 
 fn test_from_replay(replay_folder: &str, length: usize) {