How to write an AI script



This part explains how to create a small Protoss AI script step by step with ScAIEdit or PyAI. It doesn't explain all the commands. You may check the advanced scripting part to get more information.



General recommendations:




Overview:

A bit of vocabulary I will use later.



Now choose the compiler you use:



Script with PyAI

Note: The script editor integrated in PyAI is not completely stable when this part was written - 8 May 2008 - it's recommend to often save the script with another text editor regularly - as a .txt file for example.

You will see all basic commands in this script. You can choose a particular section too.







Header commands:

These commands are put at the very beginning of the script. Their effects is not always well known.

# stat_txt.tbl entry 1343: Protoss Expansion Custom Level<0>
PMCx(1343, 101, aiscript):
   


# This line and the following are comments
# Starting headers

start_town()
transports_off()
farms_notiming()






Now we define the maximum number for the different units. This is generally done just after header commands but that's not an obligation.


# Maximum number of units

define_max(80, Protoss Probe)
define_max(20, Protoss Zealot)
define_max(20, Protoss Dragoon)
define_max(6, Protoss High Templar)
define_max(10, Protoss Dark Templar)
define_max(6, Protoss Archon)
define_max(2, Protoss Dark Archon)
define_max(1, Protoss Shuttle)
define_max(255, Protoss Reaver)
define_max(6, Protoss Observer)
define_max(255, Protoss Scout)
define_max(15, Protoss Carrier)
define_max(4, Protoss Arbiter)
define_max(4, Protoss Corsair)


define_max: the number is the maximum number of this unit the AI can have at the same time. 255 stands for 0 units, here the AI won't have any reaver.



Debugging command:

We will greet our opponent with a message.

# Debugging used to display messages

