4.2 - Dynamic Info- Minerals, Supply, and Time
If self.game_info
is the unchanging blueprint of the battlefield, then the data available through self.state
and its convenience properties represents your bot's real-time senses. This is the dynamic information—minerals, supply, unit positions, time—that changes on every single frame of the game.
Mastering your bot's senses is the key to reactive, intelligent behavior. This data is the input for every decision your bot will ever make.
The Core AI Loop: Sense, Think, Act
Your on_step
method should be structured around this fundamental AI control loop.
.-------------------- SENSE ------------------------.
| - What are my resources? (self.minerals) |
| - How much supply do I have? (self.supply_left) |
| - Where are my units? (self.units) |
| - Where are the enemy units? (self.enemy_units) |
`---------------------------------------------------`
|
v
.-------------------- THINK --------------------.
| if self.minerals > 100: |
| if self.supply_left < 5: |
| if self.enemy_units.in_attack_range_of(x): |
`---------------------------------------------`
|
v
.--------------------- ACT ---------------------.
| await self.build(UnitTypeId.BARRACKS, ...) |
| barracks.train(UnitTypeId.MARINE) |
`-----------------------------------------------`
Your bot continuously cycles through this loop within on_step
, sensing the world, applying its strategic logic, and issuing commands.
Key Dynamic Properties (Your Bot's Senses)
While the raw data lives in self.state
, the BotAI
class provides easy-to-use convenience properties. You should always prefer these helpers.
Category | Property | Data Type | Practical Use in a Project |
---|---|---|---|
Resources | self.minerals self.vespene | int | Crucial for managing your bot's in-game economy, representing the current stockpile of minerals and vespene gas, respectively. |
Supply | self.supply_used self.supply_cap self.supply_left | int | Triggers the construction of new Supply Depots, Pylons, or Overlords to avoid being supply-blocked. The cap in self.supply_cap short for capacity. |
Time | self.time self.time_formatted | float str | Used for timing build orders, scouting schedules, and logging events. self.time is more reliable than iteration . |
Your Units | self.units self.structures self.workers self.townhalls | Units | The main tools for managing your forces. Used to find idle units, issue commands, and check army composition. |
Enemy Units | self.enemy_units self.enemy_structures | Units | The primary input for all combat and reactive logic. Used to check for threats and identify targets. |
Production | self.already_pending(unit_id) | float | Crucial for preventing redundant actions, like building two Supply Depots when you only need one. |
For RL Devs | All of the above | N/A | These properties form the core of a structured observation space for a Reinforcement Learning agent. Normalizing these values (e.g., supply used / 200) is a common and effective practice. |
A Developer's Decision-Making Checklist
When writing your on_step
logic, follow this thought process:
- 1. Am I alive? Check if you have townhalls. If not, you've likely lost.
- 2. Can I spend my money? Check
self.minerals
andself.vespene
. Idle resources are wasted resources. - 3. Am I supply-blocked? Check
self.supply_left
. If it's too low, building supply should be your #1 priority. - 4. Are my production buildings busy? Find idle townhalls, barracks, etc., and queue a unit.
- 5. Am I under attack? Check if
self.enemy_units
are near your key structures.
Code Example: The Starter Macro Bot
This bot demonstrates a robust, basic macroeconomic cycle. It constantly builds workers and ensures it never gets supply-blocked—the two most important skills for any new bot.
# starter_macro_bot.py
from sc2.main import run_game
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.ids.unit_typeid import UnitTypeId
from sc2.player import Bot, Computer
from sc2.data import Race, Difficulty
class MacroBot(BotAI):
"""
A bot that demonstrates a basic economic cycle using dynamic info.
1. Constantly builds workers.
2. Builds supply structures when needed.
"""
async def on_step(self, iteration: int):
# The two core tasks of macro: worker production and supply management.
await self.manage_worker_production()
await self.manage_supply()
async def manage_worker_production(self):
"""Builds workers from idle townhalls if we can afford them."""
# SENSE: Do we have any townhalls? Are they idle? Can we afford a worker?
if (
self.townhalls.exists
and self.can_afford(UnitTypeId.SCV)
and self.townhalls.idle.exists
):
# THINK: We have an idle townhall and money for an SCV.
# ACT: Train an SCV from a random idle townhall.
idle_townhall = self.townhalls.idle.random
idle_townhall.train(UnitTypeId.SCV)
async def manage_supply(self):
"""Builds a Supply Depot if we are running low on supply."""
# SENSE: Is our supply running low? Are we already building a depot?
# A good threshold is supply_left < (number of townhalls * 2) + 2
supply_threshold = self.townhalls.amount * 2 + 2
is_supply_low = self.supply_left < supply_threshold
is_depot_in_progress = self.already_pending(UnitTypeId.SUPPLYDEPOT) > 0
# THINK: If supply is low AND we aren't already building a depot, we must act.
if (
is_supply_low
and not is_depot_in_progress
and self.can_afford(UnitTypeId.SUPPLYDEPOT)
):
# ACT: Build a Supply Depot near our first townhall.
# self.find_placement is a powerful but potentially slow function.
build_location = await self.find_placement(
UnitTypeId.SUPPLYDEPOT,
near=self.townhalls.first.position,
max_distance=10,
)
if build_location:
# Use the built-in helper to select a worker for building.
worker = self.select_build_worker(build_location)
if worker:
worker.build(UnitTypeId.SUPPLYDEPOT, build_location)
if __name__ == "__main__":
run_game(
maps.get("AbyssalReefLE"),
[Bot(Race.Terran, MacroBot()), Computer(Race.Zerg, Difficulty.VeryEasy)],
realtime=True,
)