sc2 0.3.3

Rust implementation of the StarCraft II Client API
#![feature(proc_macro, conservative_impl_trait, generators)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;

extern crate docopt;
extern crate futures_await as futures;
extern crate glob;
extern crate nalgebra as na;
extern crate organelle;
extern crate rand;
extern crate serde;
extern crate tokio_core;

extern crate sc2;

use std::path::PathBuf;

use docopt::Docopt;
use futures::prelude::*;
use sc2::{
    AgentControl,
    Error,
    GameEvent,
    Launcher,
    LauncherBuilder,
    MeleeBuilder,
    Player,
    Result,
    UpdateScheme,
};
use sc2::data::{
    DebugCommand,
    DebugText,
    DebugTextTarget,
    Difficulty,
    GameSetup,
    Map,
    PlayerSetup,
    Point2,
    Race,
};
use tokio_core::reactor;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const USAGE: &'static str = "
StarCraft II Rust API Example.

Usage:
  example (-h | --help)
  example [options]
  example --version

Options:
  -h --help                         Show this screen.
  --version                         Show version.
  --wine                            Use Wine to run StarCraft II (for Linux).
  -d <path> --dir=<path>            Path to the StarCraft II installation.
  -p <port> --port=<port>           Port to make StarCraft II listen on.
  -m <name> --map=<name>            Path to the StarCraft II map.
  -r --realtime                     Run StarCraft II in real time
  -s <count> --step-size=<count>    How many steps to take per call.
  --replay-dir=<path>               Path to a replay pack
";

#[derive(Debug, Deserialize)]
pub struct Args {
    pub flag_dir: Option<PathBuf>,
    pub flag_port: Option<u16>,
    pub flag_map: Option<PathBuf>,
    pub flag_replay_dir: Option<PathBuf>,
    pub flag_wine: bool,
    pub flag_version: bool,
    pub flag_realtime: bool,
    pub flag_step_size: Option<u32>,
}

pub fn get_launcher_settings(args: &Args) -> Result<Launcher> {
    let mut builder = LauncherBuilder::new().use_wine(args.flag_wine);

    if let Some(dir) = args.flag_dir.clone() {
        builder = builder.install_dir(dir);
    }

    if let Some(port) = args.flag_port {
        builder = builder.base_port(port);
    }

    Ok(builder.create()?)
}

pub fn get_game_setup(args: &Args) -> Result<GameSetup> {
    let map = match args.flag_map {
        Some(ref map) => Map::LocalMap(map.clone()),
        None => bail!("no map specified"),
    };

    Ok(GameSetup::new(map))
}

struct DebugBot {
    control: AgentControl,
}

impl Player for DebugBot {
    type Error = Error;

    #[async(boxed)]
    fn get_player_setup(self, _: GameSetup) -> Result<(Self, PlayerSetup)> {
        Ok((self, PlayerSetup::Player(Race::Terran)))
    }

    #[async(boxed)]
    fn on_event(self, e: GameEvent) -> Result<Self> {
        match e {
            GameEvent::Step => await!(self.on_step()),
            _ => Ok(self),
        }
    }
}

impl DebugBot {
    fn new(control: AgentControl) -> Self {
        Self { control: control }
    }

    #[async]
    fn on_step(self) -> Result<Self> {
        let observation = await!(self.control.observer().observe())?;
        let unit_type_data = await!(self.control.observer().get_unit_data())?;

        await!(self.control.action().send_debug(
            DebugText::new("in the corner".to_string()).color((0xFF, 0, 0)),
        ))?;
        await!(
            self.control.action().send_debug(
                DebugText::new("screen pos".to_string())
                    .target(DebugTextTarget::Screen(Point2::new(1.0, 1.0)))
                    .color((0, 0xFF, 0))
            )
        )?;

        let mut commands: Vec<DebugCommand> = observation
            .get_units()
            .iter()
            .map(|u| {
                DebugText::new(
                    unit_type_data[&u.get_unit_type()].get_name().into(),
                ).target(DebugTextTarget::World(u.get_pos()))
                    .color((0xFF, 0xFF, 0xFF))
                    .into()
            })
            .collect();

        for cmd in commands {
            await!(self.control.action().send_debug(cmd))?;
        }

        Ok(self)
    }
}

quick_main!(|| -> sc2::Result<()> {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("bot-micro version {}", VERSION);
        return Ok(());
    }

    let mut core = reactor::Core::new().unwrap();
    let handle = core.handle();

    let melee = MeleeBuilder::new(
        sc2::AgentBuilder::factory(|control| DebugBot::new(control))
            .handle(handle.clone())
            .create()?,
        sc2::ComputerBuilder::new()
            .race(Race::Zerg)
            .difficulty(Difficulty::VeryEasy)
            .create()?,
    ).launcher_settings(get_launcher_settings(&args)?)
        .one_and_done(get_game_setup(&args)?)
        .update_scheme(UpdateScheme::Interval(args.flag_step_size.unwrap_or(1)))
        .break_on_ctrlc(args.flag_wine)
        .handle(handle.clone())
        .create()?;

    core.run(melee.into_future())?;

    Ok(())
});