archive by month
Skip to content

Steamhammer’s defiler play

Earlier I claimed that if Steamhammer has defilers out, it is probably winning. It’s true but misleading. Steamhammer’s game plan is such that it doesn’t try to win in the middle game (though it may win accidentally); the midgame is about holding the enemy off, getting upgrades, and growing the economy. When its drone count reaches 75, Steamhammer switches to all army production and its military strength grows rapidly—and that is the same time that defilers come into their own. When it reaches the late game, whether there are defilers or not, Steamhammer will be hitting hard and very likely winning, because if it weren’t winning it would probably have lost already. The late game is Steamhammer’s strongest phase.

Nevertheless, Steamhammer’s defiler usage has grown skillful enough that it definitely contributes to the bot’s strength. It’s an important milestone, because good defiler usage is critical to strong zerg play. Defilers are complicated, and zerg cannot reach its potential without mastering them. Steamhammer is far from mastering defilers, but I think it has become more adept with them than any other bot.

The original description of the defiler implementation is still accurate. Bugs have been fixed and refinements made, but the structure is unchanged.

that laser precision

I picked this game TyrProtoss-Randomhammer because it shows off Steamhammer’s precision and fluidity in defiler spellcasting. The precision was always there; the fluidity was reached by a long road with a year’s worth of bug fixes and other improvements. In the first picture, Steamhammer is about to start its decisive final attack. The attack would have won with or without defiler support. Accurate defiler spells made it effortless.

a slightly weak plague

Steamhammer chooses plague because it looks like the fight will feature zealots versus hydras. The plague is actually a little weak. The defiler (selected and a little hard to see) is hemmed in by friendly units and cannot move forward, and Steamhammer hasn’t seen all the approaching enemies yet, so it plagues only 3 zealots. With a short delay for the opposing armies to close, it could have plagued more enemies, but it is not able to figure that out. Still, the plague helped in the battle.

swarm neutralizes the dragoons

Once the front zealots have died, Steamhammer has a fuller view of the situation, and realizes that the enemy has mostly dragoons left. The defiler (which has the energy upgrade) casts swarm, consume, consume, then a second swarm. The swarms are accurately placed to nullify the dragoons and drive the enemy back, bringing the fight into the natural. In the picture the defiler has just cast the second swarm, and it instantly consumes again.

plague hammers in the final nail

The defiler is again stuck behind friendly units for a time, but protoss is forced back to the ramp. As zerg units spread out to raze the enemy natural, the defiler becomes free to move and drops a perfect plague on the ramp. Every protoss unit on the ramp is hit, and the zerg melee units that are in contact are untouched. No better plague is possible.

Steamhammer is awkward in maneuvering its defiler, and it is unable to foresee better opportunities that will arise in the near future. But when a spell goes down, it is often close to the best that is possible at that moment. It’s plenty good for now.

potential to turn the game

One defiler spell can make the difference between losing and winning. In practice, Steamhammer needs more skills before that will happen often in its games, but the examples here offer a foretaste. The game Steamhammer-McRave I picked to show the brute power of plague.

The first defiler of the game wandered carelessly into the front lines, stood on top of an isolated lurker, and got stormed to death. The defiler didn’t know the best place to go, that’s one weakness, and Steamhammer only makes 1 defiler at a time, that’s another weakness. An alert opponent can pick off the defiler and earn a respite. When the second defiler of the game joined the fray, this happened:

a plague on all your zealots

It’s a fun game, worth watching through. At the time of the picture, Steamhammer had been slowly twisting the situation into its favor from far behind, but McRave was still winning. The game could have gone either way. After this massive plague, turning a whole phalanx of zealots into straw, McRave’s army was broken and it quickly collapsed.

Here’s a picture from a different Steamhammer-McRave game. The red stuff all over the protoss natural is from 3 active plagues cast one after another by one defiler. The defiler just died; it sacrificed itself amidst the zealots (at the white spot of a dragoon hit) to get the third plague off, the one currently spreading over dragoons in the rear. The sacrifice was worth it. The protoss army was hollowed out, giving zerg time to consolidate an economic advantage and win. Without those plagues, McRave would have been able to move out and take the game with its bigger army (though I’m not sure it would have chosen to).

plague of plagues

Here is a third example of a turnaround plague, from Steamhammer-MadMixT. After playing well much of the game, Steamhammer went astray and collapsed under a terran attack. It was about to lose its main and the game when a defiler risked its life to throw a plague over the core of the terran force, reducing the units to eggshells. Plague hurts terran more than protoss, which has shields. It then took only a few zerg units to clear the attack, partly due to terran’s disorganized movement, and with a return to superior play, Steamhammer slowly clawed its way to a win.

eggshell terrans

keeping active

Steamhammer is weak at using defilers in defense. I think it’s the widest remaining gap in defiler play. But it’s pretty good at using defilers in attack. I chose the game Steamhammer-MadMixP to show how, in the best case, Steamhammer can keep its defiler active through a long attacking sequence. The game itself is not very interesting, but watch the finishing attack which starts at around 17:30 from across the bridge to the terran natural.

the finishing attack begins with a plague

It starts with a plague. If I didn’t miss anything, the sequence that follows is consume, consume, swarm, consume, consume, consume, plague, consume, consume, swarm, consume, consume, swarm, consume, consume, consume, swarm, consume, plague, consume, consume, consume, plague, consume, consume, consume, plague, consume, consume—and the last enemy building is destroyed. The defiler was stuck behind friendly units for short stretches, and otherwise busy every moment supporting the raging attack. Even though not all the activity was useful, the ability to keep constantly active is valuable.

blunders

Steamhammer does make mistakes in casting defiler spells, though rarely serious ones. Plague doesn’t account for moving units. It can miss fast-moving enemies altogether, for example trying to plague a group of corsairs and smearing only bare ground. In an active fight it can unintentionally plague its own units, which do not realize that they are moving into a danger zone. (It’s also perfectly willing to intentionally plague its own units, if that lets it splash more enemies. The calculation simply adds damage done to enemies and subtracts damage done to self.)

Dark swarm is thorny. Steamhammer only partially understands it. In Randomhammer-tscmoop2 the defiler cast a swarm that helped the enemy—with hydras on hand versus zealots and a reaver as well as the cannons and dragoons, swarm was a poor idea in the first place, and this swarm placement defends the enemy and does not open a path to attack. Oops. The research tab shows that plague was almost but not quite finished.

swarm protects enemy units

improvements needed

Why is Steamhammer weak with defensive dark swarm? To lay down swarm over lurkers and hold a position, or to force marines back with zerglings under swarm, you have to coordinate the swarm with the combat units. The defiler knows that swarm over lurkers is a great idea, and the combat simulator knows it too, in an approximate way. The lurkers themselves have no inkling; when their targets retreat, the lurkers will pick themselves up and obliviously step out of the swarm and die. Even something as simple as rendering hydralisks invulnerable to air attack is beyond Steamhammer. The hydras pay no attention.

Steamhammer’s squad code is already fragile from having too many skills tacked on, and needs to be rethought and rewritten. I’m reluctant to add complicated coordination skills before tackling that, so this weakness will likely be around for a while. It is a severe weakness, though, because defense is a critical use of defilers. Darn it, but there are times when only invulnerability can save you.

Besides the lack of coordination, there is no planning ahead. Defilers live in the moment and know nothing beyond. With prediction and planning, greater things could be accomplished with fewer resources.

More basically, the tendency of the defiler to get stuck behind friendly units reminds me that Steamhammer needs another fundamental skill: Smooth unit movement. Human players know that the defiler’s position is important, and move other units so that the defiler gets where it needs to be. And more generally, the missing skill shows in things like awkward movement through choke points, and clumsy collisions when squads cross paths.

I have a few simple ideas for how to control multiple defilers at once without blowing out cpu usage or seeing them simultaneously plague the same enemies. I expect I’ll implement that before SSCAIT this year. With 2 or 3 defilers in the Ground squad, defenders won’t have time to draw breath between spells.

Someday I’ll implement defiler drops. Zerglings with adrenal glands upgrade might as well be wrecking balls. Drop a defiler and some zerglings off to one side, swarm, swarm, and pick up the defiler for another go later while the lings rip down buildings. It’s cheap to do and expensive to defend against.

Steamhammer 2.3.5 source released

I finally uploaded the source for Steamhammer 2.3.5; see Steamhammer’s web page. Sorry about the delay. As I’ve mentioned, this is the same code as the AIIDE 2019 competition version, but configured to play all races rather than zerg only.

One of my steps in uploading a new version is to calculate the performance numbers of the previous version. By SSCAIT win rate, Steamhammer 2.3 (from last April) with 69% wins is the most successful version since Steamhammer 1.2 with 70% wins, way back in March 2017. And I’ve seen that the following test versions are stronger yet. This version 2.3.5 (and the tournament version 2.3.4 since it’s identical) has a new combat sim bug which makes it slightly weaker than the previous test version, 2.3.3, but I think it is still stronger than version 2.3. Steamhammer is doing well at the moment.

Before long I plan to release a new point version which fixes the combat sim bug and (just for fun) adds new queen skills. After that I’ll dig into strategy adaptation, which I expect will cause strength to plummet, because that is what major new features do at first. When strength recovers, though, it will recover to a higher level.

Next: A long post about defiler play, with examples and explanations. I also found a few research papers that I want to write up.

Steamhammer 2.3.4 change list

The change list is longer than I originally predicted! This is the Steamhammer version that I submitted to the AIIDE 2019 tournament. The list includes changes from unreleased versions 2.3.1, 2.3.2, and 2.3.3 that played on SSCAIT for testing, as well as final changes in version 2.3.4 that never played on SSCAIT.

This version is configured to play zerg only, and all changes affect zerg play. A few of them should also improve play by terran and protoss. As usual, the most important changes are in bold.

opponent model

Support pre-learned data files. If Steamhammer does not find a learning file for a given opponent in the read directory, it looks next in a “static” directory to see if there is a data file for the opponent there. The config variable Config::IO::StaticDirectory points to the static directory, and is set to bwapi-data/AI/om/ in the release version (“om” stands for “opponent model”). After the game, it proceeds as usual: It writes the pre-learned game records, followed by the game record for the current game, into the write directory; after write is copied to read, there is no reason to refer back to the static directory. Locutus does it essentially the same way.

The AIIDE version of Steamhammer includes pre-learned opponent model files for 12 of the registered bots. Most of these files don’t have much in them beyond a few sample strategies that have won in the past, so that Steamhammer doesn’t have to cast about blindly to find a likely build. Best play against Iron is more complicated—Steamhammer knows 3 very different successful openings with varying win rates depending on the map—so the pre-learned file for Iron includes 50 games to give Steamhammer an excellent winning rate right from the start. All the game records were originally extracted from Steamhammer’s learning files on BASIL.

