Split to library. Reimplemented sample strategy in new state.
authorJustin Worthe <justin@worthe-it.co.za>
Thu, 10 May 2018 21:01:22 +0000 (23:01 +0200)
committerJustin Worthe <justin@worthe-it.co.za>
Thu, 10 May 2018 21:01:22 +0000 (23:01 +0200)
src/engine/command.rs
src/engine/mod.rs
src/lib.rs [new file with mode: 0644]
src/main.rs
src/strategy/mod.rs [new file with mode: 0644]
src/strategy/sample.rs [new file with mode: 0644]

index 603ee42..eab98c1 100644 (file)
@@ -9,11 +9,9 @@ pub enum Command {
 
 impl fmt::Display for Command {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use Command::*;
-        
         match self {
-            &Nothing => write!(f, ""),
-            &Build(p, b) => write!(f, "{},{},{}", p.x, p.y, b as u8),
+            &Command::Nothing => write!(f, ""),
+            &Command::Build(p, b) => write!(f, "{},{},{}", p.x, p.y, b as u8),
         }
     }
 }
index 4ea63a3..be95c03 100644 (file)
@@ -29,12 +29,6 @@ pub enum GameStatus {
     InvalidMove
 }
 
-impl GameStatus {
-    fn is_complete(&self) -> bool {
-        *self != GameStatus::Continue
-    }
-}
-
 #[derive(Debug, Clone)]
 pub struct Player {
     pub energy: u16,
@@ -53,51 +47,6 @@ pub struct Building {
     pub energy_generated_per_turn: u16
 }
 
-impl Building {
-    fn new(pos: Point, building: BuildingType) -> Building {
-        match building {
-            BuildingType::Defense => Building {
-                pos: pos,
-                health: 20,
-                construction_time_left: 3,
-                weapon_damage: 0,
-                weapon_speed: 0,
-                weapon_cooldown_time_left: 0,
-                weapon_cooldown_period: 0,
-                energy_generated_per_turn: 0
-            },
-            BuildingType::Attack => Building {
-                pos: pos,
-                health: 5,
-                construction_time_left: 1,
-                weapon_damage: 5,
-                weapon_speed: 1,
-                weapon_cooldown_time_left: 0,
-                weapon_cooldown_period: 3,
-                energy_generated_per_turn: 0
-            },
-            BuildingType::Energy => Building {
-                pos: pos,
-                health: 5,
-                construction_time_left: 1,
-                weapon_damage: 0,
-                weapon_speed: 0,
-                weapon_cooldown_time_left: 0,
-                weapon_cooldown_period: 0,
-                energy_generated_per_turn: 3
-            }
-        }
-    }
-
-    fn is_constructed(&self) -> bool {
-        self.construction_time_left == 0
-    }
-
-    fn is_shooty(&self) -> bool {
-        self.is_constructed() && self.weapon_damage > 0
-    }
-}
-
 #[derive(Debug, Clone)]
 pub struct Missile {
     pub pos: Point,
@@ -215,4 +164,100 @@ impl GameState {
             (false, false) => GameStatus::Continue,
         };
     }
+
+    pub fn unoccupied_player_cells_in_row(&self, settings: &GameSettings, y: u8) -> Vec<Point> {
+        (0..settings.size.x/2)
+            .map(|x| Point::new(x, y))
+            .filter(|&p| !self.player_buildings.iter().any(|b| b.pos == p))
+            .collect()
+    }
+
+    pub fn unoccupied_player_cells(&self, settings: &GameSettings) -> Vec<Point> {
+        (0..settings.size.y)
+            .flat_map(|y| (0..settings.size.x/2).map(|x| Point::new(x, y)).collect::<Vec<_>>())
+            .filter(|&p| !self.player_buildings.iter().any(|b| b.pos == p))
+            .collect()
+    }
+}
+
+impl GameStatus {
+    fn is_complete(&self) -> bool {
+        *self != GameStatus::Continue
+    }
+}
+
+impl Player {
+    pub fn can_afford_all_buildings(&self, settings: &GameSettings) -> bool {
+        self.can_afford_attack_buildings(settings) &&
+            self.can_afford_defence_buildings(settings) &&
+            self.can_afford_energy_buildings(settings)
+    }
+
+    pub fn can_afford_attack_buildings(&self, settings: &GameSettings) -> bool {
+        self.energy >= settings.attack_price
+    }
+    pub fn can_afford_defence_buildings(&self, settings: &GameSettings) -> bool {
+        self.energy >= settings.defence_price
+    }
+    pub fn can_afford_energy_buildings(&self, settings: &GameSettings) -> bool {
+        self.energy >= settings.energy_price
+    }
+
+}
+
+impl Building {
+    fn new(pos: Point, building: BuildingType) -> Building {
+        match building {
+            BuildingType::Defense => Building {
+                pos: pos,
+                health: 20,
+                construction_time_left: 3,
+                weapon_damage: 0,
+                weapon_speed: 0,
+                weapon_cooldown_time_left: 0,
+                weapon_cooldown_period: 0,
+                energy_generated_per_turn: 0
+            },
+            BuildingType::Attack => Building {
+                pos: pos,
+                health: 5,
+                construction_time_left: 1,
+                weapon_damage: 5,
+                weapon_speed: 1,
+                weapon_cooldown_time_left: 0,
+                weapon_cooldown_period: 3,
+                energy_generated_per_turn: 0
+            },
+            BuildingType::Energy => Building {
+                pos: pos,
+                health: 5,
+                construction_time_left: 1,
+                weapon_damage: 0,
+                weapon_speed: 0,
+                weapon_cooldown_time_left: 0,
+                weapon_cooldown_period: 0,
+                energy_generated_per_turn: 3
+            }
+        }
+    }
+
+    fn is_constructed(&self) -> bool {
+        self.construction_time_left == 0
+    }
+
+    fn is_shooty(&self) -> bool {
+        self.is_constructed() && self.weapon_damage > 0
+    }
+}
+
+#[test]
+fn how_big() {
+    use std::mem;
+    assert_eq!(4, mem::size_of::<Player>());
+    assert_eq!(12, mem::size_of::<Building>());
+    assert_eq!(6, mem::size_of::<Missile>());
+    assert_eq!(112, mem::size_of::<GameState>());
+    assert_eq!(24, mem::size_of::<Vec<Building>>());
+    assert_eq!(24, mem::size_of::<Vec<Missile>>());
+    
 }
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..158805a
--- /dev/null
@@ -0,0 +1,9 @@
+extern crate serde;
+extern crate serde_json;
+
+#[macro_use]
+extern crate serde_derive;
+
+pub mod json;
+pub mod engine;
+pub mod strategy;
index 4e18c48..22f698d 100644 (file)
@@ -1,8 +1,6 @@
-extern crate serde;
-extern crate serde_json;
-
-#[macro_use]
-extern crate serde_derive;
+extern crate zombot;
+use zombot::*;
+use zombot::engine::command::Command;
 
 use std::error::Error;
 
