Crate rust_sc2[][src]

Introduction

Installing

Install Rust >= 1.42.0

Warning: Compilation is broken in rustc 1.45.0 - 1.46.0, you'll get following error:

thread 'rustc' has overflowed its stack
error: could not compile `rust-sc2`.

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" }

Making a bot

Making bots with rust-sc2 is pretty easy:

use::rust_sc2::prelude::*;

#[bot]
#[derive(Default)]
struct MyBot;
impl Player for MyBot {
    // This settings are used to connect bot to the game.
    fn get_player_settings(&self) -> PlayerSettings {
        PlayerSettings::new(Race::Random, Some("BotName"))
    }
    // This method will be called automatically each game step.
    // Main bot's logic should be here.
    // Bot's observation updates before each step.
    fn on_step(&mut self, iteration: usize) -> SC2Result<()> {
        /* Your code here */
        Ok(())
    }
}

fn main() -> SC2Result<()> {
    run_vs_computer(
        // Pass mutable referece to your bot here.
        &mut MyBot::default(),
        // Opponent configuration.
        Computer::new(Race::Random, Difficulty::VeryEasy, None),
        // Map name. Panics if map doesn't exists in "StarCraft II/Maps" folder.
        "EternalEmpireLE",
        // Additional settings:
        // LaunchOptions {
        //     sc2_version: Option<&str>, // Default: None - Latest available patch.
        //     save_replay_as: Option<&str>, // Default: None - Doesn't save replay.
        //     realtime: bool, // Default: false
        // }
        LaunchOptions::default(),
    )
}

Add some cool stuff and watch how it destroys the opponent.

If you are careful guy who don't trust random macros that can destroy your PC, see #[bot] macro documentation to understand how it's implemented. You always can do the same thing by hands if needed.

What bot can see?

Self information

Common

FieldTypeDescription
self.raceRaceThe actual race your bot plays.
self.player_idu32Bot's in-game id (usually 1 or 2 in 1v1 matches).
self.mineralsu32Amount of minerals bot has.
self.vespeneu32Amount of gas bot has.
self.supply_armyu32Amount of supply used by army.
self.supply_workersu32Amount of supply used by workers.
self.supply_capu32The supply limit.
self.supply_usedu32Total supply used.
self.supply_leftu32Amount of free supply.
self.start_locationPoint2Bot's starting location.
self.start_centerPoint2Bot's resource center on start location.

Race values

FieldTypeDescription
self.race_values.start_townhallUnitTypeIdDefault townhall which can be built by a worker.
self.race_values.townhallsVec<UnitTypeId>All possible forms of townhall for your race.
self.race_values.gasUnitTypeIdBuilding used to extract gas from vespene geysers.
self.race_values.rich_gasUnitTypeIdBuilding used to extract gas from rich vespene geysers.
self.race_values.supplyUnitTypeIdSupply provider for your race.
self.race_values.workerUnitTypeIdWorker of your race.

Common opponent's information

FieldTypeDescription
self.enemy_raceRaceRequested race of your opponent.
self.enemy_player_idu32Opponent in-game id (usually 1 or 2 in 1v1 matches).
self.opponent_idStringOpponent id on ladder, filled in --OpponentId.
self.enemy_startPoint2Opponent's starting location.
self.enemy_start_centerPoint2Opponents's resource center on start location.

Ramps

FieldTypeDescription
self.ramp.myRampYour main base ramp.
self.ramp.enemyRampOpponent's main base ramp.
self.ramp.allVec<Ramp>All the ramps around the map.

Units

Common

FieldTypeDescription
self.units.allUnitsAll the units including owned, enemies and neutral.
self.units.myPlayerUnitsYour's only units.
self.units.enemyPlayerUnitsOpponent's units, on current step.
self.units.cachedPlayerUnitsOpponent's units, but also contains some hidden units from previous steps.
self.units.mineral_fieldsUnitsAll mineral fields on the map.
self.units.vespene_geysersUnitsAll vespene geysers on the map.
self.units.resourcesUnitsAll resources (both minerals and geysers) on the map.
self.units.destructablesUnitsDestructable rocks and other trash.
self.units.watchtowersUnitsWatchtowers reveal area around them if there're any ground units near.
self.units.inhibitor_zonesUnitsInhubitor zones slow down movement speed of nearby units.

What PlayerUnits consists of?

All fields are collections of Units:

