From 11c791a59ac60241f253cdfb4e8765039d15edff Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Wed, 9 May 2018 22:51:38 +0200 Subject: Added converting from JSON code to game engine representation --- src/engine/geometry.rs | 3 + src/engine/mod.rs | 64 +++++++++-------- src/json.rs | 185 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 19 +++-- src/state_json.rs | 86 ----------------------- 5 files changed, 228 insertions(+), 129 deletions(-) create mode 100644 src/json.rs delete mode 100644 src/state_json.rs diff --git a/src/engine/geometry.rs b/src/engine/geometry.rs index f2a2522..a946bf9 100644 --- a/src/engine/geometry.rs +++ b/src/engine/geometry.rs @@ -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 { self.x.checked_sub(1).map(|x| Point { x: x, diff --git a/src/engine/mod.rs b/src/engine/mod.rs index d321572..4ea63a3 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -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, - opponent_buildings: Vec, - player_missiles: Vec, - opponent_missiles: Vec +pub struct GameState { + pub status: GameStatus, + pub player: Player, + pub opponent: Player, + pub player_buildings: Vec, + pub opponent_buildings: Vec, + pub player_missiles: Vec, + pub opponent_missiles: Vec } #[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 index 0000000..18e13fb --- /dev/null +++ b/src/json.rs @@ -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> { + 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, + game_map: Vec>, +} + +#[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, + missiles: Vec, + //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 { + 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 { + 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, + } + } +} diff --git a/src/main.rs b/src/main.rs index d964fee..4e18c48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 { - 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) -> Result<(), Box > { +fn write_command(filename: &str, command: Command) -> Result<(), Box > { 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 index 429db6d..0000000 --- a/src/state_json.rs +++ /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> { - 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, - pub game_map: Vec>, -} - -#[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, - pub missiles: Vec, - 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 -} -- cgit v1.2.3