Added converting from JSON code to game engine representation
authorJustin Worthe <justin@worthe-it.co.za>
Wed, 9 May 2018 20:51:38 +0000 (22:51 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Wed, 9 May 2018 20:51:38 +0000 (22:51 +0200)
src/engine/geometry.rs
src/engine/mod.rs
src/json.rs [new file with mode: 0644]
src/main.rs
src/state_json.rs [deleted file]

index f2a2522..a946bf9 100644 (file)
@@ -5,6 +5,9 @@ pub struct Point {
 }
 
 impl Point {
+    pub fn new(x: u8, y: u8) -> Point {
+        Point { x, y }
+    }
     pub fn move_left(&self) -> Option<Point> {
         self.x.checked_sub(1).map(|x| Point {
             x: x,
index d321572..4ea63a3 100644 (file)
@@ -10,14 +10,14 @@ use std::ops::Fn;
 use std::cmp;
 
 #[derive(Debug, Clone)]
-struct GameState {
-    status: GameStatus,
-    player: Player,
-    opponent: Player,
-    player_buildings: Vec<Building>,
-    opponent_buildings: Vec<Building>,
-    player_missiles: Vec<Missile>,
-    opponent_missiles: Vec<Missile>
+pub struct GameState {
+    pub status: GameStatus,
+    pub player: Player,
+    pub opponent: Player,
+    pub player_buildings: Vec<Building>,
+    pub opponent_buildings: Vec<Building>,
+    pub player_missiles: Vec<Missile>,
+    pub opponent_missiles: Vec<Missile>
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -37,20 +37,20 @@ impl GameStatus {
 
 #[derive(Debug, Clone)]
 pub struct Player {
-    energy: u16,
-    health: u16
+    pub energy: u16,
+    pub health: u16
 }
 
 #[derive(Debug, Clone)]
-struct Building {
-    pos: Point,
-    health: u16,
-    construction_time_left: u8,
-    weapon_damage: u16,
-    weapon_speed: u8,
-    weapon_cooldown_time_left: u8,
-    weapon_cooldown_period: u8,
-    energy_generated_per_turn: u16
+pub struct Building {
+    pub pos: Point,
+    pub health: u16,
+    pub construction_time_left: u8,
+    pub weapon_damage: u16,
+    pub weapon_speed: u8,
+    pub weapon_cooldown_time_left: u8,
+    pub weapon_cooldown_period: u8,
+    pub energy_generated_per_turn: u16
 }
 
 impl Building {
@@ -87,7 +87,6 @@ impl Building {
                 energy_generated_per_turn: 3
             }
         }
-        
     }
 
     fn is_constructed(&self) -> bool {
@@ -95,21 +94,15 @@ impl Building {
     }
 
     fn is_shooty(&self) -> bool {
-        self.is_constructed() && self.weapon_damage >= 0
+        self.is_constructed() && self.weapon_damage > 0
     }
 }
 
 #[derive(Debug, Clone)]
-struct Missile {
-    pos: Point,
-    damage: u16,
-    speed: u8,
-}
-
-impl Missile {
-    fn is_stopped(&self) -> bool {
-        self.speed == 0
-    }
+pub struct Missile {
+    pub pos: Point,
+    pub damage: u16,
+    pub speed: u8,
 }
 
 impl GameState {
@@ -119,8 +112,13 @@ impl GameState {
         }
         
         let mut state = self.clone();
-        GameState::perform_command(&mut state.player_buildings, player_command, &settings.size);
-        GameState::perform_command(&mut state.opponent_buildings, opponent_command, &settings.size);
+        let player_valid = GameState::perform_command(&mut state.player_buildings, player_command, &settings.size);
+        let opponent_valid = GameState::perform_command(&mut state.opponent_buildings, opponent_command, &settings.size);
+
+        if !player_valid || !opponent_valid {
+            state.status = GameStatus::InvalidMove;
+            return state;
+        }
 
         GameState::update_construction(&mut state.player_buildings);
         GameState::update_construction(&mut state.opponent_buildings);
diff --git a/src/json.rs b/src/json.rs
new file mode 100644 (file)
index 0000000..18e13fb
--- /dev/null
@@ -0,0 +1,185 @@
+use std::fs::File;
+use std::io::prelude::*;
+use serde_json;
+use std::error::Error;
+use std::cmp;
+
+use ::engine;
+
+
+pub fn read_state_from_file(filename: &str) -> Result<(engine::settings::GameSettings, engine::GameState), 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())?;
+    Ok((state.to_engine_settings(), state.to_engine()))
+}
+
+#[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: u32,
+    map_width: u8,
+    map_height: u8,
+    building_prices: BuildingPrices
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct BuildingPrices {
+    energy: u16,
+    defense: u16,
+    attack: u16
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Player {
+    player_type: char,
+    energy: u16,
+    health: u16,
+    //hits_taken: u32,
+    //score: u32
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GameCell {
+    //x: u8,
+    //y: u8,
+    buildings: Vec<BuildingState>,
+    missiles: Vec<MissileState>,
+    //cell_owner: char
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct BuildingState {
+    health: u16,
+    construction_time_left: i8,
+    //price: u16,
+    weapon_damage: u16,
+    weapon_speed: u8,
+    weapon_cooldown_time_left: u8,
+    weapon_cooldown_period: u8,
+    //destroy_multiplier: u32,
+    //construction_score: u32,
+    energy_generated_per_turn: u16,
+    //building_type: String,
+    x: u8,
+    y: u8,
+    player_type: char
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MissileState {
+    damage: u16,
+    speed: u8,
+    x: u8,
+    y: u8,
+    player_type: char
+}
+
+
+impl State {
+    fn to_engine_settings(&self) -> engine::settings::GameSettings {
+        engine::settings::GameSettings {
+            size: engine::geometry::Point::new(self.game_details.map_width, self.game_details.map_height),
+            energy_income: 5,
+            energy_price: self.game_details.building_prices.energy,
+            defence_price: self.game_details.building_prices.defense,
+            attack_price: self.game_details.building_prices.attack,
+        }
+    }
+    
+    fn to_engine(&self) -> engine::GameState {
+        engine::GameState {
+            status: engine::GameStatus::Continue,
+            player: self.player().to_engine(),
+            opponent: self.opponent().to_engine(),
+            player_buildings: self.buildings_to_engine('A'),
+            opponent_buildings: self.buildings_to_engine('B'),
+            player_missiles: self.missiles_to_engine('A'),
+            opponent_missiles: self.missiles_to_engine('B'),
+        }
+    }
+
+    fn player(&self) -> &Player {
+        self.players.iter()
+            .filter(|p| p.player_type == 'A')
+            .next()
+            .expect("Player character did not appear in state.json")
+    }
+
+    fn opponent(&self) -> &Player {
+        self.players.iter()
+            .filter(|p| p.player_type != 'B')
+            .next()
+            .expect("Opponent character did not appear in state.json")
+    }
+
+    fn buildings_to_engine(&self, player_type: char) -> Vec<engine::Building> {
+        self.game_map.iter()
+            .flat_map(|row| row.iter()
+                      .flat_map(|cell| cell.buildings.iter()
+                                .filter(|b| b.player_type == player_type)
+                                .map(|b| b.to_engine())
+                      )
+            )
+            .collect()
+    }
+
+    fn missiles_to_engine(&self, player_type: char) -> Vec<engine::Missile> {
+        self.game_map.iter()
+            .flat_map(|row| row.iter()
+                      .flat_map(|cell| cell.missiles.iter()
+                                .filter(|b| b.player_type == player_type)
+                                .map(|b| b.to_engine())
+                      )
+            )
+            .collect()
+    }
+}
+
+impl Player {
+    fn to_engine(&self) -> engine::Player {
+        engine::Player {
+            energy: self.energy,
+            health: self.health,
+        }
+    }
+}
+
+impl BuildingState {
+    fn to_engine(&self) -> engine::Building {
+        engine::Building {
+            pos: engine::geometry::Point::new(self.x, self.y),
+            health: self.health,
+            construction_time_left: cmp::max(0, self.construction_time_left) as u8,
+            weapon_damage: self.weapon_damage,
+            weapon_speed: self.weapon_speed,
+            weapon_cooldown_time_left: self.weapon_cooldown_time_left,
+            weapon_cooldown_period: self.weapon_cooldown_period,
+            energy_generated_per_turn: self.energy_generated_per_turn,
+        }
+    }
+}
+
+impl MissileState {
+    fn to_engine(&self) -> engine::Missile {
+        engine::Missile {
+            pos: engine::geometry::Point::new(self.x, self.y),
+            damage: self.damage,
+            speed: self.speed,
+        }
+    }
+}
index d964fee..4e18c48 100644 (file)
@@ -14,34 +14,33 @@ use std::fs::File;
 use std::io::prelude::*;
 use std::process;
 
-mod state_json;
+mod json;
 mod engine;
 use engine::command::Command;
 
-fn choose_move(_state: &state_json::State) -> Option<Command> {
-    None
+fn choose_move(settings: &engine::settings::GameSettings, state: &engine::GameState) -> Command {
+    state.simulate(&settings, Command::Nothing, Command::Nothing);
+    Command::Nothing
 }
 
 
-fn write_command(filename: &str, command: Option<Command>) -> Result<(), Box<Error> > {
+fn write_command(filename: &str, command: Command) -> Result<(), Box<Error> > {
     let mut file = File::create(filename)?;
-    if let Some(command) = command {
-        write!(file, "{}", command)?;
-    }
+    write!(file, "{}", command)?;
 
     Ok(())
 }
 
 
 fn main() {
-    let state = match state_json::read_state_from_file(STATE_PATH) {
-        Ok(state) => state,
+    let (settings, state) = match json::read_state_from_file(STATE_PATH) {
+        Ok(ok) => ok,
         Err(error) => {
             eprintln!("Failed to read the {} file. {}", STATE_PATH, error);
             process::exit(1);
         }
     };
-    let command = choose_move(&state);
+    let command = choose_move(&settings, &state);
 
     match write_command(COMMAND_PATH, command) {
         Ok(()) => {}
diff --git a/src/state_json.rs b/src/state_json.rs
deleted file mode 100644 (file)
index 429db6d..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-use std::fs::File;
-use std::io::prelude::*;
-use serde_json;
-use std::error::Error;
-
-pub fn read_state_from_file(filename: &str) -> Result<State, Box<Error>> {
-    let mut file = File::open(filename)?;
-    let mut content = String::new();
-    file.read_to_string(&mut content)?;
-    let state = serde_json::from_str(content.as_ref())?;
-    Ok(state)
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct State {
-    pub game_details: GameDetails,
-    pub players: Vec<Player>,
-    pub game_map: Vec<Vec<GameCell>>,
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct GameDetails {
-    pub round: u32,
-    pub map_width: u32,
-    pub map_height: u32,
-    pub building_prices: BuildingPrices
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
-pub struct BuildingPrices {
-    pub energy: u32,
-    pub defense: u32,
-    pub attack: u32
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct Player {
-    pub player_type: char,
-    pub energy: u32,
-    pub health: u32,
-    pub hits_taken: u32,
-    pub score: u32
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct GameCell {
-    pub x: u32,
-    pub y: u32,
-    pub buildings: Vec<BuildingState>,
-    pub missiles: Vec<MissileState>,
-    pub cell_owner: char
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct BuildingState {
-    pub health: u32,
-    pub construction_time_left: i32,
-    pub price: u32,
-    pub weapon_damage: u32,
-    pub weapon_speed: u32,
-    pub weapon_cooldown_time_left: u32,
-    pub weapon_cooldown_period: u32,
-    pub destroy_multiplier: u32,
-    pub construction_score: u32,
-    pub energy_generated_per_turn: u32,
-    pub building_type: String,
-    pub x: u32,
-    pub y: u32,
-    pub player_type: char
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct MissileState {
-    pub damage: u32,
-    pub speed: u32,
-    pub x: u32,
-    pub y: u32,
-    pub player_type: char
-}