operations

Squad targeting: In deciding what enemy base to attack, count defenders in a smaller radius, unless the enemy is terran and has siege mode (then we need the wide radius to include any tanks that may be sieged). This helps Steamhammer more accurately choose the least-defended enemy base to attack next.

• There was a copy-paste error in the code to detect whether the enemy has siege mode. The siege mode information was returned correctly, but it mistakenly set the flag that means “the enemy has static air defense,” so Steamhammer would believe there were turrets whether there were or not.

• Squad targeting: Don’t order a squad to attack an addon. I changed ranged units to never attack addons, to avoid wasting time when they should be attacking something else. (Melee units will, though, because it may be the closest thing.) That unexpectedly caused difficulties in finishing off games versus terran, when units might collect around an enemy addon and never attack it, rather than attacking a known building that was perhaps farther away.

• Squad targeting: If no enemy buildings are known, seek enemy units which may still be findable. This can help Steamhammer finish off winning games faster.

A terrific bug caused enemy unit counts to be horribly wrong in many cases. Steamhammer sometimes believed that the enemy had a negative number of units of some type. The broken unit counts were not used by many decisions—the most important decisions use the player snapshot unit counts, which are accurate—but decisions that did use them could be completely wrong.

UnitInfo projects when enemy buildings will complete, based on their HP when last seen. It’s not entirely accurate; the calculation leaves out some factor that I haven’t figured out yet. To see the calculated times on the screen, turn on the config option Debug::DrawEnemyUnitInfo. In code, the call getEnemyBuildingTiming() is one way to get the information. This data could make plan recognition more accurate, but is not used for that yet; it is used only to check the enemy spire timing in ZvZ.

tactics

Calculate map tiles in range of enemy static defense and keep the data available. There are separate grids of ground danger zones and air danger zones. A few uses of the data are implemented; many more are possible. Steamhammer will not place buildings in a ground danger zone; this is a vital skill to survive cannon rushes, and has saved games for Steamhammer against AIUR by Florian Richoux. The information also feeds into unit targeting decisions. Steamhammer’s units prefer to fight outside enemy static defense range when possible; Steamhammer has a stronger preference to pick off undefended targets.

Squad detectors avoid danger and seek cloaked enemies. Formerly, detectors stayed with their squads and otherwise ignored the world around them. Steamhammer lost overlords freely, a major weakness. Now they retreat out of range of enemies that could shoot them down, and move toward cloaked enemies that need to be detected. Science vessels and observers benefit too. It’s usually a big improvement, but there are cases where enemy ranged units zone out the overlords, or enemy cloaked units distract them from where they need to be. Overlords that are not acting as squad detectors (some act as base detectors and some are left idle) are as carefree as ever and can still be lost at a high rate.

• A squad detector is supposed to move back home when it finds itself abandoned—when the squad it was detecting for is gone. I don’t think this feature works, or at least, detectors still make all sorts of illogical movements.

Better combat sim results against cloaked enemies. If all enemies in the area are undetected and they can hit us, retreat without further calculation. If some enemies are detected, do the sim to see whether to fight or run. The change makes play vastly stronger in certain situations.

• FAP accounts for cost of scarabs and interceptors, and the full cost of zerg morphed stuff. It doesn’t make a big difference, but combat sim scores are a little more accurate.

• Ranged units are better at ignoring enemy zerg larvas when moving somewhere else. They’ll get distracted less often.

micro

Issue at most 1 move command per 3 frames in the micro system. Zerglings get stuck less often. I still see it happen from time to time, but I think most watchers will see zergling movement as smooth. The underlying code can send move commands to the micro system every frame if it likes, but the micro system will skip or delay some of the commands if they are too closely spaced.

MoveNear() had a potential crashing bug. It never happened as far as I saw, but a unit being given its first command ever could execute invalid code and crash.

• Use MoveNear() rather than Move() in more cases, to reduce unnecessary commanding. MoveNear() ignores commands that “don’t make a difference”—it says, “eh, we’ll end up close enough, don’t bother.” It’s for use when precision doesn’t matter, which is a lot of the time.

• Builder workers were being issued an unnecessary move command on the same frame that they were told to start construction. In the worst case, which was not frequent, building construction might be delayed by 1 frame. Still, it’s fixed.

• Steamhammer’s melee units can be configured to retreat when low on health, to regenerate health (by zerg regeneration or terran medic healing or protoss shield regeneration). The change is, this no longer happens if the unit is irradiated. Net regeneration will not happen, and since it’s a melee unit it’s likely to be right next to the enemy, exposing enemies to the radiation. This mainly helps ultralisks.

• Clip the result of DistanceAndDirection() to the map boundaries, instead of returning a bad result for the caller to deal with. In practice, the effect is that in some situations a unit will move to the edge of the map rather than not moving at all. I’m not sure it’s an improvement in play terms, but the code is safer.

zerg defilers

An outrageous bug could put a defiler into both the defiler controller and the melee unit controller, meaning that defilers were issued zergling commands intermixed with their defiler commands. “Now AttackUnit() that building!” I was floored. The error was in code that was “so simple it could not have a bug,” and the bug was caught by an error check in the micro system, far away from the underlying problem. At least it was easy to fix.

Cast dark swarm over enemy ranged units, not only over enemy buildings. Formerly, Steamhammer could not use swarm against, say, marines on the open field; it had to reach the enemy base before the conditions for the spell were triggered. It was a severe limitation (justified by Steamhammer’s poor swarm skills at the time).

Choose the nearest zerglings as defiler food, not the first zerglings that happen to come up in looping through units. Defilers consume sooner and cast swarm and plague at a higher rate. A defiler generally arrives at the front line already charged up with energy, so even if it is irradiated instantly it is still likely to get a swarm or plague off.

• A typo prevented research of Metasynaptic Node, the defiler energy upgrade. I fixed it and adjusted the conditions to be safer.

• Slightly loosen the conditions for making a defiler in the first place. They work better now, make ‘em sooner.

Swarm and plague scoring adjusted for better effect. The thresholds are reduced, so that the spells can be cast in more situations. Swarm over lurkers counts double. Plague counts more on carrier interceptors, less on buildings. Various other tweaks.

• Fixed a bug in avoiding the overlap of dark swarms. Swarms less often overlap wastefully (though it still happens, for at least 3 reasons).

• FAP bugs in understanding dark swarm are fixed (I added dark swarm to FAP, so all the bugs are my doing). There was an error in recognizing melee units that can hit under dark swarm because of a confusion between range and squared range (I renamed stuff and added comments to make it clearer). FAP also now realizes that a worker cannot do damage under dark swarm, while a reaver can. The effect of the fixes is that units under dark swarm make more accurate decisions about whether to keep attacking.

• A rare bug could allow multiple defiler mounds.

zerg scourge

Do not target an enemy that is covered by enemy static air defense. Steamhammer used to throw scourge away by the ton to cannons or turrets; this solves the issue. Scourge will chase the enemy, but turn around for home if the enemy reaches a defended area. (Mobile enemies might still shoot down the scourge; retreating in that case is decided by combat sim and the regular advance/retreat logic.)

Do not target overlords or interceptors. Shooting down those targets is usually a waste of gas. Neither the scourge squad nor the scourge units will seek overlords or interceptors. This solves another serious issue.

Predict the target’s movement. This helps scourge hit the target directly, instead of doing that weird little loop before suiciding. It also means that scourge cut corners in a turn, but only a hair.

• There was a bug in choosing scourge squad targets that were goneFromLastPosition, that is, known to no longer be where they were last seen. It caused wasteful scourge movement.

• In choosing a scourge unit target, pay less attention to target priority (“the science vessel is more important to destroy than the wraith”) and more attention to target distance (“it’s right here, just hit it!”). That wraith is now less likely to shoot the scourge down while the scourge chases a more distant science vessel.

• If we have lurkers, then enemy observers are higher priority to destroy.

• Further limit scourge production. I tuned it down repeatedly, but Steamhammer was still sometimes spending too much gas on scourge, leaving too little for everything else.

other zerg skills

Sometimes make 1 queen, use parasite, infest command centers. The queen likes to parasite expensive units like carriers and arbiters, and it also likes shuttles and dropships. It will parasite something else if it has excess energy or gets irradiated. As I expected, few enemies understand what to do when they get parasited. A common outcome is that the parasited unit gets scourged at the first opportunity. If the parasited unit stays in a safe place instead, it’s often a valuable spy. The infested command centers don’t do anything but sit there and get in the way; I haven’t yet seen this cause Steamhammer any problems in game, but it could happen. If the queen dies, which happens a lot because it plays recklessly, the strategy boss waits 3 game minutes and makes another queen.

There was a critical crashing bug affecting spellcasters in the late game. Fixed, whew.

In an emergency, set the drone production rate to 0.15 rather than the previous 0.10—that is, make a drone instead of a combat unit 15% of the time. This helps Steamhammer build up its economy even while under heavy pressure, without falling to the pressure. It increases the win rate against zealot rushers and other opponents that press attacks over a long period, like Gaoyuan Chen.

Prefer to make units at hatcheries with more larvas. Steamhammer used to often lose production capacity by using up all the larvas at one hatchery before moving to the next hatchery. A hatchery with 3 larvas will not produce more larvas, so that meant the total rate of larva production was lower, and more total hatcheries were needed to cover the next surge of zergling or drone production (cheap units that call for a lot of larvas).

• Prefer to make drones at undersaturated bases. It is a smaller improvement than it may sound. Formerly, Steamhammer used a cheap but effective trick: It preferred to make drones at the base farthest from the main base. It was the right choice most of the time, but could go wrong if the main was attacked and lost drones. The new method balances workers well in all cases, not only the common case.

• Steamhammer tries to make fewer macro hatcheries before expanding beyond its first and second bases. I saw games where it made as many as 4 hatcheries in its main before taking the natural—it might be OK if the bot is contained, but not the other 6 days of the week.

• Delay the second gas a little. Steamhammer consistently took the second geyser too soon. I think it’s still usually too soon, but the loss is reduced.

• Dark templar more strongly indicate the need for static defense. They hit hard.

• Make a defensive spire in reaction to enemy drop or certain air units. Formerly, only overlord hunters like corsairs would trigger a defensive spire.

• In ZvZ, prefer to counter hydras and especially lurkers with mutas, not lings only. Steamhammer usually did the right thing, but in a few games made a severe strategy mistake.

