Compilation, allowing new moves to be chosen, and missile move order
authorJustin Worthe <justin@worthe-it.co.za>
Mon, 25 Jun 2018 17:04:29 +0000 (19:04 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Mon, 25 Jun 2018 17:04:29 +0000 (19:04 +0200)
src/engine/mod.rs
src/strategy/monte_carlo.rs

index 13ac0ac..15b7a4d 100644 (file)
@@ -198,34 +198,48 @@ impl GameState {
     }
 
     fn fire_teslas(player: &mut Player, player_buildings: &mut Vec<Building>, opponent: &mut Player, opponent_buildings: &mut Vec<Building>, settings: &GameSettings) {
-        for tesla in player_buildings.iter().filter(|b| b.weapon_damage == 20) {
-            if tesla.pos.x + 1 >= settings.size.x/2 {
-                opponent.health = opponent.health.saturating_sub(settings.tesla.weapon_damage);
-            }
-            'player_col_loop: for x in tesla.pos.x+1..tesla.pos.x+(settings.size.x/2)+2 {
-                for &y in [tesla.pos.y - 1, tesla.pos.y, tesla.pos.y + 1].iter() {
-                    let target_point = Point::new(x, y);
-                    for b in 0..opponent_buildings.len() {
-                        if opponent_buildings[b].pos == target_point && opponent_buildings[b].health > 0 {
-                            opponent_buildings[b].health = opponent_buildings[b].health.saturating_sub(settings.tesla.weapon_damage);
-                            continue 'player_col_loop;
+        for tesla in player_buildings.iter_mut().filter(|b| b.weapon_damage == 20) {
+            if tesla.weapon_cooldown_time_left > 0 {
+                tesla.weapon_cooldown_time_left -= 1;
+            } else if player.energy >= 100 {
+                player.energy -= 100;
+                tesla.weapon_cooldown_time_left = tesla.weapon_cooldown_period;
+
+                if tesla.pos.x + 1 >= settings.size.x/2 {
+                    opponent.health = opponent.health.saturating_sub(settings.tesla.weapon_damage);
+                }
+                'player_col_loop: for x in tesla.pos.x+1..tesla.pos.x+(settings.size.x/2)+2 {
+                    for &y in [tesla.pos.y - 1, tesla.pos.y, tesla.pos.y + 1].iter() {
+                        let target_point = Point::new(x, y);
+                        for b in 0..opponent_buildings.len() {
+                            if opponent_buildings[b].pos == target_point && opponent_buildings[b].health > 0 {
+                                opponent_buildings[b].health = opponent_buildings[b].health.saturating_sub(settings.tesla.weapon_damage);
+                                continue 'player_col_loop;
+                            }
                         }
                     }
                 }
             }
         }
 
-        for tesla in opponent_buildings.iter().filter(|b| b.weapon_damage == 20) {
-            if tesla.pos.x <= settings.size.x/2 {
-                player.health = player.health.saturating_sub(settings.tesla.weapon_damage);
-            }
-            'opponent_col_loop: for x in tesla.pos.x.saturating_sub((settings.size.x/2)+1)..tesla.pos.x {
-                for &y in [tesla.pos.y - 1, tesla.pos.y, tesla.pos.y + 1].iter() {
-                    let target_point = Point::new(x, y);
-                    for b in 0..player_buildings.len() {
-                        if player_buildings[b].pos == target_point && player_buildings[b].health > 0 {
-                            player_buildings[b].health = player_buildings[b].health.saturating_sub(settings.tesla.weapon_damage);
-                            continue 'opponent_col_loop;
+        for tesla in opponent_buildings.iter_mut().filter(|b| b.weapon_damage == 20) {
+            if tesla.weapon_cooldown_time_left > 0 {
+                tesla.weapon_cooldown_time_left -= 1;
+            } else if opponent.energy >= 100 {
+                opponent.energy -= 100;
+                tesla.weapon_cooldown_time_left = tesla.weapon_cooldown_period;
+                
+                if tesla.pos.x <= settings.size.x/2 {
+                    player.health = player.health.saturating_sub(settings.tesla.weapon_damage);
+                }
+                'opponent_col_loop: for x in tesla.pos.x.saturating_sub((settings.size.x/2)+1)..tesla.pos.x {
+                    for &y in [tesla.pos.y - 1, tesla.pos.y, tesla.pos.y + 1].iter() {
+                        let target_point = Point::new(x, y);
+                        for b in 0..player_buildings.len() {
+                            if player_buildings[b].pos == target_point && player_buildings[b].health > 0 {
+                                player_buildings[b].health = player_buildings[b].health.saturating_sub(settings.tesla.weapon_damage);
+                                continue 'opponent_col_loop;
+                            }
                         }
                     }
                 }
@@ -253,8 +267,8 @@ impl GameState {
     fn move_missiles<F>(missiles: &mut Vec<Missile>, mut wrapping_move_fn: F, opponent_buildings: &mut Vec<Building>, opponent: &mut Player, unoccupied_cells: &mut Vec<Point>, settings: &GameSettings)
     where F: FnMut(&mut Point) {
         let mut missiles_len = missiles.len();
-        'missile_loop: for m in (0..missiles.len()).rev() {
-            'speed_loop: for _ in 0..missiles[m].speed {
+        'speed_loop: for _ in 0..settings.attack.weapon_speed {
+            'missile_loop: for m in (0..missiles.len()).rev() {
                 wrapping_move_fn(&mut missiles[m].pos);
                 if missiles[m].pos.x >= settings.size.x {
                     opponent.health = opponent.health.saturating_sub(missiles[m].damage);
@@ -283,8 +297,8 @@ impl GameState {
                     }
                 }
             }
+            missiles.truncate(missiles_len);
         }
-        missiles.truncate(missiles_len);
     }
 
     fn add_energy(player: &mut Player) {
index 0d813d3..1ea18db 100644 (file)
@@ -76,25 +76,35 @@ 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, &all_buildings, rng)
+    random_move(&settings, &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, &all_buildings, rng)
+    random_move(&settings, &state.unoccupied_opponent_cells, &all_buildings, rng)
 }
 
-fn random_move<R: Rng>(all_positions: &[Point], all_buildings: &[BuildingType], rng: &mut R) -> Command {
-    let number_of_commands = all_positions.len()*all_buildings.len()+1;
+fn random_move<R: Rng>(settings: &GameSettings, all_positions: &[Point], all_buildings: &[BuildingType], rng: &mut R) -> Command {
+    
+    let building_command_count = all_positions.len()*all_buildings.len();
+    let deconstruct_count = (settings.size.x as usize * settings.size.y as usize / 2) - all_positions.len();
+    let nothing_count = 1;
+
+    let number_of_commands = building_command_count + deconstruct_count + nothing_count;
+    
     let choice_index = rng.gen_range(0, number_of_commands);
 
     if choice_index == number_of_commands - 1 {
         Command::Nothing
-    } else {
+    } else if choice_index < building_command_count {
         Command::Build(
             all_positions[choice_index/all_buildings.len()],
             all_buildings[choice_index%all_buildings.len()]
         )
+    } else {
+        Command::Deconstruct(
+            all_positions[choice_index-building_command_count]
+        )
     }
 }
 
@@ -153,7 +163,11 @@ impl CommandScore {
     fn init_command_scores(settings: &GameSettings, state: &GameState) -> Vec<CommandScore> {
         let all_buildings = state.player.sensible_buildings(settings);
 
-        let mut commands = Vec::with_capacity(state.unoccupied_player_cells.len()*all_buildings.len()+1);
+        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();
+        let nothing_count = 1;
+        
+        let mut commands = Vec::with_capacity(building_command_count + deconstruct_count + nothing_count);
         commands.push(CommandScore::new(Command::Nothing));
 
         for &position in &state.unoccupied_player_cells {
@@ -162,6 +176,13 @@ impl CommandScore {
             }
         }
 
+        for building in &state.player_buildings {
+            commands.push(CommandScore::new(Command::Deconstruct(building.pos)));
+        }
+        for building in &state.player_unconstructed_buildings {
+            commands.push(CommandScore::new(Command::Deconstruct(building.pos)));
+        }
+
         commands
     }
 }