From eacb65f120ca0fcbd920f14160404cb6c709b4ef Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Mon, 14 May 2018 23:31:07 +0200 Subject: Added running total of unoccupied cells --- src/engine/mod.rs | 73 ++++++++++++++++++++++++++------------------- src/json.rs | 25 +++++++++------- src/strategy/monte_carlo.rs | 11 +++---- src/strategy/sample.rs | 5 ++-- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 090d9af..24a5a7e 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -15,7 +15,9 @@ pub struct GameState { pub player: Player, pub opponent: Player, pub player_buildings: Vec, + pub unoccupied_player_cells: Vec, pub opponent_buildings: Vec, + pub unoccupied_opponent_cells: Vec, pub player_missiles: Vec, pub opponent_missiles: Vec } @@ -54,6 +56,26 @@ pub struct Missile { } impl GameState { + pub fn new(player: Player, opponent: Player, player_buildings: Vec, opponent_buildings: Vec, player_missiles: Vec, opponent_missiles: Vec, settings: &GameSettings) -> GameState { + let unoccupied_player_cells = GameState::unoccupied_cells( + &player_buildings, Point::new(0, settings.size.x/2), Point::new(0, settings.size.y) + ); + let unoccupied_opponent_cells = GameState::unoccupied_cells( + &opponent_buildings, Point::new(settings.size.x/2, settings.size.x), Point::new(0, settings.size.y) + ); + GameState { + status: GameStatus::Continue, + player: player, + opponent: opponent, + player_buildings: player_buildings, + unoccupied_player_cells: unoccupied_player_cells, + opponent_buildings: opponent_buildings, + unoccupied_opponent_cells: unoccupied_opponent_cells, + player_missiles: player_missiles, + opponent_missiles: opponent_missiles + } + } + pub fn simulate(&self, settings: &GameSettings, player_command: Command, opponent_command: Command) -> GameState { let mut state = self.clone(); state.simulate_mut(settings, player_command, opponent_command); @@ -65,8 +87,8 @@ impl GameState { return; } - GameState::perform_command(&mut self.player_buildings, &mut self.player, settings, player_command, &settings.size); - GameState::perform_command(&mut self.opponent_buildings, &mut self.opponent, settings, opponent_command, &settings.size); + GameState::perform_command(&mut self.player_buildings, &mut self.player, &mut self.unoccupied_player_cells, settings, player_command, &settings.size); + GameState::perform_command(&mut self.opponent_buildings, &mut self.opponent, &mut self.unoccupied_opponent_cells, settings, opponent_command, &settings.size); GameState::update_construction(&mut self.player_buildings); GameState::update_construction(&mut self.opponent_buildings); @@ -75,9 +97,11 @@ impl GameState { GameState::add_missiles(&mut self.opponent_buildings, &mut self.opponent_missiles); GameState::move_missiles(&mut self.player_missiles, |p| p.move_right(&settings.size), - &mut self.opponent_buildings, &mut self.opponent); + &mut self.opponent_buildings, &mut self.opponent, + &mut self.unoccupied_opponent_cells); GameState::move_missiles(&mut self.opponent_missiles, |p| p.move_left(), - &mut self.player_buildings, &mut self.player); + &mut self.player_buildings, &mut self.player, + &mut self.unoccupied_player_cells); GameState::add_energy(&mut self.player, settings, &self.player_buildings); GameState::add_energy(&mut self.opponent, settings, &self.opponent_buildings); @@ -85,7 +109,7 @@ impl GameState { GameState::update_status(self); } - fn perform_command(buildings: &mut Vec, player: &mut Player, settings: &GameSettings, command: Command, size: &Point) { + fn perform_command(buildings: &mut Vec, player: &mut Player, unoccupied_cells: &mut Vec, settings: &GameSettings, command: Command, size: &Point) { match command { Command::Nothing => { }, Command::Build(p, b) => { @@ -99,6 +123,7 @@ impl GameState { player.energy -= blueprint.price; buildings.push(Building::new(p, blueprint)); + unoccupied_cells.retain(|&pos| pos != p); }, } } @@ -124,7 +149,7 @@ impl GameState { } } - fn move_missiles(missiles: &mut Vec, move_fn: F, opponent_buildings: &mut Vec, opponent: &mut Player) + fn move_missiles(missiles: &mut Vec, move_fn: F, opponent_buildings: &mut Vec, opponent: &mut Player, unoccupied_cells: &mut Vec,) where F: Fn(Point) -> Option { for missile in missiles.iter_mut() { for _ in 0..missile.speed { @@ -139,7 +164,7 @@ impl GameState { for hit in opponent_buildings.iter_mut().filter(|b| b.is_constructed() && b.pos == point/* && b.health > 0*/) { //TODO surely this health>0 belongs? Not what the real game engine is doing unfortunately let damage = cmp::min(missile.damage, hit.health); hit.health -= damage; - missile.speed = 0; + missile.speed = 0; } } } @@ -153,6 +178,10 @@ impl GameState { } } missiles.retain(|m| m.speed > 0); + + for b in opponent_buildings.iter().filter(|b| b.health == 0) { + unoccupied_cells.push(b.pos); + } opponent_buildings.retain(|b| b.health > 0); } @@ -172,32 +201,16 @@ impl GameState { }; } - pub fn unoccupied_player_cells_in_row(&self, settings: &GameSettings, y: u8) -> Vec { - (0..settings.size.x/2) - .map(|x| Point::new(x, y)) - .filter(|&p| !self.player_buildings.iter().any(|b| b.pos == p)) - .collect() - } - - pub fn unoccupied_player_cells(&self, settings: &GameSettings) -> Vec { - let mut result = Vec::with_capacity(settings.size.y as usize *settings.size.x as usize / 2); - for y in 0..settings.size.y { - for x in 0..settings.size.x/2 { - let pos = Point::new(x, y); - if !self.player_buildings.iter().any(|b| b.pos == pos) { - result.push(pos); - } - } - } - result + pub fn unoccupied_player_cells_in_row(&self, y: u8) -> Vec { + self.unoccupied_player_cells.iter().filter(|p| p.y == y).cloned().collect() } - pub fn unoccupied_opponent_cells(&self, settings: &GameSettings) -> Vec { - let mut result = Vec::with_capacity(settings.size.y as usize *settings.size.x as usize / 2); - for y in 0..settings.size.y { - for x in settings.size.x/2..settings.size.x { + fn unoccupied_cells(buildings: &[Building], bl: Point, tr: Point) -> Vec { + let mut result = Vec::with_capacity((tr.y-bl.y) as usize * (tr.x-bl.x) as usize); + for y in bl.y..tr.y { + for x in bl.x..tr.x { let pos = Point::new(x, y); - if !self.opponent_buildings.iter().any(|b| b.pos == pos) { + if !buildings.iter().any(|b| b.pos == pos) { result.push(pos); } } diff --git a/src/json.rs b/src/json.rs index 10c3ab8..4253a19 100644 --- a/src/json.rs +++ b/src/json.rs @@ -12,7 +12,10 @@ pub fn read_state_from_file(filename: &str) -> Result<(engine::settings::GameSet let mut content = String::new(); file.read_to_string(&mut content)?; let state: State = serde_json::from_str(content.as_ref())?; - Ok((state.to_engine_settings(), state.to_engine())) + + let engine_settings = state.to_engine_settings(); + let engine_state = state.to_engine(&engine_settings); + Ok((engine_settings, engine_state)) } #[derive(Deserialize)] @@ -116,16 +119,16 @@ impl State { } } - fn to_engine(&self) -> engine::GameState { - engine::GameState { - status: engine::GameStatus::Continue, - player: self.player().to_engine(), - opponent: self.opponent().to_engine(), - player_buildings: self.buildings_to_engine('A'), - opponent_buildings: self.buildings_to_engine('B'), - player_missiles: self.missiles_to_engine('A'), - opponent_missiles: self.missiles_to_engine('B'), - } + fn to_engine(&self, settings: &engine::settings::GameSettings) -> engine::GameState { + engine::GameState::new( + self.player().to_engine(), + self.opponent().to_engine(), + self.buildings_to_engine('A'), + self.buildings_to_engine('B'), + self.missiles_to_engine('A'), + self.missiles_to_engine('B'), + settings + ) } fn player(&self) -> &Player { diff --git a/src/strategy/monte_carlo.rs b/src/strategy/monte_carlo.rs index 59e86a6..6f0f681 100644 --- a/src/strategy/monte_carlo.rs +++ b/src/strategy/monte_carlo.rs @@ -60,15 +60,13 @@ fn simulate_to_endstate(command_score: &mut CommandScore, settings: &Gam } fn random_player_move(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command { - let all_positions = state.unoccupied_player_cells(settings); let all_buildings = state.player_affordable_buildings(settings); - random_move(&all_positions, &all_buildings, rng) + random_move(&state.unoccupied_player_cells, &all_buildings, rng) } fn random_opponent_move(settings: &GameSettings, state: &GameState, rng: &mut R) -> Command { - let all_positions = state.unoccupied_opponent_cells(settings); let all_buildings = state.opponent_affordable_buildings(settings); - random_move(&all_positions, &all_buildings, rng) + random_move(&state.unoccupied_opponent_cells, &all_buildings, rng) } fn random_move(all_positions: &[Point], all_buildings: &[BuildingType], rng: &mut R) -> Command { @@ -139,13 +137,12 @@ impl CommandScore { } fn enumerate_player_commands(settings: &GameSettings, state: &GameState) -> Vec { - let all_positions = state.unoccupied_player_cells(settings); let all_buildings = state.player_affordable_buildings(settings); - let mut commands = Vec::with_capacity(all_positions.len()*all_buildings.len()+1); + let mut commands = Vec::with_capacity(state.unoccupied_player_cells.len()*all_buildings.len()+1); commands.push(Command::Nothing); - for position in all_positions { + for &position in &state.unoccupied_player_cells { for &building in &all_buildings { commands.push(Command::Build(position, building)); } diff --git a/src/strategy/sample.rs b/src/strategy/sample.rs index 2dad924..370df2f 100644 --- a/src/strategy/sample.rs +++ b/src/strategy/sample.rs @@ -9,7 +9,7 @@ pub fn choose_move(settings: &engine::settings::GameSettings, state: &engine::Ga if state.player.can_afford_defence_buildings(settings) { for y in 0..settings.size.y { if is_under_attack(state, y) { - let p_options = state.unoccupied_player_cells_in_row(settings, y); + let p_options = state.unoccupied_player_cells_in_row(y); if let Some(&p) = rng.choose(&p_options) { return Command::Build(p, BuildingType::Defence); } @@ -18,8 +18,7 @@ pub fn choose_move(settings: &engine::settings::GameSettings, state: &engine::Ga } if state.player.can_afford_all_buildings(settings) { - let options = state.unoccupied_player_cells(settings); - let option = rng.choose(&options); + let option = rng.choose(&state.unoccupied_player_cells); let buildings = [BuildingType::Attack, BuildingType::Defence, BuildingType::Energy]; let building = rng.choose(&buildings); match (option, building) { -- cgit v1.2.3