• In ZvZ, Steamhammer formerly made an evolution chamber if it saw an enemy spire, no matter the state of its own spire. That’s a mistake; the evo is often unneeded, and in ZvZ the drone to make the building is valuable. Now Steamhammer attempts to time the enemy spire and make the evo only if needed. I don’t find it particularly accurate, but it is an improvement.

• In ZvZ, enemy scourge more strongly discourages Steamhammer from making guardians and devourers. See the game I posted last time, where Steamhammer scourged Microwave’s devourers. Devourers and guardians have little ability to defend themselves against scourge; they can only survive with the help of escorts.

• Limit devourers further. Devourers are support units, so try to keep more mutalisks than devourers.

• A lost defiler mound could cause a production freeze. It was rare, but a production freeze can be a game-losing bug. It’s frustrating, but there remain rare production freezes that I have not traced.

• Emergency reactions: When there are 0 drones, Steamhammer can cancel upgrades and more building types. Formerly it could cancel only units and a few building types.

• Emergency reactions: Correctly account for the gain of canceling a building (you get 3/4 of the cost back). It helps in making the right cancelation decisions.

• Emergency reactions: A bug could allow a planned hatchery to be dropped even though it was the only hatchery.

• ZvZ mutalisk target selection adjusted in a few cases.

Lurkers prefer the most distant target. It helps visibly.

• Fixed lurker vibration: Sometimes lurkers would move back and forth, achieving nothing, instead of advancing on the enemy.

• Attackers can no longer permanently drive overlords away from the spore colonies where they are sheltering. This was a bug introduced in a recent version.

zerg openings

3HatchLingBust2 makes the minimum number of drones and goes all-in on the bust. This version is now configured in place of the original 3HatchLingBust as one counter to protoss forge-expand builds.

12Hatch_4HatchLing makes the right number of drones for continuous zergling production from 4 hatcheries. It’s similar to 3HatchLingBust2.

2HatchMutaPure and 3HatchMutaPure each make only 1 pair of zerglings, and instead get as many drones as possible without delaying the mutalisks. The 2 hatchery one is especially elegant. These are for use against opponents that sit back and defend for a time before moving out.

Overpool2HatchLurker and 3GasMuta similarly are more effective against opponents that are late to attack.

DefilerRush rushes for defilers to support highly-upgraded zerglings. I used it mostly in testing defilers, but there are opponents it could be strong against. The defiler rush is similar to the existing HiveRush build, but better optimized (and of course it also makes a defiler).

12PoolHydra is configured as one counter to terran factory-first openings. 12PoolLurker fills a gap in the lineup of lurker openings. ZvZ_12PoolLingB is a minor variant of ZvZ_12PoolLing. ZvZ_12Gas11Pool is a gas-first stem that fills a gap.

Overpool_3HatchSpeed, Overpool_3HatchLing, and Overpool_4HatchLing try to combine the early zerglings of overpool, which can keep the opponent in the dark and defensive, with a mass zergling followup. Overpool_4HatchLing is configured to be played 1% of the time in ZvZ versus an unknown opponent—it’s risky but can also be hard for the opponent to understand and counter (it makes a third base that the opponent is not likely to scout in time).

10Pool9Gas and 10Pool9Hatch fill small gaps in the ZvZ opening repertoire.

7Pool6GasMuta is derived from one of my 7 pool 6 gas dawn lurker rush builds. It’s quite easy to defeat, even without air defense, with an early attack. I suspect it is useful mainly against opponents which want to defend and fail to figure out how to defend so early.

• Minor tweak to 2HatchHydra. Bigger adjustments to OverpoolTurtle so that it can more consistently beat those (weak) opponents it is intended to beat.

Next: Expect Steamhammer 2.3.5, which again plays all races, on Tuesday or Wednesday.

Steamhammer submitted to AIIDE

I have submitted Steamhammer 2.3.4 for AIIDE 2019, a day ahead of the deadline. The latest changes help in specific situations: Better decisions against dark templar, less time wasted when the defiler wants to consume, more accurate strategy choices in ZvZ when the opponent goes hydras, that kind of thing.

Overall, I’m pleased with this version. It is far more solid than any previous tournament version of Steamhammer. There are a couple of game-losing bugs that I did not solve, but they are rare. There are rough edges, but fewer than before. In last year’s AIIDE, Steamhammer was version 2.0, with many new features including unit clustering and defilers—and they were buggy. This year it is version 2.3.4, and the version number is deserved: The strength improvements over 2.0 are due to bug fixes and refinements, not new features.

This means, of course, that it is time to get back to work on version 3.0, with new strategy adaptation features. Add bugs for victory!

Before long I’ll either upload version 2.3.4 on SSCAIT as zerg only, or I’ll come out with version 2.3.5 that can play as any race.

By the way, I counted the number of zerg openings. There are now 142.

Steamhammer-Microwave rivalry

Microwave was forked from an early version of Steamhammer, and the two have been struggling against each other ever since.

• AIIDE 2017 - Microwave 70.86%, Steamhammer 64.14%
• SSCAIT 2017 - Steamhammer 83.12%, Microwave 79.87%
• AIIDE 2018 - Steamhammer 54.97%, Microwave 54.16%
• SSCAIT 2018 - Microwave 74.65%, Steamhammer 72.54%

The two bots have different strengths, but most results have been close. Which will edge out the other this time? On BASIL, Steamhammer and Microwave have been trading the top zerg position back and forth. Microwave has an advantage in head-to-head fights, but I think Steamhammer has a slight edge overall. We’ll see how the tournament goes!

Randomhammer (Z)-Microwave is a ZvZ with devourers and defilers. Microwave wins the majority of games, but Steamhammer has better hive tech skills.

defiler bugs to the horizon

I wasn’t planning to work more on defiler play for the time being, but then I discovered two more sets of bugs affecting defiler play. Since defilers are bugs, does it follow that you can’t have defilers without bugs? First, my additions to FAP to understand dark swarm had multiple errors in determining which units can hit under swarm. That could cause Steamhammer to cast swarm over its units, then needlessly retreat from under it. Second, there was an unbelievable bug which assigned defilers not only to the defiler controller, but to the melee unit controller at the same time. In between their regular orders, defilers were being interrupted with nonsense orders to act like zerglings. How did defilers ever work? Because in the buggy live version, defiler play is, somehow, already valuable.

Rule of thumb for the upcoming tournament version: If Steamhammer survives to the late game and has defilers out, it is probably winning.

Steamhammer 2.3.2 with bug fix

The test paid for itself! Version 2.3.1 has a crashing bug that can strike replacement defilers. I got two crashes on SSCAIT, though strangely none on BASIL. I’ve uploaded Steamhammer 2.3.2, which fixes the bug—I hope it’s the only new defiler bug. Also in the new version are a micro improvement for smoother maneuvering, plus tweaks to plague usage.

The tournament test versions are configured to play zerg only.

I’ve seen a few games with queens and some parasites cast, but Steamhammer hasn’t infested a command center in public yet. I’m sure it will before long. It leaves the infested command center sitting there, blocking the base. I’m curious to find out whether that causes more problems for the opponent, or for itself.

defeating undetected enemies

An enemy lurker or dark templar is in your base, and you have absolutely no form of detection nearby. Can you defeat it anyway? In many cases, yes.

An enemy unit can be in sight range but undetected if it is burrowed—a spider mine or a burrowed zerg unit—or if it is cloaked—a wraith, ghost, dark templar, observer, or a unit under an arbiter’s cloaking field. I’ve written before about countering spider mines, so I’ll skip that.

burrow versus cloak

When a cloaked unit is in sight, a bot knows where it is. Without detection the cloaked unit cannot be targeted, but BWAPI gives its type and position.

A burrowed unit does not give itself away. If the bot did not see the enemy burrow, and did not detect it since it burrowed, then BWAPI does not reveal that it is there. Exception: A lurker can give away its position when it attacks. The lurker spines are a bullet, in BWAPI parlance. However, if you try to build on top of a burrowed unit, construction will fail. Many bots use that fact to infer that a spider mine is blocking an expansion position, but the principle is general. In theory, you could locate burrowed zerglings by starting buildings in different positions. It’s rare, but I have seen a game where hold lurkers waiting in ambush in an expansion were discovered by trying to build a turret before any mining SCVs arrived.

spider mines

Spider mines will attack cloaked enemies. They are popular for fending off dark templar.

splash damage

An undetected unit can’t be targeted, but it can be hit by splash damage. Psionic storm is the simplest way; burrowed and cloaked units are affected like any other. Nuke may be overkill, but is effective.

An example: For protoss, the most common way to deal with a single undetected lurker is to park a friendly zealot on top of the lurker and attack the zealot with an archon. The zealot will remain stoic as it takes full damage, and the lurker will take splash damage. You may need 2 zealots to finish off the lurker. Skynet by Andrew Smith has this skill, and I wrote it up in 2016.

Any unit or spell that does splash damage will work—well, it has to do ground splash for ground enemies or air splash for air enemies (archons do both ground and air splash). Besides archons, popular choices are reavers, lurkers, and sieged tanks. Corsairs and valkyries are good for air splash. Firebats and infested terrans can theoretically work, though I’ve never seen it done. You can also do the eraser: Irradiate a unit (preferably an air unit) and keep it next to a dark templar or ghost—but then, if you have a science vessel, you have a detector, so the eraser is for when you have no other forces around. Spider mines do not attack friendlies and are the only splash damage unit that cannot be used, but they do attack enemies so you don’t need to. Mutalisk bounce does not count as splash; glaive wurms do not bounce to undetected enemies. Also devourer acid spores do not splash onto undetected air units—they will reveal a cloaked unit that they are attached to, but they can only attach to a unit that is detected.

Also, any target that will splash onto the undetected enemy is good—anything directly next to the undetected enemy. Sometimes you can target a building or an enemy unit.

revealing cloaked units

The queen’s ensnare and the defiler’s plague are area effect spells that work on cloaked units, though not on burrowed units. Besides affecting cloaked units, the green or red goo reveals cloaked units so that they can be targeted.

EMP

Wraiths and ghosts need energy to cloak. Hit them with EMP, and they are forced to become visible. Of course, the science vessel can also directly detect them. EMP might be worth it if the vessel is at risk (perhaps from the wraiths, or perhaps from ghost lockdown), or if one EMP can hit a group of enemies which will then have to build up energy to cloak again.

drilling cloaked units

Sometimes you only need to gain a little time before you can deal with the cloaked enemy. You can delay the enemy if you can force one of your units to overlap with it; both units will move randomly, unable to act, until they separate. Zerg can do this by unburrowing from under the unit, or by turning a larva into an egg when the unit is very close to the larva. All races can do it with a worker drill: Send workers to a mineral patch so that their path will take them through the position of the enemy—mineral walk through the enemy—then stop or attack when they are overlapping.

