From 868287bbce9464cb28a6ea816d928af0e876533b Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Sun, 12 Aug 2018 20:41:31 +0200 Subject: Folded duplicate code for player and opponent --- src/engine/bitwise_engine.rs | 46 +++++++++++++++++--------------------------- src/strategy/monte_carlo.rs | 41 ++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/src/engine/bitwise_engine.rs b/src/engine/bitwise_engine.rs index 24189ee..a4842ec 100644 --- a/src/engine/bitwise_engine.rs +++ b/src/engine/bitwise_engine.rs @@ -78,31 +78,6 @@ impl BitwiseGameState { self.update_status(); self.status } - - pub fn player_has_max_teslas(&self) -> bool { self.player.count_teslas() >= TESLA_MAX } - pub fn opponent_has_max_teslas(&self) -> bool { self.opponent.count_teslas() >= TESLA_MAX } - - pub fn player_can_build_iron_curtain(&self) -> bool { - self.player.iron_curtain_available && self.player.iron_curtain_remaining == 0 && self.player.energy >= IRON_CURTAIN_PRICE - } - pub fn opponent_can_build_iron_curtain(&self) -> bool { - self.opponent.iron_curtain_available && self.opponent.iron_curtain_remaining == 0 && self.opponent.energy >= IRON_CURTAIN_PRICE - } - - pub fn unoccupied_player_cell_count(&self) -> usize { self.player.occupied.count_zeros() as usize } - pub fn unoccupied_opponent_cell_count(&self) -> usize { self.opponent.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.occupied, i as u64); - let point = Point { index: bit }; - debug_assert!(point.to_either_bitfield() & self.player.occupied == 0); - point - } - pub fn location_of_unoccupied_opponent_cell(&self, i: usize) -> Point { - let bit = find_bit_index_from_rank(self.opponent.occupied, i as u64); - let point = Point { index: bit }; - debug_assert!(point.to_either_bitfield() & self.opponent.occupied == 0); - point - } } fn find_bit_index_from_rank(occupied: u64, i: u64) -> u8 { @@ -147,9 +122,8 @@ impl BitwiseGameState { } /** - * Like with the expressive, this is to make things more - * comparable when writing tests, not for actual use in the - * engine. + * This is to make things more comparable when writing tests, not + * for actual use in the engine. */ #[cfg(debug_assertions)] pub fn sort(&mut self) { @@ -463,4 +437,20 @@ impl Player { pub fn energy_generated(&self) -> u16 { ENERGY_GENERATED_BASE + self.energy_towers.count_ones() as u16 * ENERGY_GENERATED_TOWER } + + pub fn has_max_teslas(&self) -> bool { + self.count_teslas() >= TESLA_MAX + } + + pub fn can_build_iron_curtain(&self) -> bool { + self.iron_curtain_available && self.iron_curtain_remaining == 0 && self.energy >= IRON_CURTAIN_PRICE + } + + pub fn unoccupied_cell_count(&self) -> usize { self.occupied.count_zeros() as usize } + pub fn location_of_unoccupied_cell(&self, i: usize) -> Point { + let bit = find_bit_index_from_rank(self.occupied, i as u64); + let point = Point { index: bit }; + debug_assert!(point.to_either_bitfield() & self.occupied == 0); + point + } } diff --git a/src/strategy/monte_carlo.rs b/src/strategy/monte_carlo.rs index 87033cb..3e6cb7a 100644 --- a/src/strategy/monte_carlo.rs +++ b/src/strategy/monte_carlo.rs @@ -92,7 +92,7 @@ fn simulate_all_options_once(command_scores: &mut[CommandScore], state: &Bitwise 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(&state_mut, rng); + let opponent_first = random_move(&state_mut.opponent, rng); let mut status = state_mut.simulate(command_score.command, opponent_first); for _ in 0..MAX_MOVES { @@ -100,8 +100,8 @@ fn simulate_to_endstate(command_score: &mut CommandScore, state: &Bitwis break; } - let player_command = random_player_move(&state_mut, rng); - let opponent_command = random_opponent_move(&state_mut, rng); + let player_command = random_move(&state_mut.player, rng); + let opponent_command = random_move(&state_mut.opponent, rng); status = state_mut.simulate(player_command, opponent_command); } @@ -114,30 +114,23 @@ fn simulate_to_endstate(command_score: &mut CommandScore, state: &Bitwis } } -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, state.player_can_build_iron_curtain(), rng, state.unoccupied_player_cell_count(), |i| state.location_of_unoccupied_player_cell(i)) -} - -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, state.opponent_can_build_iron_curtain(), rng, state.unoccupied_opponent_cell_count(), |i| state.location_of_unoccupied_opponent_cell(i)) -} - // TODO: Given enough energy, most opponents won't do nothing -fn random_movePoint>(all_buildings: &[BuildingType], iron_curtain_available: bool, rng: &mut R, free_positions_count: usize, get_point: F) -> Command { +fn random_move(player: &Player, rng: &mut R) -> Command { + let all_buildings = sensible_buildings(player); let nothing_count = 1; - let iron_curtain_count = if iron_curtain_available { 1 } else { 0 }; + let iron_curtain_count = if player.can_build_iron_curtain() { 1 } else { 0 }; + let free_positions_count = player.unoccupied_cell_count(); + let building_choice_index = rng.gen_range(0, all_buildings.len() + nothing_count + iron_curtain_count); if building_choice_index == all_buildings.len() { Command::Nothing - } else if iron_curtain_available && building_choice_index == all_buildings.len() + 1 { + } else if iron_curtain_count > 0 && building_choice_index == all_buildings.len() + 1 { Command::IronCurtain } else if free_positions_count > 0 { let position_choice = rng.gen_range(0, free_positions_count); Command::Build( - get_point(position_choice), + player.location_of_unoccupied_cell(position_choice), all_buildings[building_choice_index] ) } else { @@ -199,15 +192,15 @@ impl CommandScore { //TODO: Devalue nothing so that it doesn't stand and do nothing when it can do things fn init_command_scores(state: &BitwiseGameState) -> Vec { - let all_buildings = sensible_buildings(&state.player, state.player_has_max_teslas()); + let all_buildings = sensible_buildings(&state.player); - let unoccupied_cells = (0..state.unoccupied_player_cell_count()).map(|i| state.location_of_unoccupied_player_cell(i)); + let unoccupied_cells = (0..state.player.unoccupied_cell_count()).map(|i| state.player.location_of_unoccupied_cell(i)); let building_command_count = unoccupied_cells.len()*all_buildings.len(); let mut commands = Vec::with_capacity(building_command_count + 2); commands.push(CommandScore::new(Command::Nothing)); - if state.player_can_build_iron_curtain() { + if state.player.can_build_iron_curtain() { commands.push(CommandScore::new(Command::IronCurtain)); } @@ -222,7 +215,7 @@ impl CommandScore { } #[cfg(not(feature = "energy-cutoff"))] -fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec { +fn sensible_buildings(player: &Player) -> Vec { let mut result = Vec::with_capacity(4); if DEFENCE_PRICE <= player.energy { @@ -234,7 +227,7 @@ fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec Vec Vec { +fn sensible_buildings(player: &Player) -> Vec { let mut result = Vec::with_capacity(4); let needs_energy = player.energy_generated() <= ENERGY_PRODUCTION_CUTOFF || player.energy <= ENERGY_STORAGE_CUTOFF; @@ -259,7 +252,7 @@ fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec