Skip to main content

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 awaited or batched with self.do_actions().

A Tactical Comparison of Core Commands

Choosing the right command depends entirely on your tactical goal.

Commandunit.attack(target)unit.move(target)unit.stop()unit.hold_position()
Tactical RoleOffensive. 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 TypeUnit or Point2Point2 onlyNoneNone
Primary Use CaseAttack-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.
  • Should this unit hold its ground?
    • Yes, and fire back -> Use hold_position.
    • Yes, and do nothing -> Use stop.

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,
)