Any other techniques? I won’t be surprised if I forgot a few.

the third defiler bug

Looking closely at the newly implemented queen play, I noticed a bug that caused delays in action. The queen would spot a good target and prepare to launch its parasite... then lose track of what it was doing and execute a move instead, find the good target again, etc. I fixed it to keep proper track of its intentions, and suddenly the queen’s actions were prompt to the point of being overeager: It would move into range and cast its parasite, then before the parasite landed, parasite the same target again! I had to fix that too; it was not enough to stick to targets which are not already parasited. Then I realized... oh... this must also be the long sought Third Defiler Bug. Queens and defilers have their own code, but they work about the same way.

Last fall I added defiler support. In the winter I fixed two serious bugs (see the zerg section) in defiler control. And I suspected that there was a third one, because defilers still reacted sluggishly. This is the third bug—I finally found it.

When the queen fix is properly generalized for defilers, I expect that defilers will start getting stuff done. As ground units they will still meander into traffic jams and struggle to coordinate with their food to consume, and dark swarm (which is extremely hard to use well) will still be restricted to narrow circumstances. Even so, I expect defilers to be much more active, more nearly what they should be.

So, as usual, the next release should have more improvements than planned.

Parasite works moderately well. On average the queen does not really pay for itself, but it’s a small expense (no research and the queen’s nest is there anyway), and I expect it will pay off in a few games. If the queen is lost, the strategy boss waits 3 game minutes before ordering up a new one, so even if the queen gets irradiated instantly it won’t be a big drain on resources. And every once in a while it can infest a command center.

Steamhammer 2.1.4 change list, SSCAIT 2018 version

The version 2.1.4 change list also includes the changes in the unreleased test versions 2.1.1 through 2.1.3. I rolled them all up. A lot of the work in 2.1.4 was fixing bugs that I introduced in the test versions. Expect source release tomorrow, if I find the energy.

The most important improvements are in bold.

UnitInfo

UnitInfo was inherited from UAlbertaBot. It is responsible for keeping track of enemy units that may be out of sight.

• Keep track of burrowed units, both zerg burrowed units and spider mines. Formerly, when a unit burrowed, Steamhammer thought “oh, it disappeared from its last seen position” and lost track of where the unit was. Now the units are tracked and get passed to the combat simulator, and could be used for other purposes—”mines are ahead, send an overlord now!” It’s a little tricky, by the way. If you detect a burrowed unit then you detected it, but if detection is not available and a unit disappears, then why? When a zerg unit burrows, it has the order Orders::Burrowing, so you can distinguish a burrowing unit from a unit that merely walks out of sight. But a spider mine has the order Orders::VultureMine no matter what it is doing. So I simply marked spider mines down at the position where they were last seen, and it works accurately so far though it could be wrong in rare cases. BWAPI doesn’t provide “a cloaked unit in this position would be detected if it were there, therefore it’s not there,” so for now I ignore the case of a burrowed unit which moved away or was destroyed while out of sight. At some point I’ll add a feature to cover BWAPI’s lack.

• I renamed the field lastHealth to lastHP, since that’s what it is. I decided to use “health” to mean HP + shields.

• Steamhammer 2.0 added UnitInfo::estimateHealth() to estimate the health of an unseen unit, accounting for protoss shield regeneration and zerg HP regeneration. Terran repair and medic healing are not so easy to predict. This version adds separate estimateHP() and estimateShields() for use by the combat simulator.

The HP and shields of an undetected enemy are 0 because the enemy unit is not detected (easy to understand, right?). Steamhammer formerly took it literally and did not pass an undetected enemy unit in sight range into the combat simulator, because a unit with 0 HP is paper. I had assumed that Steamhammer was weak against dark templar because FAP doesn’t support cloaking and detection, but it was deeper than that. I fixed UnitInfo and the estimators to assume that a visible but undetected enemy has full HP and shields. Hmm, maybe a better fix is possible?

combat sim

• Use estimateHP() and estimateShields() for a more accurate representation of the starting situation. Formerly, for unseen enemies, the combat sim used the last known HP and shields (if the HP were not incorrectly 0 for an undetected enemy, as mentioned in the previous bullet). The estimates take into account regeneration since the units were last seen. Cloaked units are understood much better, though FAP still doesn’t understand that it can’t hit them without detection. Otherwise, the estimators rarely make a big difference.

Mutalisks versus spore colonies situations play adequately again. A bug was introduced in version 2.1.

Combat sim is centered on the nearest enemy rather than the frontmost friendly unit. UAlbertaBot provided a system where it picks one of its own units as the vanguard of its force, draws a circle around the vanguard, and includes everything that can fire into the circle in the combat sim. When our force moves forward, more enemies are included, so we may retreat, causing fewer enemies to be included, so we advance, etc. Steamhammer 2.0 changes this to include one cluster of friendly units (based on the unit clustering algorithm) in the combat sim, plus the enemies in the circle. Steamhammer 2.1.4 now centers the circle on the nearest enemy instead of the vanguard friendly unit. As friendly forces move back and forth, the set of enemies often stays the same. It greatly reduces vacillation and unit suicides. The circle is also smaller, to encourage aggression.

• Bruce @ Locutus pointed out a FAP bug confusing ground and air units in unitsim(). I fixed it, and it helps... to a limited extent. It’s a severe bug, and I expected a bigger difference.

• The FAP unit field airMinRange is always 0. I removed it and all its uses. Since it was tested in an inside loop, all sims involving air units run a trifle faster. The groundMinRange affects sieged tanks, so it has a use.

• Units that are under maelstrom (detail stolen from MCRS) or under disruption web are excluded from the combat sim by UnitUtil::IsCombatSimUnit(). A dwebbed unit could move out of dweb, but will it? The combat sim doesn’t understand it.

• The whichEnemies is reworked and completed. It specifies which types of enemies should be included in the combat sim, which makes a difference because Steamhammer decides the result by the unusual but successful criterion “who has more stuff left over at the end?” I renamed AntigroundEnemies to ZerglingEnemies because, believe it or not, it’s clearer, even though it doesn’t apply only to zerglings. You’re a zergling enemy if you’re on the ground (a zergling can hit you) or you’re in the air and you can shoot down (you can hit a zergling). A corsair is not a zergling enemy and is excluded. I added GuardianEnemies (which is unused) and DevourerEnemies (used) to complete the set (AllEnemies was already there). The CombatSimulation setup class calculates all the exclusions more accurately than before to pass the right units to the combat sim.

• Steamhammer scores the combat sim by unit prices. Some units have deceptive prices that don’t represent their value. For example, BWAPI arbitrarily says that a spider mine has mineral cost 1. I made special cases for deceptive prices.

micro

• Nearly all micro actions now have bookkeeping in MicroInfo, and the action of moving (which is when units get stuck) is handled completely by the MicroInfo system. Any move commands are now carried out at the end of the frame, after additional checks are done. I think units get stuck less often, though with more experience it becomes harder to see; maybe I’m fooling myself. At worst, the extra bookkeeping will make it easier to get units to follow their orders. On the downside, some bugs in the previous test versions were caused by failing to record changed orders in the MicroInfo system. I believe that this includes the recent production freeze bug, which I hope is fixed now. I should figure out how to avoid the risk of this kind of bug; I will have to change something.

DistanceAndDirection() is corrected and simplified. It’s a utility in Common.cpp which takes a base point, a direction point, and a distance. The direction point specifies a direction from the base point, and the routine returns a point at the given distance from the base point in that direction (it’s scaling a vector). The distance can be negative. There was a basic error: It calculated the (x, y) offset from the base point correctly, but forgot to add the base point to the offset, so the code looked right when I read it but the result was completely wrong! Since I was touching it anyway, I also simplified the code.

• A ground unit which finds itself directly next to an undetected enemy dark templar will try to flee away from the DT (using DistanceAndDirection()). The DT has to work harder and some units escape danger, but many units still get hit, especially if there is more than one DT. A disadvantage is that the fleeing units get disorganized and work together even less well than usual.

• Guardians are kited by the same code as mutalisks. It reduces cases where they marry a target and refuse to switch to a better one.

operations and tactics

• Attempt to retreat behind static defense, instead of stopping in front of it. It’s not entirely successful, but it seems to help some.

• There are a number of changes to base defense. There is a minor rewrite to simplify one part and improve efficiency. The enemy scout worker is no longer ignored; any enemy in the base is now reason to form a Base squad and clear the base; it helps deny enemy scouting. When deciding on how many defenders to assign, Steamhammer now weights enemy workers more lightly and certain tough enemy units more heavily, so the squad size should be more appropriate to the threat.

• The base defense squad also assigns a detector under narrower conditions, which ameliorates one major cause of mass overlord suicide. Unfortunately there are other causes.

• Don’t assign a detector to an otherwise empty squad. There was a loophole.

• Fixed a minor bug in dropping the empty Base squad of a destroyed base. It had no important effect. I think there is still one more case where dropping an empty squad does not happen as intended.

• For most of Steamhammer’s life, it has been the case that a melee unit next to a sieged tank does not retreat, but attacks the tank instead. I extended it slightly: If the tank is in the process of sieging or unsieging, the unit also does not retreat. It’s a little more insistent about hitting the tank while it can.

• For purposes of operations targeting, a refinery building is considered always reachable by ground. It’s one of the 4 cooperating bugs that I hit recently. This is, of course, a workaround and not a fix. If Steamhammer becomes aware of a refinery on an island....

early-game scouting

• In the scouting code that Steamhammer inherited from UAlbertaBot, a potential enemy starting base is considered scouted and unoccupied if we have explored the TilePosition where the enemy resource depot would be, and nothing is there. But that’s a little inefficient; it’s the position of the upper left corner tile of a building which is 4x3 tiles in size. If we are approaching from the right, say, then it’s one of the last tiles of the building location that we see. So now Steamhammer can recognize a base as unoccupied if we have scouted any of the 4 corners of the building location. The early game worker scout sometimes saves giant fractions of a second in finding the enemy. Someday I’ll add creep recognition too, which will save a useful amount of time when scouting a zerg enemy.

building construction

Expansion hatcheries could be mistakenly reassigned as macro hatcheries due to a bug in the building manager. The advent of this bug is what caused Steamhammer to so often expand slowly in the early middle game. Steamhammer’s traditional damn-the-torpedoes-take-the-map attitude is restored.

