Code Monkey home page Code Monkey logo

rust-sc2's Introduction

Table of Contents

rust-sc2

crates.io Documentation

Rust implementation of StarCraft II API

The library aims to be simple and easy to use, being very fast and functional at the same time. However, it provides both high and low level abstractions. This lib is inspired by python-sc2 lib, so people might find it easy to switch to rust-sc2. It was originally created because other rust libs were old, not functional and low level.

Feel free to ask questions in #rust channel of Starcraft 2 AI Discord server

Getting started

Rust

Install latest stable Rust (Older versions may also work, but compatibility is not guaranteed)

Create your project

cargo new <project_name>

Add to dependencies in Cargo.toml:

[dependencies]
rust-sc2 = "1"

Or if you want developer version directly from github:

[dependencies]
rust-sc2 = { git = "https://github.com/UltraMachine/rust-sc2" }

Or if you want to use a local version:

[dependencies]
rust-sc2 = { path = "/path/to/rust-sc2" }

NOTE: Version of this library on crates.io is outdated and lacks many features. Unfortunately, I can't update it yet, so it's highly recommended to use github version for now.

StarCraft II

Installation

Windows and macOS

Install SC2 through Battle.net.

Linux

Headfull (Lutris and Wine)
  1. Install Lutris from your package manager
  2. Install Battle.net dependencies. (Wine and Vulkan drivers)
  3. Install SC2 through Lutris
