summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/engine/bitwise_engine.rs125
-rw-r--r--tests/expressive_to_bitwise_comparison.rs11
2 files changed, 117 insertions, 19 deletions
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;
}
diff --git a/tests/expressive_to_bitwise_comparison.rs b/tests/expressive_to_bitwise_comparison.rs
index 40181b2..cb57e3b 100644
--- a/tests/expressive_to_bitwise_comparison.rs
+++ b/tests/expressive_to_bitwise_comparison.rs
@@ -38,7 +38,6 @@ fn test_reading_from_replay(replay_folder: &str, length: usize) {
proptest! {
#[test]
- #[ignore]
fn follows_the_same_random_game_tree(seed in any::<[u32;4]>()) {
let mut rng = XorShiftRng::from_seed(seed);
@@ -49,25 +48,25 @@ proptest! {
while expected_status == GameStatus::Continue {
let player_command = random_player_move(&settings, &expressive_state, &mut rng);
let opponent_command = random_opponent_move(&settings, &expressive_state, &mut rng);
+ println!("Player command: {}", player_command);
+ println!("Opponent command: {}", opponent_command);
expected_status = expressive_state.simulate(&settings, player_command, opponent_command);
let actual_status = bitwise_state.simulate(&settings, player_command, opponent_command);
assert_eq!(&expected_status, &actual_status);
- assert_eq!(build_bitwise_from_expressive(&expressive_state), bitwise_state.clone());
+ assert_eq!(build_bitwise_from_expressive(&expressive_state), bitwise_state.sorted());
}
}
}
-
-
fn random_player_move<R: Rng, GS: GameState>(settings: &GameSettings, state: &GS, rng: &mut R) -> Command {
- let all_buildings = sensible_buildings(settings, &state.player(), state.player_has_max_teslas());
+ let all_buildings = sensible_buildings(settings, &state.player(), state.player_has_max_teslas()||true);
random_move(&state.unoccupied_player_cells(), &all_buildings, rng)
}
fn random_opponent_move<R: Rng, GS: GameState>(settings: &GameSettings, state: &GS, rng: &mut R) -> Command {
- let all_buildings = sensible_buildings(settings, &state.opponent(), state.opponent_has_max_teslas());
+ let all_buildings = sensible_buildings(settings, &state.opponent(), state.opponent_has_max_teslas()||true);
random_move(&state.unoccupied_opponent_cells(), &all_buildings, rng)
}