@@ -14,20 +12,14 @@ use std::fs::File;
 use std::io::prelude::*;
 use std::process;
 
-mod json;
-mod engine;
-use engine::command::Command;
-
 fn choose_move(settings: &engine::settings::GameSettings, state: &engine::GameState) -> Command {
-    state.simulate(&settings, Command::Nothing, Command::Nothing);
-    Command::Nothing
+    strategy::sample::choose_move(settings, state)
 }
 
 
 fn write_command(filename: &str, command: Command) -> Result<(), Box<Error> > {
     let mut file = File::create(filename)?;
     write!(file, "{}", command)?;
-
     Ok(())
 }
 
@@ -36,7 +28,6 @@ fn main() {
     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);
         }
     };
@@ -45,7 +36,6 @@ fn main() {
     match write_command(COMMAND_PATH, command) {
         Ok(()) => {}
         Err(error) => {
-            eprintln!("Failed to write the {} file. {}", COMMAND_PATH, error);
             process::exit(1);
         }
     }
diff --git a/src/strategy/mod.rs b/src/strategy/mod.rs
new file mode 100644 (file)
index 0000000..ce8e751
--- /dev/null
@@ -0,0 +1 @@
+pub mod sample;
diff --git a/src/strategy/sample.rs b/src/strategy/sample.rs
new file mode 100644 (file)
index 0000000..bd23916
--- /dev/null
@@ -0,0 +1,38 @@
+use engine;
+use engine::command::*;
+
+
+pub fn choose_move(settings: &engine::settings::GameSettings, state: &engine::GameState) -> Command {
+    if state.player.can_afford_defence_buildings(settings) {
+        for y in 0..settings.size.y {
+            if is_under_attack(state, y) {
+                let p_options = state.unoccupied_player_cells_in_row(settings, y);
+                if let Some(&p) = p_options.first() {
+                    return Command::Build(p, BuildingType::Defense);
+                }
+            }
+        }
+    }
+
+    if state.player.can_afford_all_buildings(settings) {
+        let options = state.unoccupied_player_cells(settings);
+        let option = options.first();
+        let buildings = [BuildingType::Attack, BuildingType::Defense, BuildingType::Energy];
+        let building = buildings.first();
+        match (option, building) {
+            (Some(&p), Some(&building)) => Command::Build(p, building),
+            _ => Command::Nothing
+        }
+    }
+    else {
+        Command::Nothing
+    }
+}
+
+fn is_under_attack(state: &engine::GameState, y: u8) -> bool {
+    let attack = state.opponent_buildings.iter()
+        .any(|b| b.pos.y == y && b.weapon_damage > 0);
+    let defences = state.player_buildings.iter()
+        .any(|b| b.pos.y == y && b.health > 5);
+    attack && !defences
+}