Updated point representation to better match my heavy bitfield usage
[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     building_type: String,
52     x: u8,
53     y: u8,
54     player_type: char
55 }
56
57 #[derive(Deserialize)]
58 #[serde(rename_all = "camelCase")]
59 struct MissileState {
60     player_type: char
61 }
62
63
64 impl State {
65     fn to_bitwise_engine(&self) -> bitwise_engine::BitwiseGameState {
66         let player = self.player().to_bitwise_engine();
67         let opponent = self.opponent().to_bitwise_engine();
68         let mut player_buildings = bitwise_engine::PlayerBuildings::empty();
69         let mut opponent_buildings = bitwise_engine::PlayerBuildings::empty();
70         for row in &self.game_map {
71             for cell in row {
72                 let point = engine::geometry::Point::new(cell.x, cell.y);
73                 for building in &cell.buildings {
74                     let building_type = building.convert_building_type();
75                     
76                     let mut bitwise_buildings = if building.player_type == 'A' {
77                         &mut player_buildings
78                     } else {
79                         &mut opponent_buildings
80                     };
81                     let bitfield = point.to_either_bitfield();
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                         }
95                         else if building_type == command::BuildingType::Attack {
96                             for cooldown_tier in 0..MISSILE_COOLDOWN + 1 {
97                                 if building.weapon_cooldown_time_left == cooldown_tier as u8 {
98                                     bitwise_buildings.missile_towers[cooldown_tier] |= bitfield;
99                                 }
100                             }
101                         }
102                         else if building_type == command::BuildingType::Tesla {
103                             bitwise_buildings.tesla_cooldowns.push(bitwise_engine::TeslaCooldown { 
104                                 pos: point,
105                                 cooldown: building.weapon_cooldown_time_left,
106                                 age: building.construction_time_left.abs() as u16
107                             });
108                         }
109                     }
110                 }
111                 for missile in &cell.missiles {
112                     let (mut left, mut right) = engine::geometry::Point::new_double_bitfield(cell.x, cell.y, missile.player_type == 'A');
113                     let mut bitwise_buildings = if missile.player_type == 'A' {
114                         &mut player_buildings
115                     } else {
116                         &mut opponent_buildings
117                     };
118
119                     for mut tier in bitwise_buildings.missiles.iter_mut() {
120                         let setting = (!tier.0 & left, !tier.1 & right);
121                         tier.0 |= setting.0;
122                         tier.1 |= setting.1;
123                         left &= !setting.0;
124                         right &= !setting.1;
125                     }
126                 }
127             }
128         }
129             
130         bitwise_engine::BitwiseGameState::new(
131             player, opponent,
132             player_buildings, opponent_buildings
133         )
134     }
135
136     fn player(&self) -> &Player {
137         self.players.iter()
138             .find(|p| p.player_type == 'A')
139             .expect("Player character did not appear in state.json")
140     }
141
142     fn opponent(&self) -> &Player {
143         self.players.iter()
144             .find(|p| p.player_type == 'B')
145             .expect("Opponent character did not appear in state.json")
146     }
147 }
148
149 impl Player {
150     fn to_bitwise_engine(&self) -> engine::bitwise_engine::Player {
151         engine::bitwise_engine::Player {
152             energy: self.energy,
153             health: self.health
154         }
155     }
156 }
157
158 impl BuildingState {
159     fn to_bitwise_engine_unconstructed(&self) -> bitwise_engine::UnconstructedBuilding {
160         bitwise_engine::UnconstructedBuilding {
161             pos: engine::geometry::Point::new(self.x, self.y),
162             construction_time_left: self.construction_time_left as u8, // > 0 check already happened
163             building_type: self.convert_building_type()
164         }
165     }
166
167     fn convert_building_type(&self) -> command::BuildingType {
168         match self.building_type.as_ref() {
169             "ATTACK" => command::BuildingType::Attack,
170             "ENERGY" => command::BuildingType::Energy,
171             "TESLA" => command::BuildingType::Tesla,
172             _ => command::BuildingType::Defence,
173         }
174     }
175 }