summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2018-08-12 20:41:31 +0200
committerJustin Worthe <justin@worthe-it.co.za>2018-08-12 20:41:31 +0200
commit868287bbce9464cb28a6ea816d928af0e876533b (patch)
tree52f2d3f3c2d6438091cb1453400353e364988c9a
parent629c70c86b0bb29d2d0c281add9d0d826a11c419 (diff)
Folded duplicate code for player and opponent
-rw-r--r--src/engine/bitwise_engine.rs46
-rw-r--r--src/strategy/monte_carlo.rs41
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<R: Rng>(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<R: Rng>(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<R: Rng>(command_score: &mut CommandScore, state: &Bitwis
}
}
-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, state.player_can_build_iron_curtain(), rng, state.unoccupied_player_cell_count(), |i| state.location_of_unoccupied_player_cell(i))
-}
-
-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, 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_move<R: Rng, F:Fn(usize)->Point>(all_buildings: &[BuildingType], iron_curtain_available: bool, rng: &mut R, free_positions_count: usize, get_point: F) -> Command {
+fn random_move<R: Rng>(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<CommandScore> {
- 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<BuildingType> {
+fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
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<BuildingType
if ENERGY_PRICE <= player.energy {
result.push(BuildingType::Energy);
}
- if TESLA_PRICE <= player.energy && !has_max_teslas {
+ if TESLA_PRICE <= player.energy && !player.has_max_teslas() {
result.push(BuildingType::Tesla);
}
@@ -245,7 +238,7 @@ fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec<BuildingType
//TODO: Heuristic that avoids building the initial energy towers all in the same row? Max energy in a row?
//TODO: Update cutoff to maybe build iron curtains
#[cfg(feature = "energy-cutoff")]
-fn sensible_buildings(player: &Player, has_max_teslas: bool) -> Vec<BuildingType> {
+fn sensible_buildings(player: &Player) -> Vec<BuildingType> {
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<BuildingType
if ENERGY_PRICE <= player.energy && needs_energy {
result.push(BuildingType::Energy);
}
- if TESLA_PRICE <= player.energy && !has_max_teslas {
+ if TESLA_PRICE <= player.energy && !player.has_max_teslas() {
result.push(BuildingType::Tesla);
}