544e5ed7772d8e09d15445ac1b5e724d8fb3e276
[entelect-challenge-tower-defence.git] / src / input / json.rs
1 use std::fs::File;
2 use std::io::prelude::*;
3 use serde_json;
4 use std::error::Error;
5
6 use engine;
7 use engine::command;
8 use engine::bitwise_engine;
9 use engine::constants::*;
10
11 pub fn read_bitwise_state_from_file(filename: &str) -> Result<bitwise_engine::BitwiseGameState, Box<Error>> {
12     let mut file = File::open(filename)?;
13     let mut content = String::new();
14     file.read_to_string(&mut content)?;
15     let state: State = serde_json::from_str(content.as_ref())?;
16
17     let engine_state = state.to_bitwise_engine();
18     Ok(engine_state)
19 }
20
21 #[derive(Deserialize)]
22 #[serde(rename_all = "camelCase")]
23 struct State {
24     game_details: GameDetails,
25     players: Vec<Player>,
26     game_map: Vec<Vec<GameCell>>,
27 }
28
29 #[derive(Deserialize)]
30 #[serde(rename_all = "camelCase")]
31 struct GameDetails {
32     round: u16
33 }
34
35 #[derive(Deserialize)]
36 #[serde(rename_all = "camelCase")]
37 struct Player {
38     player_type: char,
39     energy: u16,
40     health: u8,
41     iron_curtain_available: bool,
42     active_iron_curtain_lifetime: i16
43 }
44
45 #[derive(Deserialize)]
46 #[serde(rename_all = "camelCase")]
47 struct GameCell {
48     x: u8,
49     y: u8,
50     buildings: Vec<BuildingState>,
51     missiles: Vec<MissileState>,
52 }
53
54 #[derive(Deserialize)]
55 #[serde(rename_all = "camelCase")]
56 struct BuildingState {
57     health: u8,
58     construction_time_left: i16,
59     weapon_cooldown_time_left: u8,
60     building_type: String,
61     x: u8,
62     y: u8,
63     player_type: char
64 }
65
66 #[derive(Deserialize)]
67 #[serde(rename_all = "camelCase")]
68 struct MissileState {
69     player_type: char
70 }
71
72
73 impl State {
74     fn to_bitwise_engine(&self) -> bitwise_engine::BitwiseGameState {
75         let json_player = self.player();
76         let player = json_player.to_bitwise_engine();
77         let json_opponent = self.opponent();
78         let opponent = json_opponent.to_bitwise_engine();
79         let mut player_buildings = bitwise_engine::PlayerBuildings::empty();
80         let mut opponent_buildings = bitwise_engine::PlayerBuildings::empty();
81
82         // TODO roll the player into the playerbuildings and remove this duplication
83         player_buildings.iron_curtain_available = json_player.iron_curtain_available;
84         player_buildings.iron_curtain_remaining = if json_player.active_iron_curtain_lifetime < 0 {
85             0
86         } else {
87             json_player.active_iron_curtain_lifetime as u8
88         };
89         opponent_buildings.iron_curtain_available = json_opponent.iron_curtain_available;
90         opponent_buildings.iron_curtain_remaining = if json_opponent.active_iron_curtain_lifetime < 0 {
91             0
92         } else {
93             json_opponent.active_iron_curtain_lifetime as u8
94         };
95
96         
97         for row in &self.game_map {
98             for cell in row {
99                 let point = engine::geometry::Point::new(cell.x, cell.y);
100                 for building in &cell.buildings {
101                     let building_type = building.convert_building_type();
102                     
103                     let mut bitwise_buildings = if building.player_type == 'A' {
104                         &mut player_buildings
105                     } else {
106                         &mut opponent_buildings
107                     };
108                     let bitfield = point.to_either_bitfield();
109
110                     bitwise_buildings.occupied |= bitfield;
111                     if building.construction_time_left >= 0 {
112                         bitwise_buildings.unconstructed.push(building.to_bitwise_engine_unconstructed());
113                     } else {
114                         for health_tier in 0..DEFENCE_HEALTH {
115                             if building.health > health_tier as u8 * MISSILE_DAMAGE {
116                                 bitwise_buildings.buildings[health_tier] |= bitfield;
117                             }
118                         }
119                         if building_type == command::BuildingType::Energy {
120                             bitwise_buildings.energy_towers |= bitfield;
121                         }
122                         else if building_type == command::BuildingType::Attack {
123                             for cooldown_tier in 0..MISSILE_COOLDOWN + 1 {
124                                 if building.weapon_cooldown_time_left == cooldown_tier as u8 {
125                                     bitwise_buildings.missile_towers[cooldown_tier] |= bitfield;
126                                 }
127                             }
128                         }
129                         else if building_type == command::BuildingType::Tesla {
130                             bitwise_buildings.tesla_cooldowns.push(bitwise_engine::TeslaCooldown { 
131                                 pos: point,
132                                 cooldown: building.weapon_cooldown_time_left,
133                                 age: building.construction_time_left.abs() as u16
134                             });
135                         }
136                     }
137                 }
138                 for missile in &cell.missiles {
139                     let (mut left, mut right) = engine::geometry::Point::new_double_bitfield(cell.x, cell.y, missile.player_type == 'A');
140                     let mut bitwise_buildings = if missile.player_type == 'A' {
141                         &mut player_buildings
142                     } else {
143                         &mut opponent_buildings
144                     };
145
146                     for mut tier in bitwise_buildings.missiles.iter_mut() {
147                         let setting = (!tier.0 & left, !tier.1 & right);
148                         tier.0 |= setting.0;
149                         tier.1 |= setting.1;
150                         left &= !setting.0;
151                         right &= !setting.1;
152                     }
153                 }
154             }
155         }
156             
157         bitwise_engine::BitwiseGameState::new(
158             player, opponent,
159             player_buildings, opponent_buildings,
160             self.game_details.round
161         )
162     }
163
164     fn player(&self) -> &Player {
165         self.players.iter()
166             .find(|p| p.player_type == 'A')
167             .expect("Player character did not appear in state.json")
168     }
169
170     fn opponent(&self) -> &Player {
171         self.players.iter()
172             .find(|p| p.player_type == 'B')
173             .expect("Opponent character did not appear in state.json")
174     }
175 }
176
177 impl Player {
178     fn to_bitwise_engine(&self) -> engine::bitwise_engine::Player {
179         engine::bitwise_engine::Player {
180             energy: self.energy,
181             health: self.health
182         }
183     }
184 }
185
186 impl BuildingState {
187     fn to_bitwise_engine_unconstructed(&self) -> bitwise_engine::UnconstructedBuilding {
188         bitwise_engine::UnconstructedBuilding {
189             pos: engine::geometry::Point::new(self.x, self.y),
190             construction_time_left: self.construction_time_left as u8, // > 0 check already happened
191             building_type: self.convert_building_type()
192         }
193     }
194
195     fn convert_building_type(&self) -> command::BuildingType {
196         match self.building_type.as_ref() {
197             "ATTACK" => command::BuildingType::Attack,
198             "ENERGY" => command::BuildingType::Energy,
199             "TESLA" => command::BuildingType::Tesla,
200             _ => command::BuildingType::Defence,
201         }
202     }
203 }