Buildings were often re-placed after their initial placement due to a subtle interaction bug that has been around since the beginning. This is what caused the spawning pool drone to move into position, stop, then move to another position before starting the pool. The bad behavior: The production manager notices that the pool is coming up, asks where it should be placed, and moves a drone there to pre-position it, trying to arrive just as 200 minerals become available. Then the building manager runs the placement code again, rejects that position because it is blocked by a drone, chooses a new position, then assigns the nearest drone. Now the building manager assigns the drone first, then places the building, usually keeping the same location because a drone does not block its own building—that’s the sneaky interaction. In most cases, but not all, the closest drone can be assigned to the building because the building was already placed by the production manager. The bug was hard to understand because its 3 parts are in 3 different files. At some point I’ll figure out a way to place the building only once unless a problem occurs; that should avoid the remaining problems. Anyway, the effect of fixing this is that buildings commonly start a little sooner with less wasted drone motion, which can help a lot if one side or the other is rushing.

opponent model

Checking for enemy proxies had a serious bug. If the location of the enemy base was known, the check did not run at all! On a 2-player map, the enemy base location is always known. That is why Steamhammer lost so many games against Juno by Yuanheng Zhu under the false belief that its opponent was playing a Turtle strategy rather than a cannon rush Proxy strategy. In fixing this, I moved proxy checking from the information manager to the Bases class, and (with the extra info that class makes available) extended it so that it now checks both the main and natural for enemy proxies. When I first wrote the code, no bots proxied to the enemy natural and checking your main was enough.

terran

• I had to touch the tank code to update it for MicroInfo, and I couldn’t resist a tweak. Tanks siege and unsiege too often, and I cut away a tiny bit of the stupidity: An unsieged tank does not siege if faced with a single enemy melee unit.

zerg

A serious bug in defiler control caused defilers to jitter back and forth seemingly randomly instead of moving to the front where they are wanted. Fixing it also makes the defiler code run substantially faster; there is little risk of overstepping the frame time in the late game due to defilers. This is the second serious defiler control bug I fixed; version 2.1 had the first fix. Defilers are still not active enough—is there a third serious bug?

• A minor bug could prevent a dying defiler from casting one last plague.

• Try harder to avoid making a duplicate or unnecessary defiler mound. It still happened in 1 test game; I don’t know how.

• Even in an emergency, research consume for defilers. That’s when we need it most!

• After an emergency spawning pool because the enemy is rushing or proxying, save larvas so we can get as many zerglings as possible right when the pool finishes. Steamhammer has lost games by making drones while waiting for the pool to finish.

• If the enemy played a fast rush, be more cautious about expanding to the natural. It might not be safe.

• Make emergency zerglings in response to the enemy ground army, not the enemy total army. I saw games where Steamhammer made emergency zerglings to defend against mutalisks, and found itself surprised that it didn’t help.

• I saw Steamhammer lose a number of ZvZ games by making all zerglings versus mutalisks. It’s not always a mistake, but if there are enough mutas and they don’t let themselves get too far out of position, then the lings will do nothing but die (since they don’t know how to scatter or hide). I fixed it to do that less often.

• Emergency sunkens are allowed even if they may not finish before the enemy attack arrives. Steamhammer was trying too hard to avoid overdefending. They are still not allowed if the enemy is rampaging in the natural—most bots will build sunkens then, but to me Steamhammer looks too awkward when it tries, and loses too much. This change involved adding to the “emergency” state a separate “EMERGENCY NOW!” state, which is updated independently.

• Make macro hatcheries faster when there are more drones. It’s a crude rule, but it should improve macro a modest amount.

• Steamhammer has continued to have the problem of overproducing scourge, using up all its gas and delaying other production that it needs. I keep tightening limits, and they seem never tight enough. I added another limit: A total of 12 scourge are allowed to be alive at any one time.

• If we’re maxed, trim the production queue to keep it short. In the late game, Steamhammer likes to put a lot of items in the queue at the same time, because it has the larvas and the resources and it can make them all nearly simultaneously. But when it reaches supply max, it can’t make things quickly any more. The long queue prevents Steamhammer from reacting to changes; it has to work through the whole queue, losing one unit before it can produce the next one, before it empties the queue and reconsiders the situation. Now Steamhammer ruthlessly prunes the queue to a couple items, so it can react and do the most important things first.

• Recognize that zerglings are weak versus dark templar. For some reason, this tidbit of knowledge never made it into the unit mix scores.

• Be more willing to add a second spore colony if the enemy makes multiple scouts. Scouts are not that dangerous... if you actually react to them.

• Don’t try to make a spore colony in the natural base when we don’t own the natural base. This generally causes the spore to be built at the edge of the main base closest to the natural, which is rarely helpful.

• The preferred army size in ZvP is tweaked upward (I adjusted a parameter from 0.60 to 0.65). I concluded that sometimes Steamhammer simply does not make enough fighting units.

openings

• I added 5HatchBeforeGas. It cheats and makes 1 extractor just before the 5th hatchery and 1 after, to avoid a bug that comes up when trying to build 2 extractors simultaneously (the building reservation system is tile-based and doesn’t work in that case, because all geysers are non-buildable for buildings other than a refinery, so it tries to place both extractors on the same geyser).

• I added 8Hatch7PoolSpeed, another rush opening. It’s not configured to be played, and it takes Steamhammer many games against an opponent before it experiments with unconfigured openings, so you won’t see it during the tournament.

• I added a strategy combo AllIn which collects the all-in attacks, such as 8Hatch7PoolSpeed above. It’s not used for now. I’m thinking that exploring all-in openings should be a phase in the exploration program that happens before exploring all openings without restriction. In the meantime, those who wish to play with their own copies can configure AllIn openings for use if they like.

• The 3 hatch mutalisk openings now build a 4th hatchery before mutalisk production starts. The hatchery finishes around the same time as the opening’s last mutalisks. The opening was tuned when I first wrote it, but with mineral locking introduced in version 2.0, it accumulates excess minerals. Steamhammer struggled to recover in the middle game from the macro imbalance of the opening. Mineral locking can make a huge difference in macro openings.

• The usual minor tweaks to openings and probabilities.

configuration file

• If the strategy boss debug option is turned on, you get separate red “emergency” and “EMERGENCY NOW!” indicators. They are independent; either can occur without the other, or you can get both at once. “PANIC” didn’t seem like the right word. Maybe I should call it “DOWN IN FLAMES”?

• A new debug option DrawHiddenEnemies draws remembered positions for out-of-sight enemy units. It was described in this post on Steamhammer 2.1.1.

• There was an accidental duplicate of “Counter Naked expand vT”. Dropped.

The next release should be Steamhammer 2.2, according to my plans as of today (ask again tomorrow). I’m low on energy and feel like skipping an interim 2.1.5 release that fixes the terran and protoss bugs. The headline feature of 2.2 will be that BWTA is dropped and Steamhammer does all its own map analysis. I hope it will be done in January around the time SSCAIT finishes. I want to drop BWTA and upgrade to BWAPI 4.2.0 as separate steps, so that I know what to attribute bugs to. Moving to BWAPI 4.2.0 might be version 2.2.1—I am looking forward to being free of the zerg bugs of BWAPI 4.1.2.

Next: Tournament prospects. Some time after that: CherryPi and SAIDA analysis from AIIDE 2018.

Steamhammer 2.1.1 status

Posts take time, but I am also making progress on Steamhammer. The next version will have no big features, so I’ll call it version 2.1.1. Big stuff has to wait until next year, after the tournament season. I found a second serious bug in defiler control, and now defilers consistently move to where they are wanted. It helps them swarm and plague more actively, though they still don’t do as much work as I would like. So far, 2.1.1 development version keeps track of burrowed units accurately, recognizes enemy proxies better, wards off the enemy scout worker more reliably, and has some improved macro decisions and emergency reactions, a new opening (of course), and a variety of other fixes.

There are a bunch of debilitating bugs in squad control and micro. For upcoming work, I have my eyes on 2 bugs in particular that I think cause the most frequent setbacks (rather than the most glaring blunders), the suicide pokes and the stuck units. If I get those fixed in time, I have a priority list of more stuff. If I succeed, Steamhammer will play better in nearly every game, which should show in the SSCAIT round robin phase.

Steamhammer 2.1 change list

Steamhammer 2.1 is uploaded. The headline feature is that terran and protoss play acceptably well again, unlike in version 2.0, so Randomhammer is updated. Zerg should also play significantly better: Zergling micro is fixed, scourge are more effective (I hope), and defilers are more active.

I reset Steamhammer’s learned data for version 2.0, because 2.0 plays differently. Since then it has learned enough to play its best against some opponents, but it is still short of equilibrium. It reached final elo 2164, which is probably lower than its elo if it had enough games under its belt. Version 2.1 is not that different from 2.0, so it is continuing with the same data.

I reset Randomhammer’s data for 2.1, since the existing data is for version 1.4.7. It will have to figure its opponents out from scratch again, which will take months.

Stand by for source. I’m way behind in updating Steamhammer’s web page. Here is the change list:

opponent model

• Fixed a bug in InformationManager that could fail to recognize an in-base proxy.

macro

• Configured WorkersPerPatch to 2.0 for terran and protoss, to reflect mineral locking. Mineral locking helps terran and protoss over zerg, because bases tend to have more workers. Reducing the maximum worker count per base while maintaining full mining efficiency means that much more cash and supply to spend on tech and army.

tactics

• Medics and tanks act in clusters. They are controlled by their own code, which needed updating.

• Bug fix: A cluster of all medics which happened to reach the front line would then continue to advance heedlessly into the enemy position and die. Now it falls back toward the base—normally it will soon merge with another cluster that is on the way. To implement this, I split the former “no fight” case into 2 cases, “I have no fighting units” and “there are no enemies nearby,” which behave differently. All medics is an example of no fighting units.

• Steamhammer knows a number of places that a cluster of units can retreat to. I moved “retreat to join up with another cluster” (which can be an advance rather than a retreat) ahead of “retreat to the location of a cluster unit which is not near the enemy” in priority order. It helps clusters merge a little more often.

• When there are many clusters, Steamhammer saves execution time by not updating all clusters every frame. It divides the clusters into “phases” and updates one phase per frame. In Steamhammer 2.0, the phases were calculated incorrectly, which contributed to bad micro (though it wasn’t the biggest cause). Fixed.

combat sim

• Combat sim scores are based on the cost of units, not the destroyScore, because destroyScore is sometimes strange. For now I set score = mineral cost + gas cost.

• Steamhammer uses the combat sim scores in an unusual way to decide who won the simulation: It’s the side that ended up with more stuff surviving. It seems illogical, but it tested better than alternatives I tried, such as the side that lost less. Still, there are pathological cases where it gives a wrong answer. I fixed this pathological case: If you lost nothing, then you won the simulation, even if you finished with less stuff surviving. (If the other side also lost nothing, then you still won, because a draw counts as a win.)