FieldDescription
.allAll player units (includes both units and structures).
.unitsUnits only, without structures.
.structuresStructures only.
.townhallsFrom all structures only townhalls here.
.workersWorkers only (doesn't include MULEs).
.gas_buildingsThe gas buildings on geysers used to gather gas.
.larvasMost of zerg units are morphed from it (Populated for zergs only).
.placeholdersKind of things that appear when you order worker to build something but construction didn't started yet.

Other information

FieldTypeDescription
self.timef32In-game time in seconds.
self.expansionsVec<(Point2,Point2)>All expansions stored in (location, resource center) pairs.
self.vision_blockersVec<Point2>Obstacles on map which block vision of ground units, but still pathable.
self.game_infoGameInfoInformation about map: pathing grid, building placement, terrain height.
self.game_dataGameDataConstant information about abilities, unit types, upgrades, buffs and effects.
self.stateGameStateInformation about current state, updated each step.

What bot can do?

Units training

Training as much as possible marines may look like:

// Iterating bot's barracks which are completed (ready) and not already training (idle).
for barrack in self.units.my.structures.iter().of_type(UnitTypeId::Barracks).ready().idle() {
    // Checking if we have enough resources and supply.
    if self.can_afford(UnitTypeId::Marine, true) {
        // Ordering barracks to train marine.
        barrack.train(UnitTypeId::Marine, false);
        // Subtracting resources and suply used to train.
        self.subtract_resources(UnitTypeId::Marine, true);
    // Can't afford more marines. Stopping the iterator.
    } else {
        break;
    }
}

Building structures

Building up to 5 barracks might look like:

// Building near start location, but a bit closer to map center to not accidentally block mineral line.
let main_base = self.start_location.towards(self.game_info.map_center, 8.0);

// Checking if we have enough resources to afford a barrack.
if self.can_afford(UnitTypeId::Barracks, false)
    // Checking if total (current + ordered) number of barracks less than we want.
    && self.counter().all().count(UnitTypeId::Barracks) < 5
{
    // Finding a perfect location for a building.
    if let Some(location) = self.find_placement(
        UnitTypeId::Barracks,
        main_base,
        PlacementOptions {
            // Step increased here to leave some space between barracks,
            // so units won't stuck when coming out of them.
            step: 4,
            ..Default::default()
        },
    ) {
        if let Some(builder) = self.units
            // Finding workers which are not already building.
            .my.workers.iter().filter(|w| !w.is_constructing())
            // Selecting closest to our build location.
            .closest(location)
        {
            // Ordering scv to build barracks finally.
            builder.build(UnitTypeId::Barracks, location, false);
            // Subtracting resources used to build it.
            self.subtract_resources(UnitTypeId::Barracks, false);
        }
    }
}

Expanding

Building new CCs might look like:

// Checking if we have enough minerals for new expand.
if self.can_afford(UnitTypeId::CommandCenter, false)
    // Checking if we not already building new base.
    && self.counter().ordered().count(UnitTypeId::CommandCenter) == 0
{
    // Getting next closest expansion
    if let Some((location, _resource_center)) = self.get_expansion() {
        if let Some(builder) = self.units
            // Finding workers which are not already building.
            .my.workers.iter().filter(|w| !w.is_constructing())
            // Selecting closest to our build location.
            .closest(location)
        {
            // Ordering scv to build new base.
            builder.build(UnitTypeId::CommandCenter, location, false);
            // Subtracting resources used to build CC.
            self.subtract_resources(UnitTypeId::CommandCenter, false);
        }
    }
}

Units micro

Attacking when marines >= 15, defending base before:

let main_base = self.start_location.towards(self.game_info.map_center, 8.0);
let marines = self.units.my.units.iter().of_type(UnitTypeId::Marine).idle();

if self.counter().count(UnitTypeId::Marine) >= 15 {
    let targets = &self.units.enemy.all;
    if targets.is_empty() {
        for m in marines {
            m.attack(Target::Pos(self.enemy_start), false);
        }
    } else {
        for m in marines {
            m.attack(Target::Tag(targets.closest(m)?.tag), false);
        }
    }
} else {
    let targets = self.units.enemy.all.closer(25.0, self.start_location);
    if targets.is_empty() {
        for m in marines {
            m.move_to(Target::Pos(self.main_base), false);
        }
    } else {
        for m in marines {
            m.attack(Target::Tag(targets.closest(m)?.tag), false);
        }
    }
}

Prepearing for ladder

There're community organized ladders for bots:

  • SC2AI - Runs games on windows and latest patch of SC2.
  • AI Arena - Runs games on linux and patch 4.10.

Both use the same kind of system. In order to get your bot ready for ladder, make it parse following args:

  • --LadderServer - IP address.
  • --OpponentId - Id of the opponent on ladder.
  • --GamePort - Port.
  • --StartPort - Yet another port.

If you're too lazy to add argparser yourself, see examples folder, some examples already have fully functional parser.

Then call run_ladder_game this way:

run_ladder_game(
    &mut bot,
    ladder_server, // Should be 127.0.0.1 by default.
    game_port,
    start_port,
    opponent_id, // Or `None`.
)

The API will do the rest.

Since SC2AI and AI Arena run the games on different platforms you'll need to provide suitable binaries for each ladder.

Because of version differences ids are conditionally compiled for windows and linux.

Modules

action

Data structures for executing actions and analyzing actions failure.

api

Contains nice wrapper around SC2 API.

bot

Bot struct and it's helpers.

client

Everything needed to easily run games in SC2.

consts

Useful constants and static values.

debug

Items for interacting with Debug API.

distance

Traits for comparing distance between points and units.

game_data

Information about units, ablities, upgrades, buffs and effects provided by API stored here.

game_info

Constant information about map, populated on first step.

game_state

Information updated every step stored here.

geometry

Things you liked (hated) at school, now in SC2.

ids

Auto generated with generate_ids.py script from stableid.json ids of units, ablities, upgrades, buffs and effects.

pixel_map

Data structures, used to store map data.

player

Items representing various player's data.

prelude

The most frequent used items and various traits here. Prefered usage: use::rust_sc2::prelude::*;.

ramp

Data structures for storing data of ramps on the map with methods for extracting useful info from them.

score

SC2 Score interface.

unit

Stuff for convenient interaction with Units.

units

Data structures for storing units, fast filtering and finding ones that needed.

utils

Different utilites useful (or useless) in bot development.

Structs

PlayerSettings

Settings that must be provided by a player when joining a game.

Request

Request to the SC2 API.

Enums

Event

Events that happen in game. Passed to on_event.

Traits

Player

Trait that bots must implement.

Type Definitions

SC2Result

Attribute Macros

bot

#[bot] macro implements Deref<Target = Bot> and DerefMut<Target = Bot> for your struct. Implementing this traits allows you to access Bot fields and methods on your struct through self.

bot_new

#[bot_new] macro adds initialization of field added by #[bot] macro.