summaryrefslogtreecommitdiff
path: root/2018-tower-defence/src/input/json.rs
diff options
context:
space:
mode:
Diffstat (limited to '2018-tower-defence/src/input/json.rs')
-rw-r--r--2018-tower-defence/src/input/json.rs191
1 files changed, 191 insertions, 0 deletions
diff --git a/2018-tower-defence/src/input/json.rs b/2018-tower-defence/src/input/json.rs
new file mode 100644
index 0000000..a71d49e
--- /dev/null
+++ b/2018-tower-defence/src/input/json.rs
@@ -0,0 +1,191 @@
+use std::fs::File;
+use std::io::prelude::*;
+use serde_json;
+use std::error::Error;
+
+use engine;
+use engine::command;
+use engine::bitwise_engine;
+use engine::constants::*;
+
+pub fn read_bitwise_state_from_file(filename: &str) -> Result<bitwise_engine::BitwiseGameState, Box<Error>> {
+ let mut file = File::open(filename)?;
+ let mut content = String::new();
+ file.read_to_string(&mut content)?;
+ let state: State = serde_json::from_str(content.as_ref())?;
+
+ let engine_state = state.to_bitwise_engine();
+ Ok(engine_state)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct State {
+ game_details: GameDetails,
+ players: Vec<Player>,
+ game_map: Vec<Vec<GameCell>>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GameDetails {
+ round: u16
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Player {
+ player_type: char,
+ energy: u16,
+ health: u8,
+ iron_curtain_available: bool,
+ active_iron_curtain_lifetime: i16
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GameCell {
+ x: u8,
+ y: u8,
+ buildings: Vec<BuildingState>,
+ missiles: Vec<MissileState>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct BuildingState {
+ health: u8,
+ construction_time_left: i16,
+ weapon_cooldown_time_left: u8,
+ building_type: String,
+ x: u8,
+ y: u8,
+ player_type: char
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MissileState {
+ player_type: char
+}
+
+
+impl State {
+ fn to_bitwise_engine(&self) -> bitwise_engine::BitwiseGameState {
+ let mut player = bitwise_engine::Player::empty();
+ let mut opponent = bitwise_engine::Player::empty();
+
+ self.player().map_onto_engine(&mut player);
+ self.opponent().map_onto_engine(&mut opponent);
+
+ for row in &self.game_map {
+ for cell in row {
+ let point = engine::geometry::Point::new(cell.x, cell.y);
+ for building in &cell.buildings {
+ let building_type = building.convert_building_type();
+
+ let mut bitwise_buildings = if building.player_type == 'A' {
+ &mut player
+ } else {
+ &mut opponent
+ };
+ let bitfield = point.to_bitfield();
+
+ bitwise_buildings.occupied |= bitfield;
+ if building.construction_time_left >= 0 {
+ bitwise_buildings.unconstructed.push(building.to_bitwise_engine_unconstructed());
+ } else {
+ for health_tier in 0..DEFENCE_HEALTH {
+ if building.health > health_tier as u8 * MISSILE_DAMAGE {
+ bitwise_buildings.buildings[health_tier] |= bitfield;
+ }
+ }
+ if building_type == command::BuildingType::Energy {
+ bitwise_buildings.energy_towers |= bitfield;
+ }
+ else if building_type == command::BuildingType::Attack {
+ for cooldown_tier in 0..MISSILE_COOLDOWN + 1 {
+ if building.weapon_cooldown_time_left == cooldown_tier as u8 {
+ bitwise_buildings.missile_towers[cooldown_tier] |= bitfield;
+ }
+ }
+ }
+ else if building_type == command::BuildingType::Tesla {
+ bitwise_buildings.tesla_cooldowns.push(bitwise_engine::TeslaCooldown {
+ pos: point,
+ cooldown: building.weapon_cooldown_time_left,
+ age: building.construction_time_left.abs() as u16
+ });
+ }
+ }
+ }
+ for missile in &cell.missiles {
+ let (mut left, mut right) = engine::geometry::Point::new_double_bitfield(cell.x, cell.y, missile.player_type == 'A');
+ let mut bitwise_buildings = if missile.player_type == 'A' {
+ &mut player
+ } else {
+ &mut opponent
+ };
+
+ for mut tier in &mut bitwise_buildings.missiles {
+ let setting = (!tier.0 & left, !tier.1 & right);
+ tier.0 |= setting.0;
+ tier.1 |= setting.1;
+ left &= !setting.0;
+ right &= !setting.1;
+ }
+ }
+ }
+ }
+
+ bitwise_engine::BitwiseGameState::new(
+ player, opponent,
+ self.game_details.round
+ )
+ }
+
+ fn player(&self) -> &Player {
+ self.players.iter()
+ .find(|p| p.player_type == 'A')
+ .expect("Player character did not appear in state.json")
+ }
+
+ fn opponent(&self) -> &Player {
+ self.players.iter()
+ .find(|p| p.player_type == 'B')
+ .expect("Opponent character did not appear in state.json")
+ }
+}
+
+impl BuildingState {
+ fn to_bitwise_engine_unconstructed(&self) -> bitwise_engine::UnconstructedBuilding {
+ bitwise_engine::UnconstructedBuilding {
+ pos: engine::geometry::Point::new(self.x, self.y),
+ construction_time_left: self.construction_time_left as u8, // > 0 check already happened
+ building_type: self.convert_building_type()
+ }
+ }
+
+ fn convert_building_type(&self) -> command::BuildingType {
+ match self.building_type.as_ref() {
+ "ATTACK" => command::BuildingType::Attack,
+ "ENERGY" => command::BuildingType::Energy,
+ "TESLA" => command::BuildingType::Tesla,
+ _ => command::BuildingType::Defence,
+ }
+ }
+}
+
+
+impl Player {
+ fn map_onto_engine(&self, engine_player: &mut bitwise_engine::Player) {
+ engine_player.health = self.health;
+ engine_player.energy = self.energy;
+ engine_player.iron_curtain_available = self.iron_curtain_available;
+ engine_player.iron_curtain_remaining = if self.active_iron_curtain_lifetime < 0 {
+ 0
+ } else {
+ self.active_iron_curtain_lifetime as u8
+ };
+ }
+}