• I also changed the units included in the combat sim in special cases. 1. If you have nothing but ground units that can’t shoot up, then ignore enemy air units that can’t shoot down. Because the side with more surviving stuff wins, this can affect who wins the simulation. This fixes another pathological case, where for example zerglings might run away from corsairs. 2. If you’re scourge, include only ground enemies that can shoot up. Scourge are never afraid of the air units that they want to destroy, only of useless death by ground enemies.

At some point I’ll add the other natural special case, for air units that can’t shoot down. All this combat sim stuff could be way more sophisticated....

micro

• I fixed the biggest cause of poor micro in Steamhammer 2.0. As part of choosing an enemy target, the melee and ranged unit controllers called CanCatchUnit() to see whether the enemy unit would be able to escape if chased. It was meant to reduce goose chases. Anyway, the results of CanCatchUnit() are apparently wrong. I haven’t looked into what the trouble was, because I found that removing the calls had no effect on goose chases—long chases have become rare due to other changes. The error caused units to overlook targets that they could, and often should, attack. Zergling micro became weak, and zealot micro was worse. It’s all back to normal now.

• Vultures and wraiths would become fixated on their targets, unable to switch away even to retreat in an emergency. Fixed.

• Like clusters, defiler actions are divided into phases. There was a bug in coordinating the cluster phases and defiler phases, so that defiler actions might be skipped for a long time depending on the phase of the cluster the defiler was in. Fixing it makes defilers more active, though they still don’t swarm or plague as often as they should.

• Scourge is allowed to spread out more in regrouping. Mutalisks should usually group tight, scourge should spread out some. Formerly, all air units behaved the same.

• I made a couple adjustments to defiler plague. 1. Plague on a building was formerly worth 0.6 of plague on units with the same hit point loss; I changed the discount to 0.3, half as much, so that defilers will try harder to plague units. Buildings have a lot of hit points and threaten to dominate the scoring. (Static defense buildings are treated the same as mobile units, though.) 2. Plague gets a bonus for carrier interceptors, to exploit the plague-on-interceptor behavior, but I didn’t see Steamhammer trying hard to plague XIMP’s interceptors (only the carriers themselves). I increased the bonus by a factor of 4.

zerg

• Scourge are in their own squad, the Scourge squad. They behave somewhat better in my tests, but it’s primitive and they still have a long way to go. I mentioned a couple other improvements to scourge behavior above. It was surprisingly difficult to get scourge to do anything sensible.

• Get an evolution chamber and a spore colony for air defense when needed even if still in book. Steamhammer formerly waited until the book line came to an end before it dared defend itself. I think this will be a net gain, though it will make mistakes sometimes.

• Tweak: Enemy dragoons and dark templar loom a little larger as reasons to make static defense.

• Fixed a bug in deciding to get defilers. Battlecruisers are an excellent reason to get defilers; valkyries, not so much.

• Strongly avoid spawning mutalisks versus large valkyrie counts. Valkyries in numbers pass through mutas like they’re hardly there.

openings

• Fixed a typo in the opening name Over10Hatch2SunkHard in the AntiZealot strategy combo. When this opening was selected, Steamhammer couldn’t find it and played its default 9 pool instead, a poor choice against mass zealots.

• Added the zerg opening AntiFactoryHydra, which may be better against SAIDA’s unit mix than Steamhammer's original AntiFactory, and the terran opening 10-10-10FD in a form close to that popularized by Flash. 10-10-10 is an opening stem that gets a super-fast factory, and 10-10-10 FD is a followup that continues into an attack with 2 tanks and 8 marines, which is strong against protoss that techs too hard or expands fast.

• I configured terran and protoss counters to Naked expand by protoss. That means configuring which openings are to be tried as counters to an expected enemy plan. 10-10-10FD should be a good counter to protoss Naked expand.

debug drawing

• In the game info display (drawn in the upper left of the screen when turned on), Steamhammer 2.0 added an overall score versus this opponent, shown next to the opponent name, “2-3” meaning 2 wins and 3 losses. Steamhammer 2.1 also adds a score for the chosen opening, drawn next to the opening name. The context makes it easier to interpret Steamhammer’s choices. The numbers are specific to the matchup. Randomhammer will show different numbers depending on what race it rolled.

• Squads have 2 settable flags, “fight visible only” (only include visible enemies in the combat sim, not all known enemies) and “meatgrinder” (be more aggressive, willing to accept high losses). Visible-only is used by the Recon squad, and meatgrinder tested poorly and is not used. If the squad info display is turned on, it draws cyan V and M left of the squad’s information line if the flags are turned on.

Steamhammer 2.0 change list

Some parts of the change list are already posted; think of those posts as part of this one (ahem, “included here by reference are....”). Here is the rest. You may notice that it is slightly long.

code changes

• More stuff in UnitUtil: Cooldown and FramesToReachAttackRange() functions added, and used in micro. GetWeapon() functions reworked for simplicity. Damage-per-frame functions added to compare weapon strengths. IsCompletedResourceDepot() added, factoring out code that was repeated in several places.

UnitInfo::estimateHealth() estimates the health of a unit which may not been seen for a while, accounting for protoss shield regeneration and zerg hp regeneration. (Terran medic healing and SCV repair are not easy to predict.)

InformationManager::enemyHasSiegeMode() added, and used in tactical calculations.

• In GameCommander, I sorted the manager calls so that managers which gather information are called first, and managers which use the information are called later. They had gotten jumbled over time. The main effect is that Steamhammer reacts 1 frame faster after discovering the race of a random opponent, a crucial difference that I estimate will, over Steamhammer’s lifetime, save approximately zero games.

• I renamed SquadData::addSquad() to createSquad(), since that’s what it does, and reworked it for simplicity. I removed the declaration of SquadData::clearSquad(), which was not implemented.

• Some calls in WorkerManager iterate through bases instead of through units to find base-related information. It’s faster and simpler (but I did have to spend time to fix a bug that I introduced in the process).

• More unnecessary includes removed, bringing a negligible improvement in compile times.

• I removed the configuration option Config::Micro::UnitNearEnemyRadius. The value is now chosen dynamically in code.

• In the game info display (turned on with Config::Debug::DrawGameInfo and drawn in the upper left), most labels were not needed because the meaning is obvious. I removed the labels of items other than “Opp Plan”. Less clutter is better.

• The TimerManager display, turned on with Config::Debug::DrawModuleTimers, had grown disorganized and probably incorrect. I straightened it out. I also improved comments in the code to prevent future disorganization.

opponent model

The opponent model has always distinguished between opponents that appear to follow the same plan every game, and opponents that vary their play. Until this version, it used the information only in a minor way. Now it selects openings using an entirely different method for multi-strategy enemies. If you play the same every game, Steamhammer will try to find the best single response, exploiting your predictability. If you mix up your play to confuse Steamhammer, then Steamhammer will mix up its play to confuse you; you get minimal predictability to exploit. We’ll see how well it works, but I’m expecting it to make a big difference in a long tournament like AIIDE.

• Against a single-strategy opponent, Steamhammer sticks with the variant of epsilon-greedy that it has always used: With probability epsilon, explore randomly; otherwise, choose the best known opening according to a weighted win rate that tries to take the maps into account. It’s not strictly classic epsilon-greedy, though, because epsilon varies according to the loss rate: If we are losing a lot, explore more often and play the best known opening less often. (It’s an adaptation to having more openings available than can be tried.) I have modified it so that the exploration rate increases more rapidly with the loss rate, because I found it was too often repeating an opening that won 1 game out of many, instead of looking for a better choice.

• Against a multi-strategy opponent, Steamhammer starts with the same weighted-win-rate calculation that it uses in the single-strategy case. Each opening it has already played is an option, and has a measured win rate. Exploring a new opening is an option, and its win rate is the mean win rate of openings that have been tried, its best estimate of how likely a newly explored opening is to win—but with a hard floor, so that the exploration rate never goes too close to zero. It randomly chooses among the possibilities, giving each a probability proportional to the square of its win rate. Squaring the rates gives openings with win rates near zero little chance of being chosen—unless most openings have win rates near zero. If all win rates are zero, because there are no wins yet, the hard floor on exploration means that Steamhammer explores every time. If the win rates are high for some openings and low for many others, exploration will be rare and Steamhammer will most often randomly choose one of the better openings. It’s ad hoc but makes a certain amount of sense.

macro

• Mineral locking. Steamhammer’s implementation follows Locutus in outline, but is different in detail. Mineral locking helps mainly in macro games, which Steamhammer is now able to play better because of the squad changes, so this was an opportune time to add it.

construction

• Prefer to expand to bases near the edge of the map. Bases near the edge are usually (not always) more protected than bases near the center of the map. In practice, Steamhammer now mostly avoids taking the risky center bases on Heartbreak Ridge and other maps, and the exposed mineral only bases on Python, at least until later in the game. I had to tune it carefully so that the natural of the 1 o’clock base on Tau Cross, far from the edge, is still preferred over the exposed 3rd base at the edge. Someday I’ll implement map analysis and figure out a real measure of exposure to attack.

• Bug fix: Don’t try to build at a location which a worker cannot reach. It never happened, as far as I know, but there was a mistake in the code.

tactics

• In the overview I said that workers are not transferred to base that is in danger, but there is more to it than that. Each base keeps track of whether it is being attacked severely enough that workers appear to be in danger; the estimate is made by CombatCommander::updateBaseDefenseSquads(), which I wrote about under base defense, and can be accessed via the Base objects for each base with base->inWorkerDanger(). If a worker is idle, meaning it is due to be assigned a new task if one is available, then it is not assigned a task at a base where workers are in danger. A worker is normally made idle after completing any task: worker was just created, gas collection is being turned off so worker no longer needs to mine gas, worker is finished defending itself and should be put back to work, and other cases. Workers are not only not transferred to an endangered base, they are often (not always) transferred away from the endangered base because they had to defend themselves, or were pulled for defense, or otherwise changed tasks. It’s hardly perfect, but it saves workers and greatly improves Steamhammer’s resilience to attack. It was a critical improvement. Related weaknesses remain: Steamhammer may still try to build at the endangered base, or transfer drones to a distant base through the enemy army.

• Fixed CombatCommander bugs in deciding which enemy base to attack.

• Rules for recognizing enemy cloaking and assigning detectors to our squads are slightly improved. Steamhammer pays attention to whether it has cloaked units itself and needs to catch enemy observers. This allows overlords to stay safer in some game situations: We don’t have lurkers, therefore we don’t have a strong need to hunt observers, therefore overlords can stay home.

