diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/engine/bitwise_engine.rs | 53 | ||||
-rw-r--r-- | src/engine/constants.rs | 2 | ||||
-rw-r--r-- | src/input/json.rs | 2 | ||||
-rw-r--r-- | src/strategy/monte_carlo.rs | 51 |
4 files changed, 82 insertions, 26 deletions
diff --git a/src/engine/bitwise_engine.rs b/src/engine/bitwise_engine.rs index 27c1bd8..f8af86d 100644 --- a/src/engine/bitwise_engine.rs +++ b/src/engine/bitwise_engine.rs @@ -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); + } +} diff --git a/src/engine/constants.rs b/src/engine/constants.rs index 60e8101..71d937d 100644 --- a/src/engine/constants.rs +++ b/src/engine/constants.rs @@ -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; diff --git a/src/input/json.rs b/src/input/json.rs index 843f228..6c83563 100644 --- a/src/input/json.rs +++ b/src/input/json.rs @@ -137,6 +137,8 @@ impl State { } } } + player.update_energy_towers_with_heuristics(); + opponent.update_energy_towers_with_heuristics(); bitwise_engine::BitwiseGameState::new( player, opponent, diff --git a/src/strategy/monte_carlo.rs b/src/strategy/monte_carlo.rs index 6599212..75ca055 100644 --- a/src/strategy/monte_carlo.rs +++ b/src/strategy/monte_carlo.rs @@ -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() { |