From 8251d5899a64515c9b2e8a71349dd4d2fcece69e Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Mon, 2 Jul 2018 21:29:52 +0200 Subject: Game engine working, except for teslas and choosing a move --- src/engine/bitwise_engine.rs | 125 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/engine/bitwise_engine.rs b/src/engine/bitwise_engine.rs index d8e7868..e3ca4c6 100644 --- a/src/engine/bitwise_engine.rs +++ b/src/engine/bitwise_engine.rs @@ -3,7 +3,9 @@ use engine::geometry::Point; use engine::settings::{GameSettings}; use engine::{GameStatus, Player, GameState}; -const MAP_WIDTH: usize = 16; +const FULL_MAP_WIDTH: u8 = 16; +const SINGLE_MAP_WIDTH: u8 = FULL_MAP_WIDTH/2; +const MAX_CONCURRENT_MISSILES: usize = SINGLE_MAP_WIDTH as usize / 2; const MISSILE_COOLDOWN: usize = 3; @@ -11,6 +13,9 @@ const DEFENCE_HEALTH: usize = 4; // '20' health is 4 hits const MAX_TESLAS: usize = 2; +const LEFT_COL_MASK: u64 = 0x0101010101010101; +const RIGHT_COL_MASK: u64 = 0x8080808080808080; + #[derive(Debug, Clone, PartialEq, Eq)] pub struct BitwiseGameState { pub status: GameStatus, @@ -29,7 +34,7 @@ pub struct PlayerBuildings { pub energy_towers: u64, pub missile_towers: [u64; MISSILE_COOLDOWN+1], - pub missiles: [(u64, u64); MAP_WIDTH/4], + pub missiles: [(u64, u64); MAX_CONCURRENT_MISSILES], pub tesla_cooldowns: [TeslaCooldown; MAX_TESLAS] } @@ -61,7 +66,9 @@ impl GameState for BitwiseGameState { BitwiseGameState::add_left_missiles(&mut self.player_buildings); BitwiseGameState::add_right_missiles(&mut self.opponent_buildings); - //TODO: Move and collide missiles + + BitwiseGameState::move_left_and_collide_missiles(settings, &mut self.player, &mut self.player_buildings, &mut self.opponent_buildings.missiles); + BitwiseGameState::move_right_and_collide_missiles(settings, &mut self.opponent, &mut self.opponent_buildings, &mut self.player_buildings.missiles); BitwiseGameState::add_energy(settings, &mut self.player, &mut self.player_buildings); BitwiseGameState::add_energy(settings, &mut self.opponent, &mut self.opponent_buildings); @@ -91,12 +98,45 @@ impl BitwiseGameState { } } + /** + * Like with the expressive, this is to make things more + * comparable when writing tests, not for actual use in the + * engine. + */ + pub fn sort(&mut self) { + for i in 0..MAX_CONCURRENT_MISSILES { + for j in i+1..MAX_CONCURRENT_MISSILES { + let move_down1 = !self.player_buildings.missiles[i].0 & self.player_buildings.missiles[j].0; + self.player_buildings.missiles[i].0 |= move_down1; + self.player_buildings.missiles[j].0 &= !move_down1; + + let move_down2 = !self.player_buildings.missiles[i].1 & self.player_buildings.missiles[j].1; + self.player_buildings.missiles[i].1 |= move_down2; + self.player_buildings.missiles[j].1 &= !move_down2; + + let move_down3 = !self.opponent_buildings.missiles[i].0 & self.opponent_buildings.missiles[j].0; + self.opponent_buildings.missiles[i].0 |= move_down3; + self.opponent_buildings.missiles[j].0 &= !move_down3; + + let move_down4 = !self.opponent_buildings.missiles[i].1 & self.opponent_buildings.missiles[j].1; + self.opponent_buildings.missiles[i].1 |= move_down4; + self.opponent_buildings.missiles[j].1 &= !move_down4; + } + } + } + + pub fn sorted(&self) -> BitwiseGameState { + let mut res = self.clone(); + res.sort(); + res + } + fn perform_command(settings: &GameSettings, player: &mut Player, player_buildings: &mut PlayerBuildings, command: Command) { match command { Command::Nothing => {}, Command::Build(p, b) => { let blueprint = settings.building_settings(b); - let bitfield = p.to_either_bitfield(settings.size.x); + let bitfield = p.to_either_bitfield(SINGLE_MAP_WIDTH); // This is used internally. I should not be making // invalid moves! @@ -116,7 +156,7 @@ impl BitwiseGameState { }, Command::Deconstruct(p) => { let unconstructed_to_remove_index = player_buildings.unconstructed.iter().position(|ref b| b.pos == p); - let deconstruct_mask = !(p.to_either_bitfield(settings.size.x) & player_buildings.buildings[0]); + let deconstruct_mask = !(p.to_either_bitfield(SINGLE_MAP_WIDTH) & player_buildings.buildings[0]); debug_assert!(deconstruct_mask != 0 || unconstructed_to_remove_index.is_some()); @@ -150,7 +190,7 @@ impl BitwiseGameState { let building_type = player_buildings.unconstructed[i].building_type; let blueprint = settings.building_settings(building_type); let pos = player_buildings.unconstructed[i].pos; - let bitfield = pos.to_either_bitfield(settings.size.x); + let bitfield = pos.to_either_bitfield(SINGLE_MAP_WIDTH); for health_tier in 0..4 { if blueprint.health > health_tier*5 { @@ -216,20 +256,79 @@ impl BitwiseGameState { } - fn move_and_collide_missiles_left(settings: &GameSettings, player_buildings: &mut PlayerBuildings, opponent: &mut Player) { + fn move_left_and_collide_missiles(settings: &GameSettings, opponent: &mut Player, opponent_buildings: &mut PlayerBuildings, player_missiles: &mut [(u64, u64); MAX_CONCURRENT_MISSILES]) { + for _ in 0..settings.attack.weapon_speed { + for i in 0..player_missiles.len() { + let about_to_hit_opponent = player_missiles[i].0 & LEFT_COL_MASK; + let damage = about_to_hit_opponent.count_ones() as u8 * settings.attack.weapon_damage; + opponent.health = opponent.health.saturating_sub(damage); + player_missiles[i].0 = (player_missiles[i].0 & !LEFT_COL_MASK) >> 1; + + let swapping_sides = player_missiles[i].1 & LEFT_COL_MASK; + player_missiles[i].0 |= swapping_sides << 7; + player_missiles[i].1 = (player_missiles[i].1 & !LEFT_COL_MASK) >> 1; + + + let mut hits = 0; + for health_tier in (0..DEFENCE_HEALTH).rev() { + hits = opponent_buildings.buildings[health_tier] & player_missiles[i].0; + player_missiles[i].0 &= !hits; + opponent_buildings.buildings[health_tier] &= !hits; + } + + let deconstruct_mask = !hits; + opponent_buildings.energy_towers &= deconstruct_mask; + for tier in 0..opponent_buildings.missile_towers.len() { + opponent_buildings.missile_towers[tier] &= deconstruct_mask; + } + for tesla in 0..opponent_buildings.tesla_cooldowns.len() { + if opponent_buildings.tesla_cooldowns[tesla].pos.to_either_bitfield(SINGLE_MAP_WIDTH) & deconstruct_mask == 0 { + opponent_buildings.tesla_cooldowns[tesla].active = false; + } + } + opponent_buildings.occupied &= deconstruct_mask; + } + } + } + + fn move_right_and_collide_missiles(settings: &GameSettings, opponent: &mut Player, opponent_buildings: &mut PlayerBuildings, player_missiles: &mut [(u64, u64); MAX_CONCURRENT_MISSILES]) { for _ in 0..settings.attack.weapon_speed { - for i in 0..player_buildings.missiles.len() { - //TODO this isn't so simple... - //collide some with the player, others jump the boundary - player_buildings.missiles[i].0 = player_buildings.missiles[i].0 << 1; - //TODO Collide with buildings + for i in 0..player_missiles.len() { + let about_to_hit_opponent = player_missiles[i].1 & RIGHT_COL_MASK; + let damage = about_to_hit_opponent.count_ones() as u8 * settings.attack.weapon_damage; + opponent.health = opponent.health.saturating_sub(damage); + player_missiles[i].1 = (player_missiles[i].1 & !RIGHT_COL_MASK) << 1; + + let swapping_sides = player_missiles[i].0 & RIGHT_COL_MASK; + player_missiles[i].1 |= swapping_sides >> 7; + player_missiles[i].0 = (player_missiles[i].0 & !RIGHT_COL_MASK) << 1; + + + let mut hits = 0; + for health_tier in (0..DEFENCE_HEALTH).rev() { + hits = opponent_buildings.buildings[health_tier] & player_missiles[i].1; + player_missiles[i].1 &= !hits; + opponent_buildings.buildings[health_tier] &= !hits; + } + + let deconstruct_mask = !hits; + opponent_buildings.energy_towers &= deconstruct_mask; + for tier in 0..opponent_buildings.missile_towers.len() { + opponent_buildings.missile_towers[tier] &= deconstruct_mask; + } + for tesla in 0..opponent_buildings.tesla_cooldowns.len() { + if opponent_buildings.tesla_cooldowns[tesla].pos.to_either_bitfield(SINGLE_MAP_WIDTH) & deconstruct_mask == 0 { + opponent_buildings.tesla_cooldowns[tesla].active = false; + } + } + opponent_buildings.occupied &= deconstruct_mask; } } } fn add_energy(settings: &GameSettings, player: &mut Player, player_buildings: &mut PlayerBuildings) { - player.energy_generated = player_buildings.energy_towers.count_ones() as u16 * settings.energy.energy_generated_per_turn; + player.energy_generated = settings.energy_income + player_buildings.energy_towers.count_ones() as u16 * settings.energy.energy_generated_per_turn; player.energy += player.energy_generated; } -- cgit v1.2.3