• In feeding units to the combat sim, an enemy building which was last seen uncompleted is entered as completed if is currently out of sight. It is sometimes an improvement, sometimes a mistake. It would be better to track the estimated completion time and use that (a number of bots do).

micro

• Previous versions changed former stateless module Micro into an object with state, but did nothing new. This version updates the state with each of our unit’s orders and a little more information, but still doesn’t use the state for anything. It’s all prepared for serious work, though.

• Enemy unit movement prediction is smarter. It takes distance into account. Also, prediction is used differently in different cases to get better results in practical situations. Mutalisks, wraiths, and vultures now use prediction; as instant-acceleration units they are kited by a different routine than other units, which didn’t use prediction until now.

• Kite only units which the enemy has targeted. If nobody wants to shoot you, you don’t have to step back from the shooting. This especially helps hydralisks, which fire more slowly when kited and tend to get in each other’s way.

• Some bits of micro explicitly take latency into account in their calculations, especially kiting. It’s more accurate.

• Don’t chase an enemy if we’re predicted to be unable to catch it. CanCatchUnit() (defined in Common.cpp) figures it out. It makes less difference than I expected.

• Targeting priority: A ghost which is nuking is the highest priority target. An enemy defiler is also a high priority. These two were oversights. There are other targeting tweaks, for example to reduce cases of attacking the pylon when the cannon behind it is firing; it’s not fully successful.

zerg

• If we are short of mutas or lurkers (by a simple hardcoded count), then do not substitute a drone for a muta or lurker, no matter how much we may want drones. This was the biggest cause of delayed tech switches, and fixing it makes strategic play crisper at key times. This was one of the most important fixes in version 2.0.

• Late in the game, add extractors willy-nilly everywhere that they are possible. Steamhammer was too often gas-starved in the late game, too slow to get more gas when it was needed. This is also an important fix.

• Limit scourge more stringently. Sometimes Steamhammer makes so much scourge that it has no gas for anything else, causing macro problems as it delays production to get more gas.

• The building manager is much more cautious about turning a failed expansion into a macro hatchery. It only orders the change if it appears that we actually need a macro hatchery.

• Build a queen’s nest sooner in some situations. This is to speed hive tech when it is needed. It doesn’t make a big difference.

• If the enemy has too many corsairs or valkyries, get air carapace. This gets air armor for overlords even if we are not making any other air units.

• Favor guardians more, especially versus mass cannons. Past Steamhammer versions reduced guardian use by too much, and this starts to correct it.

• Emergency reaction: If we’re dangerously short on drones, don’t spend one on a building. Oops.

• Emergency reaction: If we have no bases but do have hatcheries, then make sure that the drone limit is at least 3. Having “no bases” means that no hatchery is at a predefined base location; we might still have hatcheries that can mine. Steamhammer formerly thought that, without bases, it had no need for drones. That was OK if it had drones already, not if they were all dead. Now Steamhammer has a chance to recover, provided the enemy is also prostrate after a base trade.

• Don’t automatically make a sunken in reaction to a proxy. It was often an overreaction.

• Don’t automatically get an early sunken versus protoss 2 gate (the sunken was usually too early) or against zerg 2 hatch (it was often unnecessary).

• When planning a morphed unit type (such as a lurker), don’t count it as using up a larva. This minor bookkeeping fix should occasionally make for better production decisions.

• Cancel grossly excess overlords even in the opening book. This may help if an emergency situation comes up in the opening, but it is mainly meant to mitigate bugs which cause production loops. No simple production loops are possible, but there seem to still be some complex loops where unrelated emergency reactions fire, and each prevents the other from recognizing that it has already taken action. It’s rare, though.

• Don’t let rebuilding the spawning pool cause a production jam, and don’t allow multiple copies of the spawning pool. It was a rare but deadly bug in production unjamming.

• Fixed a crash if a hydralisk den, lair, or spire was dropped in the opening (in reaction to an emergency). It was that specific.

• Fixed a rare bug that could prevent gas from being retaken after it was lost.

• Fixed a bug in defensive reactions that could request zerglings when there was no spawning pool.

• Fixed an unimportant bug in deciding to make a hive. It had no real consequences.

zerg openings

• Fixed a number of openings that had suffered bit decay: They were broken or mistuned due to code changes, such as queue reordering and mineral locking.

• Added new turtle openings designed to exploit particular enemy strategies: 11HatchTurtleHydra, 11HatchTurtleLurker, 12HatchTurtle, ZvZ_OverpoolTurtle. These are meant to stop specific rushes and leave Steamhammer in a sound position.

• Finished up and optimized the anti-forge-expand macro openings ZvP_3BaseSpire+Den (a good success) and 4HatchBeforeGas (not as effective). Steamhammer is finally able to play macro games well, so this was important. To play these openings truly well, though, Steamhammer needs greater ability to understand and react to the enemy’s timings.

• I fiddled with opening probabilities, mainly to continue to make openings more equally likely so that the opponent model can explore more efficiently. But also to include the new openings and to adjust to Steamhammer’s new strengths and weaknesses.

• I split the opening 9PoolSpeed into 2 variants. One variant keeps the same name and makes fewer zerglings (hit them by surprise, then transition to a normal game), the other is called 9PoolSpeedAllIn and makes more zerglings (hit them hard and maintain pressure). Both are more effective in their ways than the former compromise opening.

• A few ZvZ openings make 1 drone fewer, to keep zergling numbers as high as possible.

• Some other changes.

Steamhammer-Bereaver game with defiler

The game Steamhammer-Bereaver is a good example of Steamhammer’s defiler skills, both the good and bad points. Steamhammer gained an early advantage and contained Bereaver to its main, so the outcome of the game was not in doubt, hive tech or not. Bereaver mounted a strong defense at its ramp and held on for a long time. A timid zerg might have feared to attack and lost on points when the game time ran out—but of course Steamhammer is not timid.

The defiler dithered for a long time. Consume research finished at about 15:45 into the game, and the defiler filled up on energy within 15 seconds after, so that part was OK. A first attempt to break up the ramp was repelled with storm; the defiler was too far back and unable to help, due to lacking squad coordination. The second attempt to break up the ramp was at 18:20, and this time the defiler was in range. That’s about 2 and a half minutes from when the defiler was ready to act until it finally did; it had plenty of time in between to swarm or plague. It would have been worth it to sacrifice the defiler to plague the defenders.

swarm on the ramp

Excellent swarm placement! The defiler had energy to cast a second swarm farther into the enemy base, but did not. Even so, the one swarm nullified the dragoons and cannons and ensured that the attack would succeed if pressed. Thanks to my modifications, FAP understood that it would succeed and Steamhammer did press until it broke through. The attack had to be under way before the defiler could support it; Steamhammer has no ability to swarm (or plan to swarm) in advance of an attack. Steamhammer attacks when maxed, so in most winning games an eventual attack is guaranteed. When the swarm does come, it is generally well-positioned for where the units are at the moment, rather than where they are going or where they want to be.

The defiler seemed to get confused and did not contribute again until the very end, when it laid down a perfectly-placed plague over a few protoss buildings that were about to be destroyed anyway. The laser-precise plague hit as many protoss buildings as possible while missing zerg units. It’s impressive in a way, and it may even have shortened the game... by a fraction of a second.

Still next: Base defense.

Steamhammer 2.0 defilers

Defilers are complicated to use. I implemented a pile of features to get them to work acceptably, and I suspect that a bug has crept in since they don’t seem to work as well as when I first finished the implementation. They don’t swarm or plague as often as they used to. A likely source of the bug is the time control work.

The first step is to get defiler tech. The strategy boss decides when to get defilers. Currently, it delays defilers until fairly late versus terran that has a high ratio of tanks and/or vultures in its force, especially with spider mines, and otherwise uses the same criteria for all races: Do I have a hive, enough drones, and a few more items? When the defiler mound finishes, the strategy boss eagerly orders consume research. It is lazier about ordering plague, and later metasynaptic node (+50 energy). According to conventional wisdom, the energy upgrade is useful for defilers because then a full-energy defiler can swarm once and plague once before it has to consume again.

The strategy boss currently orders the production of 1 defiler at a time. A second defiler is not made unless the first one is lost. The defiler is sent toward the front in the hope that it will find something useful to do there.

consume

The consume implementation is on 3 levels. Strategy: If there are not enough zerglings on the field for defilers to consume, because the unit mix calls for other units, then the strategy boss orders up a few more. It makes sure that there exist at least 4 zerglings for each defiler. Tactics: A small number of zerglings are assigned to the MicroDefilers unit controller alongside any defilers. The number assigned depends on how much energy the defilers need; see Squad::addUnitsToMicroManagers(). The controller orders the zerglings to catch up to and stay near the defilers; each seeks the closest hungry defiler. Micro: If a defiler is hungry and finds an assigned zergling standing by, it’s lunch time.

A defiler counts as “hungry” if it has less than 150 energy. Defilers don’t consume anything other than zerglings which have been assigned for consumption. It’s not urgent, but it would be nice if they could consume irradiated units, or any units nearby that are destined to die. In an emergency, they should be willing to consume overlords. In the very late game, when minerals are gone and gas is plentiful, it may be more efficient to consume scourge.

plague

Plague is a spell of opportunity. If there is a big ball of enemies that aren’t already plagued and have a lot of hit points to lose, it virtually always pays to plague them if you can. Steamhammer is not smart enough to intentionally seek out good opportunities, but it is smart enough to recognize opportunities that arise.

So defilers keep their eyes open. If a defiler passes a cheap check (I have plague researched, I have enough energy, there are a bunch of enemies nearby) then it performs an expensive check to decide whether and where to cast plague. It draws a square of each place it can reach without moving far, and does a raster scan through the square, scoring each possible plague it might cast. For enemy units, it adds up the hit points the enemy will lose, counting it as 0 if the enemy is already plagued and adjusting for a few special cases: Buildings other than static defense get a discount, they are less important to plague, while a bonus goes to cloaked units (which are revealed by plague) and carrier interceptors (which will get stuck in the carrier until plague wears off, as the carrier tries to repair them). It also subtracts plague on friendly units, so that it is willing to plague itself if it covers enough enemies too. If the best score passes a threshold, cast the plague there.

Death rule: If the defiler thinks it is about to die, then plaguing any enemy is good enough. Theoretically, in this case Steamhammer might plague 1 stray probe that happens to be close enough.

This exhaustive search method is startlingly accurate at deciding where to plague (though I think I should increase the discount for buildings). I’m sure there are computationally cheaper ways to get good results.

dark swarm

