The config file is named Steamhammer_2.4.2.json and controls settings including the bot’s choice of opening build order.
The config file is easy and fun to edit. Especially useful are the Debug section if you want to see what the bot is thinking, and the Strategy section if you want to change its openings. If you want to test your own play or any bot against a specific opening, you can write out the opening build order and tell Steamhammer to follow it. (The config file is inherited from UAlbertaBot and you can do the same with UAlbertaBot, though Steamhammer has more features.)
The file is in JSON format. That means it is finicky and there is no official way to add comments. If you introduce a syntax error that you can’t find, drop the file into any handy JSON checker (plenty are online).
I’ll go through section by section.
The bot’s name and author. If you fork Steamhammer, don’t miss this part! Setting PrintInfoOnStart to true tells the bot to announce its name and author when it starts up.
SetLocalSpeed is 0 to let the bot play at its full speed in games you set up against other bots. Some bots set a slower local speed by default. During a game, you can enter commands like /speed 30 to slow it down and /speed 0 to speed it up again. These commands work across bots, not only in Steamhammer, and there are other commands. UserInput means you can manually enter Starcraft actions for the bot while it is playing, which makes some experiments easier. Set CompleteMapInformation to true for the bot to cheat and see everything (it is not allowed in competition, obviously).
Various micro settings. The most confusing is KiteLongerRangedUnits, which really means “kite these units even if the opposing unit has a longer weapons range.” It makes sense mostly for fast units like mutalisks and vultures. If ScoutDefenseRadius is set to a positive integer, then an enemy worker scout which approaches to within that range of our main base (in pixels) will be met with a worker of our own that tries to chase it away. In Steamhammer it is turned on for terran, because terran needs to defend SCVs which are constructing buildings, and set to zero for other races.
The following settings can be given a single value, or a different value for each race. A different value for each race can be useful if you are playing random.
KiteWithRangedUnitsWorkersDefendRushRetreatMeleeUnitHPCombatSimRadiusUnitNearEnemyRadiusScoutDefenseRadiusTo give a single value, just specify it: "CombatSimRadius" : 600.
To give a different value for each race, use a JSON object like this. "CombatSimRadius" : { "Zerg" : 400, "Protoss" : 600, "Terran" : 700 }.
If you use a JSON object and leave out some races, then omitted races get the value 0 for the setting (or false for a true/false setting). In Steamhammer, this is how scout defense is turned on only for terran: "ScoutDefenseRadius" : { "Terran" : 500 }.
ProductionJamFrameLimit sets a timeout on production. If this many frames pass without any macro action being taken (such a training a unit or starting research), the production manager assumes that production is deadlocked and clears the production queue to try again. The timeout must be long or it will trigger in normal play. In Steamhammer, it is set to 1440 frames, which is 1 minute of game time.
WorkersPerPatch sets the maximum number of workers to seek to make for each mineral patch at the bot’s bases. It is a floating point number. According to conventional wisdom, you should go for 1 to 1.5 drones per mineral patch as zerg, and 2 workers per patch as the other races. 2.0 is the largest reasonable value, because Steamhammer uses mineral locking which locks no more than two workers to each mineral patch.
If you play a fixed race, you can give a single floating point number for WorkersPerPatch. Steamhammer gives a number for each race so that it can play random. It works the same as in the Micro settings; see above.
The other options should be self-explanatory, except that PylonSpacing only applies to the first 2 pylons made. The first 2 pylons are spaced more widely to power a larger area for buildings.
The options named “Draw” draw information on the screen. In the release version, only a couple of the drawing options are turned on. In development, I turn on the appropriate ones to see what the bot is thinking. If you turn on too many at the same time, they’ll draw all over each other. DrawGameInfo and DrawProductionInfo are my favorites.
DrawGameInfo - the opponents, chosen strategy, map, and game time in the upper leftDrawUnitHealthBars - above each unitDrawProductionInfo - the production queue, what the bot is planning to build, drawn in color on the leftDrawBuildOrderSearchInfo - tech info from BOSS, can be useful to coders (not used when playing zerg)DrawQueueFixInfo - print a message when the production queue is reordered or a production jam is clearedDrawScoutInfo - about the early game scouting worker and its plansDrawEnemyUnitInfo - counts of enemy stuff we’ve seen, drawn on the rightDrawHiddenEnemies - The former positions of enemy units which were seen, but are now out of sight, are marked as small green circles, or yellow circles if they were burrowed. If the position itself has been in view and the unit was not there any more, the mark is a red X. A cloaked unit in sight range but undetected is a larger violet circle and is labeled with its unit type.DrawModuleTimers - black box of millisecond timers, to see how well the bot is adhering to the time limitDrawResourceInfo - details related to gathering minerals and gasDrawCombatSimInfo - The predictions of who will win the fight and by how much, drawn near each cluster of friendly combat units.DrawUnitTargetInfo - lines pointing to where units are moving or aimingDrawUnitOrders - For each unit, unit->getOrder(), plus any research or production that is underway.DrawMicroState - For each unit, information about what it is doing, as recorded by MicroState. Not the same as its actual orders, but closely related.DrawMapInfo - Information about each base on the map, drawn at the base locations.DrawMapGrid - a big grid laid over the map, with summary info in some grid squaresDrawMapDistances - ground distance of each tile from the starting location (slow because it draws so much)DrawBaseInfo - A list of x,y locations of all bases down the right side of the screen, green if we own them, orange if the enemy is believed to.DrawStrategyBossInfo - Zerg only. Info from the zerg strategy boss, drawn to the left of the base info.DrawSquadInfo - the bot divided its forces into these squads, drawn in the upper centerDrawClusters - The clusters of friendly combat units, with a little summary data about each. See also DrawCombatSimInfo above.DrawWorkerInfo - some stuff related to workersDrawMouseCursorInfo - I never use thisDrawBuildingInfo - buildings about to be started or under construction, drawn in the upper centerDrawReservedBuildingTiles - places where buildings are not allowedDrawBOSSStateInfo - more tech info from BOSS, not really usefulThe map grid that Steamhammer uses to keep track of explored areas and find nearby units efficiently. I have never had a reason to change the number.
Everything to do with reading and writing files, which means the error log file and the persistent opponent models (learning how each opponent plays so we can adapt). The tournament server or other game playing setup determines what the directories should be set to. The default values are standard and should Just Work unless you are doing something unusual.
About the opponent model: For dynamic data, tournament servers are generally set up so that a bot has read-only access to its read directory and write-only access to its write directory. The server is responsible for deciding whether and when to copy files from the write directory to the read directory. The separate AI/ directory is read-only and is traditionally for static data read at initialization time.
You have independent control over whether to read existing opponent model data and/or write new data after each game. The data includes game results and a variety of other information. I use the independent control for debugging. Other potential uses are: 1. For analysis. Set it to write only and examine the game records for information you’re interested in. 2. For manual control over the bot’s play against each opponent. Instead of having the bot learn for itself, prepare data so that the bot plays as you wish against each opponent, and set it to read only.
ErrorLogFilename - When an assertion fails, it writes to this file.LogAssertToErrorFile - You should never need to turn this off.StaticDirectory - For prepared, read-only data. Steamhammer doesn’t currently use this directly.PreparedDataDirectory - For prepared, read-only opponent model files. I put pre-learned data in here to prepare Steamhammer for a tournament. A subdirectory of StaticDirectory.ReadDirectory - For reading back saved opponent model data (game records) from earlier games.WriteDirectory - For recording opponent model files for future games.MaxGameRecords - If there are more than this many game records to save for a given opponent, drop the oldest ones.ReadOpponentModel - Read the saved data before each game, true or false.WriteOpponentModel - Append new data after each game, true or false.Settings to control some aspects of the bot’s play.
SCHNAILMeansHuman - If true, when the bot recognizes that it is running under SCHNAIL it automatically forces the HumanOpponent flag to be true. .HumanOpponent - If true, the bot changes its play to be more appropriate for facing a human. In particular, it surrenders much earlier when losing (provided SurrenderWhenHopeIsLost is true).SurrenderWhenHopeIsLost - If true, the bot gives up the game when it is hopelessly losing.ScoutHarassEnemy - If true, the scouting worker attacks, wreaking havoc on weak opponents and dying quickly to strong opponents; can be set differently for each race (see the Micro section).AutoGasSteal - Decide automatically, based on data in the learned opponent model (see the IO section above), whether to steal gas this game.RandomGasStealRate - A floating point probability from 0.0 to 1.0, steal gas this game regardless.Burrow - True or false: Research zerg burrow at (what the strategy boss finds to be) an appropriate point in the game.MaxQueens - How many queens should the strategy boss make, at most? Sensible values are 0, 1, or 2. Steamhammer supports larger fleets of queens, but there are flaws, and the strategy boss doesn’t understand when it makes sense.MaxInfestedTerrans - Unused. A bug in the production system prevents Steamhammer from making infested terrans.UsePlanRecognizer - if true, try to recognize the enemy opening plan; if false, the plan is always UnknownThe next entries tell which opening to play when when there is no opponent model for the current opponent. See below for the case where the opponent model has an idea. Steamhammer inherited the opening specification from UAlbertaBot, but adds many features.
There are several different ways to tell Steamhammer which opening to play in this section.
1. If you put in a line "Zerg" : "9PoolSpeed", then Steamhammer will play 9 pool speed whenever it is zerg, unless another setting overrides it.
2. If you put in a line "ZvT" : "ZvT_2HatchMuta", then Steamhammer will play its 2 hatch mutalisk opening every time it plays zerg versus a terran. In the matchup name before the colon, “U” means that the opponent chose random (the race is “unknown”). The bot’s own race can never be unknown, even when playing random; we know it as soon as the game starts.
3. Steamhammer can choose openings randomly. Instead of the name of an opening, give a StrategyMix which lists different openings with weights for how often they should be played. The weights can be any non-negative integer, but I like to choose weights which add up to 100 so that they are percentages. (Get the punctuation exactly right. JSON format is unforgiving.)
"ZvU" :
{ "Zerg" : [
{ "Weight" : 10, "Strategy" : "5Pool" },
{ "Weight" : 20, "Strategy" : "9PoolExpo" },
{ "Weight" : 70, "Strategy" : "9PoolSpeed" }
]},
4. The map size can control the weights in a strategy mix, so that you can do things like rush only on a 2-player map or prefer to follow a greedy opening on a 4-player map. A weight given as Weight is a default weight that applies to all map sizes. A weight given with a number, like Weight2, is specific to that map size and overrides the default weight. Brood War map sizes range from 2 players to 8 players, and maps used in modern competition range from 2 to 4 players. This line says that the 9PoolSpeed opening should be played more often on 2-player maps:
{ "Weight" : 5, "Strategy" : "9PoolSpeed", "Weight2" : 15 },
Enemy specific strategies. Steamhammer can play different openings against opponents that it recognizes by name. Set UseEnemySpecificStrategy to true and, inside the EnemySpecificStrategy subsection, give an opening for each opponent that you want to treat differently. The enemy-specific openings take precedence over openings suggested by the opponent model (see below). The example means “When playing Jakub Trancik as zerg, play the 9 pool expo opening. When playing as protoss, open with double gateways on 9.” In this example, the terran opening is not specified, so it is chosen by the usual method.
"Jakub Trancik" : { "Zerg" : "9PoolExpo", "Protoss" : "9-9Gate" }
The random openings also work inside the EnemySpecificStrategy section. You can add a strategy mix for each race that your bot plays, headed by the name of the race. The example gives a strategy mix when zerg, a single opening when protoss, and no special opening for terran.
"Marian Devecka" : {
"Zerg" : [
{ "Weight" : 50, "Strategy" : "OverhatchLing" },
{ "Weight" : 50, "Strategy" : "OverhatchMuta" }
],
"Protoss" : "1ZealotCore"
},
Counter strategies for the opponent model go in the CounterStrategies subsection. When the opponent model at the start of the game believes it can predict the enemy plan, it looks here for a strategy mix which counters the plan. Steamhammer first looks for counter strategies specific to the enemy race. The name is in the format “Counter [plan name] v[race character]”. The race character is “U” for Unknown if the opponent went random. You can use all the usual features of random opening selection. As you can see in the example, zerg is configured with a wider range of choices.
"Counter Safe expand vT" :
{
"Terran" : "14CCTanks",
"Protoss" : "13Nexus",
"Zerg" : [
{ "Weight" : 1, "Strategy" : "FastPool", "Weight2" : 5 },
{ "Weight" : 9, "Strategy" : "9PoolSpeed" },
{ "Weight" : 0, "Strategy" : "ZvT_3HatchMuta", "Weight2" : 50 },
{ "Weight" : 50, "Strategy" : "ZvT_3HatchMutaExpo", "Weight2" : 0 },
{ "Weight" : 30, "Strategy" : "3HatchLurker" },
{ "Weight" : 10, "Strategy" : "3HatchPoolMuta", "Weight2" : 0 }
]
},
If no counter strategy is found for the specific enemy race, Steamhammer next looks to see if there’s a general one for all races—leave out the “vX” string. This example points to a reusable strategy combo that specifies openings for each race Steamhammer might be playing, no matter the enemy race (see below).
"Counter Worker rush" : "AntiFastCheese",
If no counter strategy is found, Steamhammer falls back on its per-matchup strategy mix. So if the regular repertoire is best in any given case, don’t specify a counter for that case.
The StrategyCombos subsection lets you define strategy mixes that you can refer to by name. For example, Steamhammer knows several variants of 4 pool and 5 pool zergling rushes, all of which should be played sparingly but in a variety of contexts. So it gathers the rushes into a FastPool strategy combo which it can refer to as a single item.
"FastPool" : {
"Zerg" : [
{ "Weight" : 1, "Strategy" : "4PoolHard", "Weight2" : 5 },
{ "Weight" : 4, "Strategy" : "4PoolSoft", "Weight2" : 10 },
{ "Weight" : 80, "Strategy" : "5PoolHard" },
{ "Weight" : 5, "Strategy" : "5PoolSoft" }
]
},
A strategy combo can be given anywhere that you can give a strategy name, including recursively in another strategy mix. (Do not form a loop. The bot could fall into an infinite loop trying to interpret it.) That way you can build strategy mixes piece by piece.
Nested strategy mixes are resolved one at a time. If the outer strategy mix says to play FastPool 5% of the time, then that’s what happens (which may itself vary depending on the map size). Then FastPool says “oh, this is a 2-player map, I’ll give the 4 pool variants more weight.”
The Strategies subsection defines the opening build orders themselves. The opening build order is a sequence of items to build one after the other. Each item can be a unit, a building, an upgrade, a tech to research, or a command. The name of each item is case-insensitive, that is, upper case and lower case do not matter.
As an abbreviation, you can order up multiple units or buildings of the same kind. "3 x zergling" is the same as "zergling", "zergling", "zergling".
OpeningGroup is set for terran and protoss openings. Production code in the StrategyManager class looks at the OpeningGroup to decide what units to produce in the middle game. (Coding hint: StrategyManager can change the OpeningGroup value on the fly to transition to a new unit mix.) Zerg does not use the OpeningGroup, but figures out dynamically what units to produce in the class StrategyBossZerg.
Commands can be executed during the build. All the commands start with the keyword “go”.
go scout - unconditionally send a scout worker to the enemy base and have it stay there watchinggo scout if needed - send a scout only if the enemy base location is not already known (by any means), and have it stay therego scout location - return the scout home (or don’t even send it out) as soon as the enemy base location becomes knowngo scout once around - send a scout on one tour of the enemy base, then return it homego steal gas - take the enemy’s geyser with the scout workergo stop gas - stop collecting gas from all extractorsgo start gas - start collecting gas from all extractorsgo gas until [n] - collect amount [n] of gas, then stop (e.g. go gas until 100 for zergling speed)go defensive - units stay at home for the time beinggo aggressive - units seek out the enemygo extractor trick drone - use the extractor trick to get an extra drone (zerg only)go extractor trick zergling - use the extractor trick to get an extra pair of zerglings (zerg only)go pull workers [n] - pull the given number of workers off of mining into a combat squadgo pull workers leaving [n] - pull all except [n] workers into a combat squadgo release workers - send any surviving pulled workers back home to minego nonadaptive - (zerg only) do not try to adjust the drone count in the opening depending on what the enemy is doing, but follow the build order literallygo queue barrier - control queue reordering; an item after the barrier will never be moved in front of itGas. When you first build a refinery, Steamhammer assumes that you want to collect gas right away, so gas collection is turned on. I put “go gas until” commands right after the refinery (such as for zerg): "extractor", "go gas until 100". Steamhammer remembers the intention and executes the steps at the right time (though it often collects 8 or 16 gas more than it should).
Stealing gas. Stealing gas means building a refinery on the opponent's geyser, to delay the opponent from getting gas income. Above, the AutoGasSteal and RandomGasStealRate options provide two ways to tell Steamhammer to steal gas. You can also command a gas steal as part of the build order with go steal gas. The methods are independent: If any of them orders a gas steal, Steamhammer will attempt to steal gas.
Aggressive versus defensive. After "go defensive", units wait next to the command center, nexus, or hatchery unless an enemy comes near. After "go aggressive", they head out onto the map and close with the enemy. Units start out aggressive and only become defensive if you explicitly put in "go defensive". At the end of the opening build order, units automatically become aggressive if they aren’t already. Defensiveness is most useful for terran, which benefits from gathering a group of units before moving out, but it can also be useful for other races when playing an opening with few early units. I put "go defensive" right at the start of a defensive opening.
Extractor trick. The extractor trick is a way for zerg to slightly exceed its supply limit and get an extra unit before making an overlord. Steamhammer supports using it to get an extra drone or an extra pair of zerglings. The commands only operate if the bot is zerg and there is an available geyser to perform the trick. If the bot is not at its supply limit (maybe there were combat losses), the bot will skip doing the trick and simply make the requested unit.
Pulling workers. Pulling workers puts workers into the Ground squad, a combat squad that generally wants to go attack the enemy. The workers join in with any other ground combat units and move and fight alongside them. This can strengthen an all-in rush.
Locations. If you ask for “hatchery”, Steamhammer assumes you want the hatchery at a gas expansion. You can choose.
"hatchery" - at a gas expansion"hatchery @ expo" - at a gas expansion (the same)"hatchery @ min only" - at any expansion, whether it has gas or not"hatchery @ hidden" - at a “hidden” base somewhere far from both players’ mains"hatchery @ macro" - macro hatchery at the main baseIf you ask for "bunker", "missile turret", "photon cannon", or "creep colony" to build static defense, Steamhammer assumes that you want it at the main base. Other buildings are the same; stuff is built at the main unless you say otherwise. There are other choices.
"creep colony" - At the main base, near the hatchery (or other resource depot)."creep colony @ main" - At the main base, near the hatchery."creep colony @ natural" - At the natural, if possible (otherwise at the main), and in any case near the hatchery. "creep colony @ front" - At what the bot believes is the current front line that should be defended, and not at the hatchery but in a forward defensive position. This is usually what you want for static defense.If you order a creep colony in the build order and do not (within a few more items) morph it into a spore colony, then Steamhammer will assume that you want it to be a sunken colony. It will automatically morph the sunken with no explicit order.
updated January 2020