Tweaked performance for enegy tower limiting
authorJustin Worthe <justin@worthe-it.co.za>
Sun, 19 Aug 2018 12:57:06 +0000 (14:57 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Sun, 19 Aug 2018 12:57:06 +0000 (14:57 +0200)
Makefile
src/engine/bitwise_engine.rs
src/engine/constants.rs
src/input/json.rs
src/strategy/monte_carlo.rs

index 42ce706..ec1506f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -11,9 +11,7 @@ profile:
        cargo build --release --features "benchmarking single-threaded extended-time"
        mkdir -p target/profile
        perf record -g target/release/perf-test
-       perf script > target/profile/out.perf
-       ../FlameGraph/stackcollapse-perf.pl target/profile/out.perf > target/profile/out.folded
-       ../FlameGraph/flamegraph.pl target/profile/out.folded > target/profile/flamegraph.svg
+       perf report
 
 clean:
        cargo clean
index 27c1bd8..f8af86d 100644 (file)
@@ -34,6 +34,8 @@ pub struct Player {
 
     pub iron_curtain_available: bool,
     pub iron_curtain_remaining: u8,
+
+    pub energy_towers_with_heuristics: u64,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -73,6 +75,9 @@ impl BitwiseGameState {
         BitwiseGameState::update_iron_curtain(&mut self.player, self.round);
         BitwiseGameState::update_iron_curtain(&mut self.opponent, self.round);
 
+        self.player.update_energy_towers_with_heuristics();
+        self.opponent.update_energy_towers_with_heuristics();
+
         self.round += 1;
 
         self.update_status();
@@ -430,7 +435,8 @@ impl Player {
             missiles: [(0,0); MISSILE_MAX_SINGLE_CELL],
             tesla_cooldowns: ArrayVec::new(),
             iron_curtain_available: false,
-            iron_curtain_remaining: 0
+            iron_curtain_remaining: 0,
+            energy_towers_with_heuristics: 0
         }
     }
 
@@ -454,28 +460,57 @@ impl Player {
         point
     }
 
-    pub fn energy_occupied_energy_with_heuristics(&self) -> u64 {
-        let mut result = 0;
+    pub fn update_energy_towers_with_heuristics(&mut self) {
+        self.energy_towers_with_heuristics = self.occupied;
         for y in 0..MAP_HEIGHT {
-            let mask = 255 << y*SINGLE_MAP_WIDTH;
+            let mask = 255u64 << (y*SINGLE_MAP_WIDTH);
             let isolated_row = self.energy_towers & mask;
             let row_count = isolated_row.count_ones();
-            result |= if row_count > ENERGY_MAX_IN_ROW {
+            self.energy_towers_with_heuristics |= if row_count >= ENERGY_MAX_IN_ROW {
                 mask
             } else {
-                self.occupied & mask
+                0
             };
         }
-        result
     }
 
     pub fn unoccupied_energy_cell_count(&self) -> usize {
-        self.energy_occupied_energy_with_heuristics().count_zeros() as usize
+        self.energy_towers_with_heuristics.count_zeros() as usize
     }
     pub fn location_of_unoccupied_energy_cell(&self, i: usize) -> Point  {
-        let bit = find_bit_index_from_rank(self.energy_occupied_energy_with_heuristics(), i as u64);
+        let bit = find_bit_index_from_rank(self.energy_towers_with_heuristics, i as u64);
         let point = Point { index: bit };
         debug_assert!(point.to_either_bitfield() & self.occupied == 0);
         point
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn energy_occupied_marks_empty_rows_appropriately() {
+        let mut player = Player::empty();
+        player.energy_towers = 0;
+        player.occupied = 0;
+        player.update_energy_towers_with_heuristics();
+
+        let expected = 0;
+        let actual = player.energy_towers_with_heuristics;
+
+        assert_eq!(expected, actual);
+    }
+        #[test]
+    fn energy_occupied_marks_full_first_row_appropriately() {
+        let mut player = Player::empty();
+        player.energy_towers = 0x00_07; //3 energy towers in first row, don't build more
+        player.occupied = 0x07_07; //3 more towers in second row, can still build by them
+        player.update_energy_towers_with_heuristics();
+
+        let expected = 0x07_ff;
+        let actual = player.energy_towers_with_heuristics;
+
+        assert_eq!(expected, actual);
+    }
+}
index 60e8101..71d937d 100644 (file)
@@ -26,7 +26,7 @@ pub const ENERGY_GENERATED_TOWER: u16 = 3;
 pub const ENERGY_PRICE: u16 = 20;
 pub const ENERGY_CONSTRUCTION_TIME: u8 = 1;
 
-pub const ENERGY_MAX_IN_ROW: u32 = 2;
+pub const ENERGY_MAX_IN_ROW: u32 = 3;
 
 pub const IRON_CURTAIN_PRICE: u16 = 100;
 pub const IRON_CURTAIN_UNLOCK_INTERVAL: u16 = 30;
index 843f228..6c83563 100644 (file)
@@ -137,6 +137,8 @@ impl State {
                 }
             }
         }
+        player.update_energy_towers_with_heuristics();
+        opponent.update_energy_towers_with_heuristics();
             
         bitwise_engine::BitwiseGameState::new(
             player, opponent,
index 6599212..75ca055 100644 (file)
@@ -7,6 +7,8 @@ use std::fmt;
 
 use rand::{Rng, XorShiftRng, SeedableRng};
 
+use arrayvec::ArrayVec;
+
 const MAX_MOVES: u16 = 400;
 const INIT_SEED: [u8;16] = [0x7b, 0x6a, 0xe1, 0xf4, 0x41, 0x3c, 0xe9, 0x0f, 0x67, 0x81, 0x67, 0x99, 0x77, 0x0a, 0x6b, 0xda];
 
@@ -159,18 +161,24 @@ fn simulate_to_endstate<R: Rng>(command_score: &mut CommandScore, state: &Bitwis
 }
 
 fn random_move<R: Rng>(player: &Player, rng: &mut R) -> Command {
-    let all_buildings = sensible_buildings(player);
     let free_positions_count = player.unoccupied_cell_count();
     let unoccupied_energy_cell_count = player.unoccupied_energy_cell_count();
-    
-    let nothing_count = if all_buildings.len() > 2 && free_positions_count > 0 { 0 } else { 1 };
+
+    let open_energy_spot = unoccupied_energy_cell_count > 0;
+    let open_building_spot = free_positions_count > 0;
+
+    let all_buildings = sensible_buildings(player, open_building_spot, open_energy_spot);
+
     let iron_curtain_count = if player.can_build_iron_curtain() { 1 } else { 0 };
-        
+    //TODO: This appears to make things much slower. Or maybe games last longer?
+    let nothing_count = 1;//if all_buildings.len() + iron_curtain_count > 0 { 0 } else { 1 };
+
     let building_choice_index = rng.gen_range(0, all_buildings.len() + nothing_count + iron_curtain_count);
 
-    if building_choice_index < all_buildings.len()
-        && all_buildings[building_choice_index] == BuildingType::Energy
-        && unoccupied_energy_cell_count > 0 {
+    let choice_is_building = building_choice_index < all_buildings.len();
+    let choice_is_energy = choice_is_building && all_buildings[building_choice_index] == BuildingType::Energy;
+
+    if choice_is_energy {
         let position_choice = rng.gen_range(0, unoccupied_energy_cell_count);
         Command::Build(
             player.location_of_unoccupied_energy_cell(position_choice),
@@ -178,7 +186,7 @@ fn random_move<R: Rng>(player: &Player, rng: &mut R) -> Command {
         )
 
     }
-    else if building_choice_index < all_buildings.len() && free_positions_count > 0 {
+    else if choice_is_building {
         let position_choice = rng.gen_range(0, free_positions_count);
         Command::Build(
             player.location_of_unoccupied_cell(position_choice),
@@ -257,11 +265,15 @@ impl CommandScore {
     }
 
     fn init_command_scores(state: &BitwiseGameState) -> Vec<CommandScore> {
-        let all_buildings = sensible_buildings(&state.player);
-
         let unoccupied_cells = (0..state.player.unoccupied_cell_count()).map(|i| state.player.location_of_unoccupied_cell(i)).collect::<Vec<_>>();
         let unoccupied_energy_cells = (0..state.player.unoccupied_energy_cell_count()).map(|i| state.player.location_of_unoccupied_energy_cell(i)).collect::<Vec<_>>();
 
+        let open_building_spot = unoccupied_cells.len() > 0;
+        let open_energy_spot = unoccupied_energy_cells.len() > 0;
+
+        
+        let all_buildings = sensible_buildings(&state.player, open_building_spot, open_energy_spot);
+
         let building_command_count = unoccupied_cells.len()*all_buildings.len();
         
         let mut commands = Vec::with_capacity(building_command_count + 2);
@@ -290,8 +302,11 @@ impl fmt::Display for CommandScore {
 
 
 #[cfg(not(feature = "energy-cutoff"))]
-fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
-    let mut result = Vec::with_capacity(4);
+fn sensible_buildings(player: &Player, open_building_spot: bool, open_energy_spot: bool) -> ArrayVec<[BuildingType;4]> {
+    let mut result = ArrayVec::new();
+    if !open_building_spot {
+        return result;
+    }
 
     if DEFENCE_PRICE <= player.energy {
         result.push(BuildingType::Defence);
@@ -299,7 +314,7 @@ fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
     if MISSILE_PRICE <= player.energy {
         result.push(BuildingType::Attack);
     }
-    if ENERGY_PRICE <= player.energy {
+    if ENERGY_PRICE <= player.energy && open_energy_spot {
         result.push(BuildingType::Energy);
     }
     if TESLA_PRICE <= player.energy && !player.has_max_teslas() {
@@ -310,8 +325,12 @@ fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
 }
 
 #[cfg(feature = "energy-cutoff")]
-fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
-    let mut result = Vec::with_capacity(4);
+fn sensible_buildings(player: &Player, open_building_spot: bool, open_energy_spot: bool) -> ArrayVec<[BuildingType;4]> {
+    let mut result = ArrayVec::new();
+    if !open_building_spot {
+        return result;
+    }
+
     let needs_energy = player.energy_generated() <= ENERGY_PRODUCTION_CUTOFF ||
         player.energy <= ENERGY_STORAGE_CUTOFF;
 
@@ -321,7 +340,7 @@ fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
     if MISSILE_PRICE <= player.energy {
         result.push(BuildingType::Attack);
     }
-    if ENERGY_PRICE <= player.energy && needs_energy {
+    if ENERGY_PRICE <= player.energy && open_energy_spot && needs_energy {
         result.push(BuildingType::Energy);
     }
     if TESLA_PRICE <= player.energy && !player.has_max_teslas() {