Dark swarm is way more difficult to use well than plague. Swarm use ought to be part of a coordinated plan. A simple plan might be: Start the attacking units moving, then cast swarm around the time the attackers come into cannon range. The decision to attack should be informed by the intention to cast swarm during the attack—with enough swarms, one zergling can be the Klingon running man and kill all the marines. You need different plans for assaulting a fixed position, fighting mobile enemies that can evade swarm, or warding off an attack on your base. You have to take into account what units on both sides are able to do damage under swarm.

Steamhammer can’t plan like that. Those skills are far in the future. All zerg bots are weak with swarm.

Steamhammer chooses where to swarm with an exhaustive search similar to the plague search, and it is not as effective. First, the quick check only allows swarm over enemy buildings; Steamhammer can use swarm to attack, and not to maneuver or to defend, a major limitation. Protect the army in the middle of the map from air attack? No can do, boss: Besides the defiler skill, that would require more smarts in unit control. In the exhaustive search, it scores the units that end up under dark swarm, trying to cover friendly units that can hit under swarm and enemy units that can’t. It’s not the score you want; it ought to take into account nearby units that will want to move into the swarm, or will be unable to hit swarmed units from outside, or will have to suddenly flee.

Death rule: If the defiler is about to die, it will swarm wherever it thinks best, even if it doesn’t cover an enemy building. If the defiler doesn’t have any better idea, it will cast swarm over itself, which may save it depending on the situation. If the dying defiler could either swarm or plague, the choice depends on which happens to be checked first.

I implemented consume, plague, and swarm as micro skills, decided at the level where the code is controlling a single defiler. It makes sense for consume and plague. I think swarm should be decided at the squad level, because its use depends on the tactical situation and the squad’s goals. That will require a lot more cleverness in the squad, though.

combat simulation and micro

I modified the combat simulator FAP with a rough understanding of dark swarm. FAP performs a loose approximation of combat, and the dark swarm knowledge I added is equally loose. If a unit is under dark swarm at the start of the simulation, it is assumed to remain under dark swarm throughout, no matter how it may move around. (Movement ignores terrain and other units too, so FAP is already loose that way.) It understands which units can do damage under swarm, and has simulated units seek out only targets that they can damage.

All this happens only if dark swarm is already cast. There is no provision for planning ahead, “when the units start shooting, I’ll cast swarm here.” In other words, dark swarm influences the combat sim, but the combat sim does not influence dark swarm; Steamhammer doesn’t extract the full value. See the discussion above about coordinated plans.

Steamhammer also understands dark swarm at the unit micro level. Melee units seek out targets under dark swarm, on the theory that then they will be protected under swarm too (“ha ha, you’re under swarm, I’m perfectly safe... I hope?”). Ranged units skip targets under swarm that they can’t hit. The ranged unit controller understands which ranged units can do damage under dark swarm.

There is room for more smarts in exploiting dark swarm at the tactical level. Units under air attack, for example, or facing marines, should move into swarm for their own protection, and mostly they don’t. I’ve seen Steamhammer make serious mistakes because of that ignorance.

time control

My first implementation made 2 defilers at once and performed all the computation steps in the same frame. A disadvantage was that sometimes the 2 defilers would swarm the same place at the same time (scoring ensures that if they swarm at different times, they swarm different places). A bigger disadvantage is that it was too slow, with some frames taking over 150ms. I changed it to make only 1 defiler at a time, and arranged that consume, plague, and dark swarm are examined in different frames. That made it fast enough. There are ways to speed up the calculations, but slicing them apart seemed easier.

Overall, the defiler system is not industrial strength, but it’s a strong start. As I mentioned at the start, I suspect a bug due to the time controls. Steamhammer implements all the basic skills, which if you go through the above text you can estimate for yourself required 1.5 zillion different software features. Now that the pieces are in place, they can be improved one by one.

One of the emergent properties of the defiler system is that it is unpredictable. The defiler may plague this and then plague that, or swarm here and then swarm there—it gives the impression of running in streaks. And I can’t judge ahead of time what it will do, it surprises me.

Next: Base defense.

Steamhammer 2.0 overview

Today I list the big changes in Steamhammer 2.0 without going into detail. There are tons of smaller changes. Details will follow.

The most effective changes:

  • squad units are clustered, and each cluster makes attack/retreat decisions independently
  • mutalisk and especially hydralisk micro is crisper
  • a base under attack does not receive transferred workers, improving resilience
  • greatly reduce cases where the bot gets a spire/lurker aspect and then delays getting mutas/lurkers

The squad unit clustering allows Steamhammer to play a big army macro game without the constant tactical blunders that it used to make (it makes smaller and mostly different blunders). Many bots use a similar system, with good reason. The tactics rework is substantial, and deserves the 2.0 version number by itself. In combination with the other improvements, Steamhammer is incomparably stronger at ZvP, which may now be its best matchup. I reported on DaQin, and Steamhammer (like Proxy) can open with 12 hatchery and still cope with Wuli’s rush (Proxy’s author called it easy, but it requires a combination of several skills that are not so easy).

I put in a ton of work, but the decisions that unit clusters make are still not as polished as they need to be. An interaction with the combat simulator causes dangerous mistakes in retreating from superior forces. It can happen in all matchups, but it is most obvious in zergling fights in ZvZ. What used to be Steamhammer’s most successful style of play in its best matchup is now its least successful in what may be its weakest matchup. It’s pretty funny, or at least it will be after I figure out how to fix it.

All these items deserve their places on the list. I fixed the case where Steamhammer’s spire used to finish and it would make a bunch of drones instead of mutalisks, and that may sound like the usual modest improvement to the strategy boss. In fact it is a critical fix that makes midgame tech switches more rapid and decisive. It wins games.

Other important changes:

  • mineral locking
  • base defense was reworked extensively
  • new openings added to exploit possibilities and old openings tweaked to fix flaws that had crept in
  • opponent model exploration and strategy randomization improved
  • defiler support with all spells: consume, dark swarm, plague

Many aspects of play have moved forward. As always, there are new bugs like the retreating problem I mentioned above. And as I blathered about yesterday, old weaknesses remain. The overall result is as I’ve reported before, hugely stronger play against some opponents and weaker play against others. Well, that’s in my testing, which is never enough. We’ll see how it goes in the wide world! Since we’re in a time of protoss domination, I’m thinking that strength against protoss is a good sign.

For AIIDE in particular, I expect that my changes to the opponent model are key. I made the couple needed improvements of a previous post. The academic papers I found turned out to be unhelpful, and my changes are ad hoc, but they address the problems. If the opponent chooses strategies unpredictably, Steamhammer will too. If the opponent model doesn’t find a winning counter, it will explore more and more widely until it is randomly trying anything it knows, including openings which are otherwise not played in any context. On the upside, I’m hoping it will eventually hit on surprise counters to the toughest opponents. On the downside, in the worst case it will explore a lot of bad ideas, and the convergence time could be longer than the tournament!

Later today: Notice after Steamhammer 2.0 is uploaded to SSCAIT. Tomorrow: Defiler support in detail.

what I didn’t do

Today, a post about things I wanted to do and didn’t get to, or started to do and rolled back or didn’t finish. I accomplished a lot, but the to-do list is functionally infinite.

• UAlbertaBot executes unit orders as they are issued during the frame, retaining no state beyond what BWAPI remembers. Steamhammer inherited the system. I am in the process of changing it to record the orders and save status info, then issue any needed orders at the end of the frame. Adding the Micro object in the last version was one step. The Micro object now records the order and status of each of our units, and has all the infrastructure it needs to issue orders, including its own update() method with TimerManager calls to track how long it takes. But it doesn’t actually issue orders; there is no effective change to the micro system.

When this is done, it will be easier or possible to unstick units, to make sure units don’t get stuck in the first place, and in general to control units precisely.

• I worked a bit on analyzing Steamhammer’s use of the combat simulator. Improvements are there for the taking, but my first try was not successful and I left it for later.

• McRave suggested that micro to avoid big damage attacks was key. Equally important is to pull off big damage attacks yourself. Besides plague, the zerg big damage attack is scourge suicide, and Steamhammer sucks with scourge. Scourge control is on my list, but never came to the top.

• I implemented mine dodging to reduce the danger of spider mines, but the way I did it was not useful for zerg. Reacting to mines is tricky: If a mine pops up, sometimes you should drag it to the enemy, sometimes drag it away from your other units, sometimes shoot it down. I lost the “just shoot it” case, and it was a net disadvantage. The feature is in the code but is disabled.

• Steamhammer’s lurker skills were adequate for when they were implemented, but have fallen behind the times. It needs to stop doing things like burrowing one lurker at a time in cannon range.

• I haven’t done anything to improve overlord safety, dark templar reactions, or any of that stuff. Many weaknesses there.

• I planned and promised a skill to put pressure on the enemy in the early or middle game, but did not finish one. I had several ideas, and all of them hinged on what I call raiding: Harassing where possible, moving around but staying near the enemy, not retreating to rejoin the main army. Harassing mutalisks can be considered raiders in that sense, and so can Krasi0’s vultures and Bereaver’s reaver drops. Runby units and dropped units should act as raiders, and Steamhammer doesn’t have the skill—with a Hold order, they go idle if they clear their area to hold, and with an Attack order they may try to retreat to the main base. Picking up dropped units is also a good raiding skill.

• I implemented a Watch squad, but did not polish it up enough that it became useful. It is turned off. It had several purposes. It could keep watch on expansions and chokes, sometimes making it possible to disband the Recon squad. It could prevent enemy expansions in some cases, and clear mines and verify the safety of expansions that we wanted to take.

• I thought of adding pre-learned opponent model data for the fixed opponents carried over from last year, plus opponent model hint configuration (like “try this first and see how it works”) for opponents that I think I know something about. PurpleWave, we’re told, did an elaborate job of that for CIG this year. The idea of hints is that the opponent model can save exploration time if the hints are right, winning more games, and correct itself with little loss if the hints are wrong. I decided that, since AIIDE is a long tournament with plenty of time for learning to converge, other work would probably help more.

upcoming

Done with the testing, today I am resting. I’ll upload zerg-only Steamhammer 2.0 to SSCAIT tomorrow. On the blog side, I think I’ll start with an overall evaluation post, what I’ve done and how successful it seems, and follow up with posts about specifics like defiler skills and squad restructuring, and then the big detailed change list. At some point soon Steamhammer 2.1 will slip in. 2.1 will likely have a few minor fixes and deserve its own little change list.

Shortly, CIG should release its detailed results. They promised source and replays. If they also provide a game result log, I’ll analyze it and post my usual colorful summary tables. Not sure how that will fit in with the other posts, the blog may have a busy time.