debug(g_MainBuilding, Hello world I'm a new born Protoss AI)
    --g_MainBuilding--


debug: this command is supprted only with ScAIEdit. It displays a message and jumps to the label "g_MainBuilding"
--name--: this is the definition of a label, it must be defined only in one place. But different commands can jump to the same label. Labels are not used only by debug, they are used by all jumping commands. We will see more of them later.



Now it's time to build our base. We will start with probes and a pylon.

Building commands:

# Main start building
   
build(7, Protoss Probe, 80)
wait_buildstart(7, Protoss Probe)
build(1, Protoss Pylon, 80)
wait_buildstart(1, Protoss Pylon)
build(13, Protoss Probe, 80)
wait_buildstart(13, Protoss Probe)

build: The first parameter is the total number of buildings (or workers) we want, the last parameter is the priority. In this example, the first line won't build 7 additional probes, it will build probes until the 7th is in construction.
wait_buildstart: This command is blocking. wait_buildstart(7, Protoss Probe): AI will wait until the 7th probe is in construction before going to the next line. Without this line, the AI may start building the pylon instead of training probes.
wait_build: This command is blocking too. It's similar to the wait_buildstart except that it will wait until the buiding is finished. Warning, if it's used with Terran and a worker is killed while building the AI will be locked until the unfinished building is destroyed.
Note: In earlier versions of BroodWar a new worker was sent.



We should have enough minerals to build a new Nexus in another place.

Expanding command:

# Main start expanding

expand(99, e_FastExpand)


expand: This command is used to start a new expansion. It's a jumping command and the expansion is run in parallel. The first parameter is the maximal number of expansions: if the AI has more expansions than this number, the expand command will be ignored. It probably acts has some kind of priority too, I nearly always use 99. The second parameter is the label correponding to the new expansion.

We will see what to build in this expansion later.




We continue to build our main base by placing a forge, an assimilator and a gateway.

build(14, Protoss Probe, 80)
wait_buildstart(14, Protoss Probe)
build(1, Protoss Forge, 80)
wait_buildstart(1, Protoss Forge)
build(15, Protoss Probe, 80)
wait_buildstart(15, Protoss Probe)
build(1, Protoss Assimilator, 80)
wait_buildstart(1, Protoss Assimilator)
build(16, Protoss Probe, 80)
wait_buildstart(16, Protoss Probe)
build(1, Protoss Gateway, 80)
wait_build(1, Protoss Gateway)


Now it's time to get some zealots to defend our base.

Training command:

# Main start training

train(2, Protoss Zealot)

train: This command is used to train non-worker units. This command is blocking. For example train(15, Protoss Zealot) will train zealots until 15 Zealots are alive or in training at the same time and next commands will wave to wait. That's why I generally put it in a parallel branch instead of the main part. But for this script sample we will put train in the main part to keep it simple.



Now we will make buildings to get our carriers and discover some useful commands. We tell the AI to use zealots and carriers in defense.

build(1, Protoss Cybernetics Core, 80)
build(2, Protoss Pylon, 80)

farms_timing()

defensebuild_gg(1, Protoss Zealot)
defenseuse_gg(1, Protoss Zealot)


# Main start researching

multirun(r_Global)

build(1, Protoss Stargate, 80)
wait_build(1, Protoss Stargate)
build(1, Protoss Fleet Beacon, 80)
wait_buildstart(1, Protoss Fleet Beacon)
build(2, Protoss Stargate, 80)
build(2, Protoss Photon Cannon, 80)

defenseuse_gg(1, Protoss Carrier)
defenseuse_ag(1, Protoss Carrier)
defenseuse_ga(1, Protoss Carrier)
defenseuse_aa(1, Protoss Carrier)

train(4, Protoss Carrier)
wait(400)
build(3, Protoss Stargate, 80)
wait_buildstart(3, Protoss Stargate)
train(10, Protoss Carrier)


defenseuse_ag: The first parameter is the number of units (second parameter) to use, when a ground (g) unit is attacked by an air (a) unit. It doesn't train them only use those available. Similar command exists for ground-ground (gg), ground-air (ga) and air-air (aa)
defensebuild_ag: This command tell to train unit (second parameter) when ground units are attacked by air units. The number of units that will be trained is not well determined, the AI often build many of them.
farms_timing: This command tells the AI to build farms - pylons for a Protoss - automatically, it's only needed once in a script. This functionnality is desactivated at the beginning by farms_notiming because the AI would build farms too soon otherwise.
multirun: This command is used quite often in my scripts, it creates a parallel branch. In this code example the new branch - starting at label "r_Global" - will be used for the upgrades, we will see how later.
wait: This command makes the AI waiting the time given as parameter before doing the next command. It only makes it waiting in the branch where this command is placed. This command is not really useful here. See here for time conversion.




We have our huge fleet of 10 carrier, let's how our opponent will handle that.

Attacking commands:


# Main start attacking

attack_add(10, Protoss Carrier)
wait(1000)
debug(s_MainStep1, Let's try these brand new spaceships)
    --s_MainStep1--
attack_do()

attack_add: Unlike most of the other commands, this command is cumulative. If you put 2 lines with "attack_add(5, Protoss Carrier)" the result will be an attack with 10 carriers. "attack_clear" resets the count. This command allows you to make attacks using different types of units.
attack_do: Attack with all the units added before. The units will make a group at the entrance of the enemy base and then attack. If some units are not trained yet, AI will train them and use them in the battle.


After this attack our enemy should be dead or we are in trouble. This is a loop I often use to close my main part, the AI expands and attacks regularly. The final training is missing in this script example.

# Main Final Loop

    --l_FinalLoop--
wait(4500)
send_suicide(0)
wait(500)
expand(99, e_FastExpand)
goto(l_FinalLoop)

send_suicide: This command take all the units and send them in the enemy base, it's rather a disorganized assault. If the parameter is set to 1 instead of 0 workers will go in the fight too.
goto: Jumping command, the execution will be continued from the label in parameter. Here it makes a loop going to "l_FinalLoop". All loops must have at least a wait(time) command, otherwise the AI will crash.





Now let's see how the parallel parts are made. An expansion starts with the start_town command. Then we need a Nexus. The building count is not shared with the main base, for instance a wait_build(1, Protoss Cybernetics Core) in expansion part will be blocking if we only build one in the main part.

# Expanding part

    --e_FastExpand--
start_town()
build(1, Protoss Nexus, 80)
wait_buildstart(1, Protoss Nexus)
wait(500)
build(1, Protoss Pylon, 80)
wait_buildstart(1, Protoss Pylon)
wait_build(1, Protoss Nexus)
build(6, Protoss Probe, 80)
wait_buildstart(6, Protoss Probe)
build(2, Protoss Photon Cannon, 70)
build(9, Protoss Probe, 70)
wait_buildstart(9, Protoss Probe)
build(2, Protoss Pylon, 70)
wait_buildstart(2, Protoss Pylon)
build(1, Protoss Assimilator, 70)
build(13, Protoss Probe, 70)
wait_buildstart(13, Protoss Probe)
build(6, Protoss Photon Cannon, 70)
stop()

stop: This command ends a branch, it can be used in the main branch too.




Here the researches are done in 2 branches, one for the cybernetics core and one for the fleet beacon. This is not mandatory, research can be placed in the main part too.

# Researching part

    --r_Global--
multirun(r_Cyber)
goto(r_Fleet)

# Research in cybernetics core

    --r_Cyber--
wait_build(1, Protoss Cybernetics Core)
upgrade(1, Protoss Air Weapons, 30)
# This is not the right research time
wait(2500)
upgrade(1, Protoss Plating, 30)
stop()


# Research in cybernetics core

    --r_Fleet--
wait_build(1, Protoss Fleet Beacon)
upgrade(1, Carrier Capacity, 30)
# This is not the right research time
wait(2500)
tech(Disruption Web, 20)
stop()


upgrade: This command upgrades the second parameter to the level of the first parameter at the priority of the third parameter. I use a 30 priority because upgrades are often less needed than new buildings. 
tech: This command research the first parameter at the priority of the second parameter. Here I put a 20 priority because we don't need disruption web in this script. This command is used for casted abilities such as psionic storm, recall, stimpacks, siege mode, burrowing, plague...



That's all, now you can create your own script or test this one. Check the following sections to get additional information.




Script with ScAIEdit


Header commands:

These commands are put at the very beginning of the script. Their effects is not always well known.

script_name Protoss Expansion Custom Level
script_id PMCx


; This line and the following are comments
; #### Starting headers ####

start_town
farms_notiming
transports_off




Now we define the maximum number for the different units. This is generally done just after header commands but that's not an obligation.

; #### Maximum number of units ####

define_max 80 probe
define_max 20 zealot
define_max 20 dragoon
define_max 6 high_templar
define_max 10 dark_templar
define_max 6 archon
define_max 2 dark_archon
define_max 1 shuttle
define_max 255 reaver
define_max 6 observer
define_max 255 scout
define_max 20 carrier
define_max 4 arbiter
define_max 4 corsair



This ScAIEdit section is not finished, you may want to take a look at the PyAI part as nearly only syntax is changing.