5.2 - Basic Commands- Attack, Move, Stop
5.2. Basic Commands: Attack, Move, and Stop
These four commands are the fundamental verbs of StarCraft II. They form the basis of all unit control, from simple army movements to complex micro-management. Every command is a method on a Unit
object and, because it's a network action, must be await
ed or batched with self.do_actions()
.
A Tactical Comparison of Core Commands
Choosing the right command depends entirely on your tactical goal.
Command | unit.attack(target) | unit.move(target) | unit.stop() | unit.hold_position() |
---|---|---|---|---|
Tactical Role | Offensive. Engages targets and pursues them. | Repositioning. Moves safely from A to B. | Full Stop. Cancels all actions. | Defensive. Holds a position and fires on enemies in range. |
Engages Enemies? | Yes. Automatically fights any enemies encountered. | No. Will ignore enemies and continue to its destination. | No. Will not auto-acquire targets. | Yes. Will fire on enemies in range but will not chase them. |
Target Type | Unit or Point2 | Point2 only | None | None |
Primary Use Case | Attack-Moving an army. Standard way to advance on the enemy. | Retreating damaged units or moving workers. | Arranging units precisely for an ambush or spell. | Setting up a defensive line at a chokepoint. |
Command Deep Dive
1. The attack
Command
This is your primary tool for aggression.
await unit.attack(enemy_unit)
: The unit will hunt down and attack a specific enemy.await unit.attack(map_location)
: The unit will attack-move. This is the most common command for an army. It moves towards a location and automatically fights any hostiles it meets along the way.
2. The move
Command
This is for pure, uninterruptible movement.
await unit.move(map_location)
: The unit will travel to the location, ignoring all distractions. If it takes damage, it will not retaliate. This is essential for retreating.
3. The stop
and hold_position
Commands
These are for static positioning.
await unit.stop()
: Cancels the unit's current order. It will stand still and do nothing.await unit.hold_position()
: The superior defensive command. The unit stands still but will fire on any enemies that enter its weapon range, making it perfect for defending a ramp.
A Developer's Tactical Checklist
When controlling a unit, ask yourself:
- What is my goal? (Aggression, Retreat, Defense)
- Should this unit fight if it meets an enemy?
- Yes -> Use
attack
. - No -> Use
move
.
- Yes -> Use
- Should this unit hold its ground?
- Yes, and fire back -> Use
hold_position
. - Yes, and do nothing -> Use
stop
.
- Yes, and fire back -> Use
Code Example: The Tactical Officer Bot
This bot demonstrates making tactical decisions for its army. It will send its main force to attack-move, but if any unit becomes heavily damaged, it will be ordered to move
back to a safe location.
# tactical_officer_bot.py
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.main import run_game
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import UnitTypeId
from sc2.units import Units
class TacticalOfficerBot(BotAI):
"""
A bot that makes tactical decisions using attack and move commands.
"""
async def on_step(self, iteration: int):
# First, ensure we have an army to command.
await self.build_army()
all_marines: Units = self.units(UnitTypeId.MARINE)
if not all_marines.exists:
return
# Define strategic points for our commands.
attack_destination = self.enemy_start_locations[0]
safe_retreat_point = self.start_location.towards(self.game_info.map_center, 10)
# We will batch our commands for performance.
actions = []
# Loop through every marine and make a tactical decision.
for marine in all_marines:
is_damaged = marine.health_percentage < 0.5
# TACTICAL DECISION:
if is_damaged:
# Goal: Retreat. The unit should not fight.
# Command: move()
actions.append(marine.move(safe_retreat_point))
else:
# Goal: Aggression. The unit should fight on its way to the target.
# Command: attack() (attack-move)
actions.append(marine.attack(attack_destination))
# Execute all collected commands in a single batch.
if actions:
await self.do_actions(actions)
async def build_army(self):
"""A simple helper method to produce units for the demo."""
if self.supply_left < 2 and not self.already_pending(UnitTypeId.SUPPLYDEPOT):
if self.can_afford(UnitTypeId.SUPPLYDEPOT):
await self.build(UnitTypeId.SUPPLYDEPOT, near=self.start_location.towards(self.game_info.map_center, 5))
return
if not self.structures(UnitTypeId.BARRACKS).exists and not self.already_pending(UnitTypeId.BARRACKS):
if self.can_afford(UnitTypeId.BARRACKS):
await self.build(UnitTypeId.BARRACKS, near=self.start_location.towards(self.game_info.map_center, 8))
return
if self.can_afford(UnitTypeId.MARINE):
train_actions = []
for barracks in self.structures(UnitTypeId.BARRACKS).ready.idle:
train_actions.append(barracks.train(UnitTypeId.MARINE))
if train_actions:
await self.do_actions(train_actions)
if __name__ == "__main__":
run_game(
maps.get("BlackburnAIE"),
[
Bot(Race.Terran, TacticalOfficerBot()),
Computer(Race.Zerg, Difficulty.Easy)
],
realtime=True,
)