9bc051835d6bbada48c394c71d277e63bb6499c2
[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     players: Vec<Player>,
25     game_map: Vec<Vec<GameCell>>,
26 }
27
28 #[derive(Deserialize)]
29 #[serde(rename_all = "camelCase")]
30 struct Player {
31     player_type: char,
32     energy: u16,
33     health: u8,
34 }
35
36 #[derive(Deserialize)]
37 #[serde(rename_all = "camelCase")]
38 struct GameCell {
39     x: u8,
40     y: u8,
41     buildings: Vec<BuildingState>,
42     missiles: Vec<MissileState>,
43 }
44
45 #[derive(Deserialize)]
46 #[serde(rename_all = "camelCase")]
47 struct BuildingState {
48     health: u8,
49     construction_time_left: i16,
50     weapon_cooldown_time_left: u8,
51     energy_generated_per_turn: u16,
52     building_type: String,
53     x: u8,
54     y: u8,
55     player_type: char
56 }
57
58 #[derive(Deserialize)]
59 #[serde(rename_all = "camelCase")]
60 struct MissileState {
61     player_type: char
62 }
63
64
65 impl State {
66     fn to_bitwise_engine(&self) -> bitwise_engine::BitwiseGameState {
67         let mut player = self.player().to_bitwise_engine();
68         let mut opponent = self.opponent().to_bitwise_engine();
69         let mut player_buildings = bitwise_engine::PlayerBuildings::empty();
70         let mut opponent_buildings = bitwise_engine::PlayerBuildings::empty();
71         for row in &self.game_map {
72             for cell in row {
73                 let point = engine::geometry::Point::new(cell.x, cell.y);
74                 for building in &cell.buildings {
75                     let building_type = building.convert_building_type();
76                     
77                     let (mut engine_player, mut bitwise_buildings, bitfield) = if building.player_type == 'A' {
78                         (&mut player, &mut player_buildings, point.to_left_bitfield())
79                     } else {
80                         (&mut opponent, &mut opponent_buildings, point.to_right_bitfield())
81                     };
82
83                     bitwise_buildings.occupied |= bitfield;
84                     if building.construction_time_left >= 0 {
85                         bitwise_buildings.unconstructed.push(building.to_bitwise_engine_unconstructed());
86                     } else {
87                         for health_tier in 0..DEFENCE_HEALTH {
88                             if building.health > health_tier as u8 * MISSILE_DAMAGE {
89                                 bitwise_buildings.buildings[health_tier] |= bitfield;
90                             }
91                         }
92                         if building_type == command::BuildingType::Energy {
93                             bitwise_buildings.energy_towers |= bitfield;
94                             engine_player.energy_generated += building.energy_generated_per_turn;
95                         }
96                         else if building_type == command::BuildingType::Attack {
97                             for cooldown_tier in 0..MISSILE_COOLDOWN + 1 {
98                                 if building.weapon_cooldown_time_left == cooldown_tier as u8 {
99                                     bitwise_buildings.missile_towers[cooldown_tier] |= bitfield;
100                                 }
101                             }
102                         }
103                         else if building_type == command::BuildingType::Tesla {
104                             let ref mut tesla_cooldown = if bitwise_buildings.tesla_cooldowns[0].active {
105                                 &mut bitwise_buildings.tesla_cooldowns[1]
106                             } else {
107                                 &mut bitwise_buildings.tesla_cooldowns[0]
108                             };
109                             tesla_cooldown.active = true;
110                             tesla_cooldown.pos = point;
111                             tesla_cooldown.cooldown = building.weapon_cooldown_time_left;
112                             tesla_cooldown.age = building.construction_time_left.abs() as u16;
113                         }
114                     }
115                 }
116                 for missile in &cell.missiles {
117                     let bitfields = point.to_bitfield();
118                     let (mut bitwise_buildings, mut left, mut right) = if missile.player_type == 'A' {
119                         (&mut player_buildings, bitfields.0, bitfields.1)
120                     } else {
121                         (&mut opponent_buildings, bitfields.1, bitfields.0)
122                     };
123
124                     for mut tier in bitwise_buildings.missiles.iter_mut() {
125                         let setting = (!tier.0 & left, !tier.1 & right);
126                         tier.0 |= setting.0;
127                         tier.1 |= setting.1;
128                         left &= !setting.0;
129                         right &= !setting.1;
130                     }
131                 }
132             }
133         }
134             
135         bitwise_engine::BitwiseGameState::new(
136             player, opponent,
137             player_buildings, opponent_buildings
138         )
139     }
140
141     fn player(&self) -> &Player {
142         self.players.iter()
143             .find(|p| p.player_type == 'A')
144             .expect("Player character did not appear in state.json")
145     }
146
147     fn opponent(&self) -> &Player {
148         self.players.iter()
149             .find(|p| p.player_type == 'B')
150             .expect("Opponent character did not appear in state.json")
151     }
152 }
153
154 impl Player {
155     fn to_bitwise_engine(&self) -> engine::bitwise_engine::Player {
156         engine::bitwise_engine::Player {
157             energy: self.energy,
158             health: self.health,
159             energy_generated: ENERGY_GENERATED_BASE
160         }
161     }
162 }
163
164 impl BuildingState {
165     fn to_bitwise_engine_unconstructed(&self) -> bitwise_engine::UnconstructedBuilding {
166         bitwise_engine::UnconstructedBuilding {
167             pos: engine::geometry::Point::new(self.x, self.y),
168             construction_time_left: self.construction_time_left as u8, // > 0 check already happened
169             building_type: self.convert_building_type()
170         }
171     }
172
173     fn convert_building_type(&self) -> command::BuildingType {
174         match self.building_type.as_ref() {
175             "ATTACK" => command::BuildingType::Attack,
176             "ENERGY" => command::BuildingType::Energy,
177             "TESLA" => command::BuildingType::Tesla,
178             _ => command::BuildingType::Defence,
179         }
180     }
181 }