Headless (no graphics)
  1. Download most recent Linux Package (Maps will come with the zip)
  2. Unzip to ~/StarCraftII (you'll need the End User License Agreement Password above the Linux Packages link)
  3. Move your .SC2Map files up out of their LadderXXXXSeasonX directory to Maps directory. (Since there are multiple versions of the same map, there is no way of knowing which one you want.)

Example

The simplest competetive bot in less than 30 lines. Copy this into your /path/to/project/main.rs

use rust_sc2::prelude::*;

#[bot]
#[derive(Default)]
struct WorkerRush;
impl Player for WorkerRush {
    fn get_player_settings(&self) -> PlayerSettings {
        PlayerSettings::new(Race::Protoss)
    }
    fn on_start(&mut self) -> SC2Result<()> {
        for worker in &self.units.my.workers {
            worker.attack(Target::Pos(self.enemy_start), false);
        }
        Ok(())
    }
}

fn main() -> SC2Result<()> {
    let mut bot = WorkerRush::default();
    run_vs_computer(
        &mut bot,
        Computer::new(Race::Random, Difficulty::Medium, None),
        "EternalEmpireLE",
        Default::default(),
    )
}

Note: The Linux client doesn't have the map EternalEmpireLE so you'll need to download it, or reference another map from the LadderXXXXSeasonX directories.

Running Example

Headfull

  1. export SC2PATH="/home/<user>/Games/starcraft-ii/drive_c/Program Files (x86)/StarCraft II"
  2. Make sure you have this snippet in your project's Cargo.toml:
[features]
wine_sc2 = ["rust-sc2/wine_sc2"]
  1. cargo run --features wine_sc2

Headless

  1. export SC2PATH=/abs/path/to/StarCraftII
  2. cargo run

Running the advanced examples

There are more advanced examples in the examples folder. To run one of these examples on your own machine, say the Reaper Rush one, clone this repository, navigate to the root folder and run the command

cargo run --example reaper-rush -- local

In addition to local (or human if you want to play against your own bot), these examples take several arguments. For a full list in either case, run the commands

cargo run --example reaper-rush -- local --help
cargo run --example reaper-rush -- human --help

Optional features

  • "rayon" - enables parallelism and makes all types threadsafe
  • "serde" - adds implementation of Serialize, Deserialize to ids, Race, GameResult, ...
  • "wine_sc2" - allows you to run headful SC2 through Lutris and Wine

Making bot step by step

First of all, import rust-sc2 lib:

use rust_sc2::prelude::*;

Create your bot's struct (Can be Unit or C-like):

#[bot]
struct MyBot;
#[bot]
struct MyBot {
    /* fields here */
}

Then implement Player trait for your bot:

// You mustn't call any of these methods by hands, they're for API only
impl Player for MyBot {
    // Must be implemented
    fn get_player_settings(&self) -> PlayerSettings {
        // Race can be Terran, Zerg, Protoss or Random
        PlayerSettings::new(Race::Random)
    }

    // Methods below aren't necessary to implement (Empty by default)

    // Called once on first step
    fn on_start(&mut self) -> SC2Result<()> {
        /* your awesome code here */
    }

    // Called on every game step
    fn on_step(&mut self, iteration: usize) -> SC2Result<()> {
        /* your awesome code here */
    }

    // Called once on last step
    // "result" says if your bot won or lost game
    fn on_end(&self, result: GameResult) -> SC2Result<()> {
        /* your awesome code here */
    }

    // Called on different events, see more in `examples/events.rs`
    fn on_event(&mut self, event: Event) -> SC2Result<()> {
        /* your awesome code here */
    }
}

Also you might want to add method to construct it:

impl MyBot {
    // It's necessary to have #[bot_new] here
    #[bot_new]
    fn new() -> Self {
        Self {
            /* initializing fields */
        }
    }
}

If your bot implements Default you can simply call MyBot::default(), but if you want more control over initializer:

impl MyBot {
    // You don't need #[bot_new] here, because of "..Default::default()"
    fn new() -> Self {
        Self {
            /* initializing fields */
            ..Default::default()
        }
    }
}

The rest is to run it:

fn main() -> SC2Result<()> {
    let mut bot = MyBot::new();
    run_vs_computer(
        &mut bot,
        Computer::new(
            Race::Random,
            Difficulty::VeryEasy,
            None, // AI Build (random here)
        ),
        "EternalEmpireLE", // Map name
        LaunchOptions::default(),
    )
}

rust-sc2's People

Contributors

ccapitalk avatar danielvschoor avatar dependabot-preview[bot] avatar dependabot[bot] avatar gbathie avatar kaszanas avatar masterhigure avatar pertsevroman avatar plichard avatar ultramachine avatar whompyjaw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

rust-sc2's Issues

Make library more modular

In current state it has almost monolith architecture, making it hard to replace/remove things you don't like/need

`get_unit_cost` and zerg buildings

The Bot::get_unit_cost method returns uncorrected values for zerg buildings, which include additional 50 minerals (the cost of a drone). For UnitTypeId::SpawningPool the returned value is 250. Since the aforementioned method is used to implement Bot::subtract_resources I'd suggest fixing this behaviour. It's counter-intuitive that Bot::subtract_resources(UnitTypeId::SpawningPool) subtracts 250 minerals, and the real mineral counter and the bot.minerals counter become out of sync for the rest of a step.

Pretty confused on setup and errors

Using Arch Linux with headless SC2 (I do have Wine/Lutris for playing)
started new project with: cargo new project
Cargo.toml

[dependencies]
rust-sc2 = { git = "https://github.com/UltraMachine/rust-sc2" }

cargo run with full verbosity

    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/raith`
thread 'main' panicked at 'Can't find maps folder in: ~/StarCraftII', /home/wj/.cargo/git/checkouts/rust-sc2-459d62a539f28979/5bcde2d/src/paths.rs:56:17
stack backtrace:
   0:     0x558bb6026b6d - std::backtrace_rs::backtrace::libunwind::trace::h8e036432725b1c57
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x558bb6026b6d - std::backtrace_rs::backtrace::trace_unsynchronized::h4f83092254c85869
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x558bb6026b6d - std::sys_common::backtrace::_print_fmt::h9728b5e056a3ece3
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:66:5
   3:     0x558bb6026b6d - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h48bb4bd2928827d2
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:45:22
   4:     0x558bb604982c - core::fmt::write::h909e69a2c24f44cc
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/fmt/mod.rs:1196:17
   5:     0x558bb6022691 - std::io::Write::write_fmt::h7f4b8ab8af89e9ef
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/io/mod.rs:1654:15
   6:     0x558bb6028275 - std::sys_common::backtrace::_print::hff4838ebf14a2171
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:48:5
   7:     0x558bb6028275 - std::sys_common::backtrace::print::h2499280374189ad9
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:35:9
   8:     0x558bb6028275 - std::panicking::default_hook::{{closure}}::h8b270fc55eeb284e
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:295:22
   9:     0x558bb6027ee9 - std::panicking::default_hook::h3217e229d6e9d13c
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:314:9
  10:     0x558bb6028848 - std::panicking::rust_panic_with_hook::h9acb8048b738d2e0
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:698:17
  11:     0x558bb60286f7 - std::panicking::begin_panic_handler::{{closure}}::h70f3b839526af6dc
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:588:13
  12:     0x558bb6027024 - std::sys_common::backtrace::__rust_end_short_backtrace::h1ecf2cee857fbe0a
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:138:18
  13:     0x558bb6028429 - rust_begin_unwind
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:584:5
  14:     0x558bb576ed33 - core::panicking::panic_fmt::h9f8393e7fd56d655
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/panicking.rs:142:14
  15:     0x558bb585cc6a - rust_sc2::paths::get_map_path::h9de852fcbb39ed17
                               at /home/wj/.cargo/git/checkouts/rust-sc2-459d62a539f28979/5bcde2d/src/paths.rs:56:5
  16:     0x558bb577075e - rust_sc2::client::RunnerSingle<B>::new::hdf81fd1867c2cc21
                               at /home/wj/.cargo/git/checkouts/rust-sc2-459d62a539f28979/5bcde2d/src/client.rs:109:18
  17:     0x558bb577032a - rust_sc2::client::run_vs_computer::ha266324bc8269321
                               at /home/wj/.cargo/git/checkouts/rust-sc2-459d62a539f28979/5bcde2d/src/client.rs:411:19
  18:     0x558bb577d79e - raith::main::h3382177a4c641df0
                               at /home/wj/files/projects/raith/src/main.rs:20:5
  19:     0x558bb5775d8b - core::ops::function::FnOnce::call_once::h01542edf3589472f
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/ops/function.rs:248:5
  20:     0x558bb577466e - std::sys_common::backtrace::__rust_begin_short_backtrace::h7c3e42b5e61b5824
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:122:18
  21:     0x558bb5774121 - std::rt::lang_start::{{closure}}::h80c21ea6dca0dd53
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/rt.rs:145:18
  22:     0x558bb601f3ce - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h5f1ba3b861cac230
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/ops/function.rs:280:13
  23:     0x558bb601f3ce - std::panicking::try::do_call::h4febfdb770eca39d
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:492:40
  24:     0x558bb601f3ce - std::panicking::try::h8b0eac8a7d726dbf
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:456:19
  25:     0x558bb601f3ce - std::panic::catch_unwind::h11b83c489c0d8394
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panic.rs:137:14
  26:     0x558bb601f3ce - std::rt::lang_start_internal::{{closure}}::h63502d2988634103
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/rt.rs:128:48
  27:     0x558bb601f3ce - std::panicking::try::do_call::h4be475cff12d2aae
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:492:40
  28:     0x558bb601f3ce - std::panicking::try::h1a3b25b8eaf9ba31
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:456:19
  29:     0x558bb601f3ce - std::panic::catch_unwind::h224588ada67b9b0b
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panic.rs:137:14
  30:     0x558bb601f3ce - std::rt::lang_start_internal::h2807b375c1959759
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/rt.rs:128:20
  31:     0x558bb57740f0 - std::rt::lang_start::h48e0f062a173824f
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/rt.rs:144:17
  32:     0x558bb577d85c - main
  33:     0x7fa3b9a87290 - <unknown>
  34:     0x7fa3b9a8734a - __libc_start_main
  35:     0x558bb576f025 - _start
                               at /build/glibc/src/glibc/csu/../sysdeps/x86_64/start.S:115
  36:                0x0 - <unknown>
ls ~/StarCraftII/
Battle.net  Interfaces  Libs  Maps  Replays  SC2Data  Versions
ls ~/StarCraftII/Maps/
Ladder2017Season1  Ladder2017Season2  Ladder2017Season3  Ladder2017Season4  Ladder2018Season1  Ladder2018Season2  Ladder2018Season3  Ladder2018Season4  Ladder2019Season1  Melee  mini_games
ls ~/StarCraftII/Maps/Ladder2019Season1/
AutomatonLE.SC2Map  CyberForestLE.SC2Map  KairosJunctionLE.SC2Map  KingsCoveLE.SC2Map  NewRepugnancyLE.SC2Map  PortAleksanderLE.SC2Map  YearZeroLE.SC2Map

You can see I have Maps.

This is my first time using rust, so I could be messing something up, but pretty sure I got it setup correctly (followed archwiki, installed rustup, then installed stable rust, clippy, etc)

I was able to run an example from burnysc2 library. The bug in that code was changing ~/StarCraftII/Maps to maps, but trying that with rust, didn't work.

What am I missing here?

BuffId - no id with 307

Hey, I'm experiencing issue with the lib

thread 'main' panicked at <home_dir>\.cargo\git\checkouts\rust-sc2-459d62a539f28979\b9f38a3\src\unit.rs:1968:64: There's no BuffId with value 307

And yes, last available BuffId enum contains 303 entries only

	RavenShredderMissileArmorReductionUISubtruct = 297,
	BatteryOvercharge = 298,
	SunderingImpact = 299,
	AmplifiedShielding = 300,
	PsionicAmplifiers = 301,
	SecretedCoating = 302,
	OnCreepVisible = 303,
}

https://github.com/UltraMachine/rust-sc2/blob/master/src/ids/buff_id.rs#L312

Seems the lib need to be updated to the last API version. @UltraMachine if you could lead me, I'd prepare the hotfix.

race_values seems to be unset

I get the following error when trying to use worker.build_gas():

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', C:\path\rust-sc2\src\unit.rs:1915:18

When running the code through a debugger, I can see that it happens in this code:

 self.command(
            self.data.game_data.units[&self.data.race_values.gas]
                .ability
                .unwrap(),
            Target::Tag(target),
            queue,
        )```
because `self.data.race_values.gas` is set to `UnitTypeId::NotAUnit`. It seems like every single value in `self.data.race_values` is still set to `UnitTypeId::NotAUnit`, even though it is supposed to be set at runtime.

Launching bot vs bot

How do you launch a bot versus bot?
Like I have two bots written in rust and I want them to fight each other.
Best I came across in the examples that are launching two examples of sc2 is bot vs human, yet, for some reason immediately after the game launches the game just hangs.
How would I go about it?

failed to select a version for `rust-sc2`

Stumbled upon this error while trying to launch a bot in linux with headful starcraft:

error: failed to select a version for `rust-sc2`.
    ... required by package `rust_sc2_test v0.1.0 (/home/xeizzeth/DATA/Projects/SC2Emul/rust_sc2_test/rust_sc2_test)`
versions that meet the requirements `^1.1.2` (locked to 1.1.2) are: 1.1.2

the package `rust_sc2_test` depends on `rust-sc2`, with features: `wine_sc2` but `rust-sc2` does not have these features.


failed to select a version for `rust-sc2` which could resolve this conflict

`get_enemy_expansion` returns the enemy starting location

The Bot::get_enemy_expansion method returns the enemy starting location (unless you've revealed the enemy "townhall" before, I suppose). In its current form this method is pretty meaningless. I suggest fixing it like this:

self.units.enemy.townhalls.iter().all(|t| t.is_further(7.0, *loc))
    && self.enemy_start.is_further(7.0, *loc))

Change Ids representation from enums to unsigned integer wrapper

Allows handling unknown ids in non-panicking way, while not breaking existing code, since associated constants can be referenced the same way as enum variants: SomeId::VariantX

Example code:

use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct SomeId(u32);

#[allow(non_upper_case_globals)]
impl SomeId {
	pub const Variant1: Self = Self(1);
	pub const Variant2: Self = Self(2);
	pub const Variant3: Self = Self(3);

	pub fn other(id: u32) -> Self {
		Self(id)
	}
}

impl fmt::Display for SomeId {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			Self::Variant1 => write!(f, "Variant1"),
			Self::Variant2 => write!(f, "Variant2"),
			Self::Variant3 => write!(f, "Variant3"),
			_ => write!(f, "Other({})", self.0),
		}
	}
}

The spam filter in command() is eating Reactor orders

Hi.

I have built a Barracks with a Reactor and want to train two Marines. However, Unit.command() and Unit.train() will not process the second train command, unless it is queued. As far as I can tell, the second command gets filtered out by the APM spam filter, because it is identical to the previous command, and the Barracks is not technically idle (it is_unused).

The code snippet for the spam filter I am talking about looks like this:

pub fn command(&self, ability: AbilityId, target: Target, queue: bool) {
	if !(queue || self.is_idle() || self.data.allow_spam.get_locked()) {
		let last_order = &self.orders()[0];
		if ability == last_order.ability && target == last_order.target {
			return;
		}
	} else if self.is_sleeping() {
		return;
	}
        [...]

I can work around this using the queue flag, but I think a more proper solution in this case would be to change self.is_idle() to self.is_unused() in command(). self.is_unused() falls back to is_idle() when no reactor is present. However, I am not sure if that is indeed the correct way to solve this problem for good.

If you want me to, I can prepare a small pull request with the proposed change.

Missing buffs

From discord discussion the lib is missing those buffs:

	SunderingImpact = 299,
	AmplifiedShielding = 300,
	PsionicAmplifiers = 301,
	SecretedCoating = 302,
	OnCreepVisible = 303,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.