archive by month
Skip to content

hiatus starts to tail off

I have regained enough time and energy to make occasional posts. Expect a low level of activity.

I registered Steamhammer for AIIDE 2019. I rolled back the BWAPI 4.4.0 upgrade for now, but I have started to make a few other changes that I have strength for. Last year the AIIDE version of Steamhammer got a multi-page change list broken down by category; this year it will be a short list. I expect to upload versions to SSCAIT once or twice before the deadline, to try to catch any bugs I’m adding in.

Doing little may actually be good for tournament performance. Last year I added so many features that I was fixing bugs for months, and the current mostly-fixed version is performing well on BASIL. At the moment, Steamhammer is the top zerg. When it loses, the most frequent cause is a gross strategic blunder, and apparently Steamhammer’s wide choice of openings allows it to eventually learn to avoid most strategic blunders. A long tournament like AIIDE should show the same effect.

switching to BWAPI 4.4.0

I’m in the process of switching Steamhammer to BWAPI 4.4.0. There are more steps to it than I expected. It involves switching from ancient Visual Studio 2013 to modestly out of date VS 2017. I hated dealing with Microsoft enough to make the upgrade, but it wasn’t actually difficult. The compiler is more strict about language conformance, and I needed a little research to figure out the minor tweaks. There are still a bunch of warnings that I’ll look at later; at first glance they seem insignificant. Changes to keep up with updates to BWAPI’s API were trivial; I had been ready for more. Up to here, everything was planned for.

I did not realize that BWAPI no longer installs BWAPI.lib but expects you to compile it yourself as a project dependency. BWAPI no longer includes a lib directory at all. See the commit. The change is to work around this issue of link errors, which looks like Microsoft’s fault. The closest thing to a mention in the change log (under BWAPI 4.3.0) is below; you have to read the closed issues to find out the problem and solution. (Steamhammer’s documentation is also behind reality....)

Added [Release|Debug]_NoCopy configurations for BWAPILib and other projects which could be used outside of BWAPI itself (bots, libraries etc). This allows for automatic rebuild of these files when BWAPI or your compiler updates #786

I wonder—could the change cause headaches for CIG and AIIDE as they compile entries? I can imagine issues with project dependencies. They certainly need to know about it, at least.

I have more to do. On the cost side, I’ll need time to run test games to see if Steamhammer has hatched any new bugs. On the benefit side, I’m looking forward to removing Steamhammer’s workarounds for the BWAPI bugs in version 4.1.2. They are holding back its play (though there are bigger weaknesses).

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.

more Steamhammer 2.3 bugs

Earlier, I pointed out 2 rare game-over bugs in Steamhammer 2.3. It turns out that there are 2 other serious bugs in this version, which are not rare; they are causing a good proportion of the bot’s losses.

One is a bug in turning gas collection on and off. The bug can turn off gas collection even when the bot needs gas to continue production, causing a production freeze that can last for the rest of the game (not likely to be long with no production). It may have been introduced when I refactored the code for gas decisions. The other is a bug that cancels vital hatcheries (and perhaps other buildings) for no apparent reason. I suspect an error in one of the emergency “uh oh, no drones!” or “panic! I need those resources back NOW!” tests. There are other possibilities.

Critical bugs go straight to the top of my list.

The bugs also point out a weakness in Steamhammer’s design: Decentralized decision making. Decision rules that take direct action are scattered through the code. Of course a bug in any rule can cause bad behavior; that’s inevitable. The weak point of the design is that the separate rules, sometimes far away from each other in the code, have to cooperate so they don’t override each other’s decisions. The gas collection bug is clearly a coordination bug; one rule says “I need gas for the next production item” and some other rule is incorrectly overriding the decision, “nah, I wanna turn off gas.”

Compare the unit mix decision, which is made using a more indirect method: Rules collect evidence, then in a second stage the evidence is weighed and the decision is made. The evidence is explicit, so bugs are easier to trace, and evidence-collecting rules do not need to cooperate with each other, they only need to weight their evidence consistently. Machine learning systems conventionally follow this collect evidence, then decide paradigm as a matter of course.

a trivial rule of thumb for lurker targeting

One of the items on my list is good lurker targeting: Lurkers should aim for the target that will bring about the most total damage, with the lurker’s line of splash damage. In the current release, Steamhammer’s lurkers use the same targeting as most units, choosing the closest available target when other things are equal. It’s not so smart, but improving lurker targeting has been a low priority; the full analysis is more effort than it’s worth for the time being. Lately I hit on a simple idea to improve targeting without doing any deeper analysis. It’s a rule of thumb that I notice when I play myself, but for some reason I never thought of implementing it before.

Suppose a lurker is to choose one of 2 targets, and it knows how far away each is but nothing else. It has to choose blindly. Which target is the better choice? Does one of the targets offer a better chance of blindly landing splash damage on the other, with the lurker spines reaching out in a line? It might be counterintuitive, but geometry gives an answer: The more distant target is better. If you aim for the near target, the far target subtends a smaller angle from the point of view of the lurker. If you aim for the far target, the near target subtends a larger angle, so you are more likely to hit it by chance. If you don’t believe me, draw some circles on paper, or start up the game and try it!

The same reasoning works if there are 3 targets; pick the farthest. If targets are dense, it doesn’t matter where you shoot, and if targets are sparse and you do no further analysis, then aiming for the farthest is best. It’s dead simple, so I implemented it in Steamhammer, choosing the highest priority target at the greatest distance rather than the smallest distance. It is a small but real improvement, and it was hardly any effort. There is a disadvantage, which is that the more distant unit may be better able to sidestep the lurker spines, since it has more time to react. But not many bots dodge lurker spines.

Of course it’s much better to do the full analysis: For each target, calculate which enemies the lurker will hit and what the hit is worth. Ideally, predict how the enemy units will move, taking into account their likely decisions, and correlate their paths with the extending line the lurker spines will follow. And optimize the fire of all lurkers at a location as a group, so that they cooperate to kill more efficiently (2 lurker shots kill a marine, so these 2 lurkers should aim here and here to overlap their splash damage). It is an elaborate calculation, and I don’t think it’s worth the effort for now; better to improve Steamhammer’s other decisions first.

Steamhammer and queens

When I announced that Steamhammer 3.0 would be next, I forgot a step: I want to upgrade to BWAPI 4.4.o. I actually want to make a 2.3.1 release first with the upgrade. I think I can probably do it this month, along with other additions, even though I may not start the work until next week.

Just now I don’t feel like any serious work, so I started to implement queens. Queens are not essential; you can be a great zerg and never spawn one. Often enough, queens are not useful at all. But queens are fun, and I feel like doing fun stuff.

I’m starting with parasite, and I’m thinking the order should be parasite, then infest command center (you get both of these with no research), then ensnare, then broodling. I don’t expect to do them all at first, but I will eventually.

My initial plan is to always make 1 queen versus terran, if the situation permits, in hopes of infesting a command center. And to make 1 queen against protoss if Steamhammer spots juicy parasite bait, like a carrier. In ZvZ a queen is too expensive, unless maybe you can broodling defilers and ultralisks if the game goes that late.

In the longer run, I want Steamhammer to learn for itself which tactics work, and which work against each individual opponent. It should be possible to measure whether parasite brought in information, whether ensnare harmed the opponent, and so on, and estimate whether the queen and the research to use it paid dividends. Decisions based on data are better decisions.

parasite

Parasite is rare in pro games, though it has been used. If you have queens at all there is usually a better use for their energy. But bots are not as knowledgeable as pro players. Could parasite be useful?

I am going to try parasiting flying critters, on maps that have them, to see if it’s useful. And I’ll try parasiting expensive enemy units: Science vessels, battlecruisers, carriers, arbiters. I expect that a few strong terrans will react well, and the bulk of bots will not notice the parasite and will give Steamhammer free information. But we’ll see!

By the way, parasiting a science vessel is not too dangerous if done carefully. Irradiate has a range of 9 tiles, while a queen has vision range 10 tiles, so a queen that approaches cautiously has good chances to launch a parasite without getting irradiated. Parasite has range of 12 tiles (the same as the range of a sieged tank), so if other units spot for the queen then parasite can be always safe.

How to react? If you spot a parasited neutral unit (and it’s not your parasite), kill it. If you’re terran and one of your units gets parasited, you can research restore for medics and remove the parasite. That will take time, though (unless you’re Krasi0, which researches restore pre-emptively and cures spell effects in moments). Zerg has 2 ways to remove parasite (if it ever happens, which is unlikely in ZvZ), both with limited use: 1. If a drone is parasited, you can turn it into a building. 2. If you have a queen of your own, you can place your own parasite on your unit, which overrides the enemy parasite. Protoss doesn’t have any options, and can only be rid of the parasite by killing its own unit.

You can also move a parasited unit away from your army, or use it to show the enemy what you want the enemy to see. The opposite approach is to put it in the front of your forces, so it is the first to be killed.

infest command center

What’s to say? If you bought the queen already and you can infest a command center, you should! For now, I won’t bother with infested terrans, or with details like stopping the attack when the command center falls below half HP so that it is not destroyed before the queen can infest it. Leaving the infested command center where it is may slow the enemy from retaking the base.

ensnare

Ensnare is useful against fast units in groups. I’ll try it first against marines, wraiths, and corsairs. Vultures are hard to tag, but I might experiment. It’s also useful to reveal cloaked units, so it may also be worth researching against dark templar, arbiters, and maybe ghosts.

An ensnared unit moves much more slowly (usually at half speed), and most unit types also shoot a little more slowly. It will be difficult to retreat, or to complete any other tactical maneuver. A logical reaction is to hold your ground until the green goo wears off; you lost a lot of mobility and only a little firepower, so rely on firepower.

broodling

Broodling is expensive to cast; it costs 150 energy, which takes a long time to build up. It’s also the only queen ability that can justify making more than a few queens. It’s good against expensive ground units like tanks and high templar—provided you keep the queens alive so they can do it again (the 100+100 cost of a queen plus the cost of the time to build energy is worth more than one 150+100 tank). Broodling is most valuable in the very late game, when the map is mined out; at that point you want to favor spellcasters no matter your race, because you won’t have the resources to replace units.

There are 2 counters to broodling: 1. Kill the queens. 2. Make units that cannot be broodlinged (wraiths, reavers, archons), or are not worth the cost of broodling (marines).

Steamhammer 2.3 early returns

I’m pleased with how the new Steamhammer 2.3 is doing. Not only is it scoring wins where it could not before, its play looks sharper and cleaner. It’s mostly due to bug fixes. It beats XIMP by Tomas Vajda again (command jam bug fixed in 2.2), and it is back to its former success with heavy early zergling pressure (unit movement fixes in 2.2 and 2.3 plus zergling value fix in 2.3). I added many new openings in 2.2, and the unsound “dawn hydra rush” (those hydras arrive in the morning gray) is scoring well against SAIDA due to SAIDA’s broken reaction. The same opening worked against Iron when I wrote it, but one of Iron’s updates added a reaction and Iron is now safe. The value of the other new openings, positive or negative, will take time to show.

Randomhammer’s better building placement make bases look prettier. Having fewer buildings bursting into the center of the map can only help play, though I think that most opponents which are able to exploit the vulnerable buildings were going to win anyway. The tank improvements make terran factory play look almost... non-ridiculous. The tank play was awesomely bad before; now it gives the impression of trying to do something sensible, though it doesn’t always succeed. And, of course, drop openings work again; that may not be reflected in performance right away, because Randomhammer remembers the results of its previous version, which learned to avoid the broken drops against some opponents. Still, I’ve seen a few crushing drop games.

BASIL is better than SSCAIT for strength measurements. Here is Steamhammer’s elo graph from BASIL:

graph showing strong rise

Version 2.3 was uploaded 2 April, and includes the improvements in 2.2 and 2.3, since the previous running version was 2.1.4. The graph shows a strong rise on 3 April, 75 elo over its base, with a peak nearly 50 elo higher than the next highest peak in the graph. It’s pretty good evidence that Steamhammer is improved; the elo increase is likely 50 or more.

The same for Randomhammer:

graph showing questionable rise

This is not as clear. The peak begins to rise on 4 April; I think that is mostly because of game scheduling. It is about as high as previous peaks on the graph, and it is not convincing that it is statistically meaningful rather than a random winning streak. I’d say there is weak evidence for a strength increase. I will be disappointed if Randomhammer doesn’t end up showing a detectable elo rise.

I did a round of work on overlord safety last fall, and improved it enough that it was no longer one of the top weaknesses. Now other aspects of play have improved enough that overlord safety is again a critical weakness. There are also 2 game-over bugs that guarantee a loss. One causes drones to hang out with the minerals instead of mining; it’s rare. The other causes general confused behavior, and it is extremely rare. Rare bugs with no identifiable trigger condition are tough, but they have to go.

Steamhammer 2.3 change list

SSCAIT has been running Steamhammer 2.1.4 from December, skipping Steamhammer 2.2 that played in AIST S2. To see everything that’s changed since December, you also want the Steamhammer 2.2 change list.

Voters have been busy trying out the update. Fixes in version 2.2 restored Steamhammer’s ability to smash XIMP by Tomas Vajda, even from an inferior opening, so that was no trouble. Steamhammer also tried its super-fast “dawn hydra rush” in a game against SAIDA, bringing out SAIDA’s broken reaction. It’s stuff from version 2.2; version 2.3 should make zerg only slightly stronger compared to 2.2. Terran and protoss are a different story.

Source release soon. It’s more work than you may imagine.

The most important changes are in bold.

buildings

Most of the new code went into building placement. I added a special-case building placement mechanism that checks each incoming building to see if it should be placed differently than the default rules say. I use it in several ways. The net result is that terran bases are much more compact, and terran can sometimes play a long game without ever spilling buildings into the center of the map, which is quite a change. It does happen at times, though. Protoss bases are more compact too, but the benefit is less. Protoss still sprawls a fair amount. Zerg bases too are affected, but it’s barely an improvement because zerg doesn’t make many buildings in the first place.

Terran builds along the map edges of its base, using the space to compactly place supply depots, academies, and armories in a single line (they don’t double up). The buildings are 3x2 tiles in size and line up neatly. More total buildings fit into a base. It doesn’t help a base which does not reach the edge of the map. Also certain maps have unbuildable tiles along the edge and see no benefit. Usually, though, it helps a lot.

2 buildings may be adjacent. 2 buildings that are the same height may be placed shoulder-to-shoulder, and 2 buildings that are the same width may be stacked vertically; the limitation is so that I could reuse code that I wrote for terran edge placement, but it makes sense anyway as a space-saving measure. It applies to most buildings; there are a few exceptions like pylons that are treated differently. It allows only 2 adjacent buildings, checking that a building chosen as a buddy is open on all sides. That way building placement creates no blockades or long detours. It applies to all races. Terran, with free building placement, benefits the most. Protoss benefits some, but pylons and pylon power get in the way. Zerg bases can fit more buildings too, but it doesn’t bring any visible benefit to Steamhammer’s play.

When building placement fails, Steamhammer looks for a new “main base” to hold future buildings. “Failure” in this sense means that either the building could not be placed, or else it was successfully placed but ended up in a different zone than requested (the building gets built there anyway, it’s usually not awful). It keeps track of the number of times placement has failed at each base, and chooses the base with the least failures to date. It’s hardly perfect, but it works better than the old system of from time to time choosing a new main base at random. It works especially well when terran expands to a different main with plenty of building space. Protoss switches main bases the most often, and zerg by far the least.

• Protoss tries to place a pylon at every base, in case it someday wants to choose the base as a new main base to build at. Or wants cannons there. It doesn’t insert extra pylons, it places regularly scheduled pylons at new bases as they come up.

• I fixed 3 separate off-by-one bugs (inherited from UAlbertaBot) that affected right and bottom edges. One was in building separation checking; on the right and bottom sides, they were kept 1 tile farther apart than requested. That was the worst one, because it wasted space around many buildings. It was visible, and held a place on my bug list, but I hadn’t found the cause. One was in building placement checking at the right and bottom edges of the map; buildings were kept 1 tile farther away from the edge than required. One was in the base overlap check; buildings were kept 1 tile farther than necessary on the right and bottom sides of a potential base location. All bases are a little more compact because of these fixes, though you might not notice with a casual look.

• I fixed a rare bug—so rare that I have never seen it—that theoretically could allow a macro hatchery to overlap the location of a base that we might want to take in the future.

• Formerly, building placement made frequent redundant checks for things like “is this tile reserved for another building?” and “are there units blocking this tile?” I gave it a severe scrubbing, and now there are few redundant checks. The basic operation “can this building go here?” is faster than before because it wastes less computation. Building placement overall is not faster, though, because the special case placements do more basic operations.

• Formerly, reserved building tiles were drawn (when the debug option was turned on) in bright yellow. I found it glaring, so I changed it to dim gray. The option is turned on in the release version.

• During the rewriting, BuildingPlacer::buildable() became redundant and was removed.

operations and tactics

MicroManager::execute() didn’t execute all orders, a bug which caused drops to do no damage. Version 2.2 fixed the command jam bug, which prevented transports from picking up units to drop. With that out of the way, when the squad arrived at the enemy base with order Drop (which in that context should mean attack enemies near the drop point), the order was not executed and the units moved to the order position and sat in the enemy base, getting killed without firing a shot. Oops. When was the bug introduced?

In FAP, I forgot to correct for twoUnitsInOneEgg() in setting unit prices for the combat simulation. That theoretically affects zerglings and scourge, but scourge work differently and in practice only zerglings were affected. They appeared twice as valuable to FAP as they actually are. Oh, that’s why zerglings seemed to be so shy! Zerglings are back to their proper level of boldness; they are more aggressive in fighting everything except other zerglings. Zealots and workers especially should be afraid.

Lurkers in a unit cluster which was joining up with a cluster ahead could be mistakenly given contradictory orders one after another, causing them to get stuck or to vibrate in place. The bug also affected medics and defilers, though not severely. Lurkers were often delayed in the middle of the map, which could be a major setback.

• A unit cluster with no surviving combat units which was falling back to the base was mistakenly given 2 different orders on each frame, causing it to behave erratically and usually get caught and die. I solved it by adding a Fall Back cluster status instead of incorrectly using Retreat, which comes with its own different behavior. This affected terran the most, when only medics survived in an attack.

• UAlbertaBot provided unitClosestToEnemy() for each squad, used in tactical analysis. Steamhammer inherited it and renamed it the vanguard unit. Until now, the vanguard was used only internally to the squad. Now the vanguard, if set, is publicly available via squad.getVanguard(), so it can be used at the operations level. It is used in deciding which enemy base to attack: A base near the current vanguard is preferred. It attempts to direct the squad to targets which are closer to or on the way to the existing squad target. I hope it will reduce cases where Steamhammer wants to attack the main and tries to run by the natural... without understanding what it is doing, so that it turns back after taking damage. I also hope it will be better at switching targets to a new base that appears near the squad’s path, and better at choosing bases that are near each other to destroy in sequence.

• Mildly prefer to attack an enemy base other than the enemy starting base. It is a popular heuristic, I thought I’d try it. Not sure it will help.

• Steamhammer calculates the “front point” of the “front base” that needs to be defended, now using the center of the base’s resource depot, no longer the upper left corner. In doing that, I noticed that the center was calculated incorrectly (even simple things can be completely wrong), so I fixed that. I also switched other uses of the base position to the center, when appropriate. It seems like an improvement, though not a big one; the front line is no longer biased to the left.

micro

the.micro.MoveNear(unit, targetPosition) does unit movement when up-to-the-frame accuracy doesn’t matter—as it often doesn’t. It compares the new target position you give it with the unit’s existing target position, if any. If the two are close enough together and not many frames have gone by, it doesn’t bother to issue a new command, but concludes that the existing target is good enough for now. This greatly reduces APM in the cases where it applies, which prevents units from freezing due to excess commands. The new feature is used to move clusters which are trying to join up with other clusters (which are often themselves moving), and in micro when chasing an enemy which is still far away via the.micro.CatchAndAttackUnit(). In practice, units now do not freeze for long periods. If you look for it, you can see many cases where units freeze for a short time, but the combination of unit unfreezing from version 2.2 and this feature means that they unfreeze quickly. I tested by setting up a game with big dragoon-on-dragoon battles, and could not see any dragoon freezing for longer than a moment. Zerglings freeze for longer periods, but are much improved over version 2.2.

terran

Tank siege decisions are much improved. Steamhammer’s long-time mechanism for tank siege has separate decision criteria for siege and unsiege: Siege if shouldSiege and not shouldUnsiege; then unsiege when shouldUnsiege. That allows it to do things like not bother to siege if the only targets are defenseless enemy buildings, but not to bother to unsiege after other targets are defeated if enemy buildings are still in range. But the criteria were so sloppy that in practice tanks were barely able to fight. I made 3 changes. First, when a tank is sieged, it prefers a target outside of its minimum range. Formerly it preferred the closest target, meaning that if any target was inside minimum range the tank had to unsiege to shoot at it. It’s not efficient. Second, when facing a single enemy threat (meaning a ground enemy that can hurt a tank), don’t bother to siege. Siege mode has higher DPS, but the time it takes to siege negates that, and with only one thing to shoot at there will be no splash damage. There are exceptions: Siege anyway if the single threat is a bunker, cannon, sunken colony, or reaver. Third, if all the targets are melee enemies and all the targets are at the same terrain level as the tank, then don’t siege. It’s a heuristic; kiting is generally better than siege in tanks versus zealots and tanks versus zerglings if the melee units can reach the tank, and the reverse if a cliff is in the way. There is no consideration of whether a wall may be in the way (as on Empire of the Sun, for example), or whether a mass of other terran units is in the way. The result of the 3 improvements is that tanks are deadly, as they should be. The biggest errors in tank usage are no longer in micro, but in tactics; a tank also unsieges if the whole squad decides to retreat, and the squad decides that without considering the cost of unsieging tanks.

• Tanks prefer to target Large enemy units. Hit the dragoons before the zealots, you will do more damage. Unit size as one of the targeting criteria was already implemented for ranged units in general, but tanks have separate micro.

• No longer automatically make a bunker in response to a detected Heavy Rush. It was sometimes useless and almost never at the right timing.

protoss

The “away from home” bugs are fixed. Reavers and carriers no longer approach the enemy before starting to build scarabs or interceptors, and high templar again merge reliably into archons instead of wandering into the enemy base, unable to storm. The bugs seem to have been introduced in version 2.0 in September.

• Scarabs and interceptors are ordered as they can be built, rather than queued up. It’s a little more efficient.

• Carriers historically have been put into the flying squad if the flying squad exists, and otherwise stay with the ground squad so they work together. Now if there are 4 or more carriers, they go into the flying squad regardless and act independently. It’s primitive but likely an improvement on average.

the.micro.MergeArchon() supports dark archons. Steamhammer doesn’t know how to use any of their abilities, but now you can make them.

• React to mutalisks with stargate and corsairs. The reaction is implemented via BOSS, and it is so sluggish that it barely helps. As I implement production goals for strategy adaptation, the reaction will become quicker and crisper.

zerg

• Be looser with macro hatcheries. I found that when Steamhammer ran short of larvas, it was too slow to build enough hatcheries—sometimes it walked along the edge the whole game, growing its economy faster than it could catch up in hatcheries. With this change, I may have overcorrected; now it sometimes makes too many hatcheries and has larvas it can’t spend. As part of strategy adaptation, I will have it predict larva needs so it can hit on the nose most games.

• If the minerals are on the left of the starting hatchery, do the larva trick to gain a tiny resource advantage. It makes virtually no difference. Stream watchers will have to know what to look for to even notice that it’s happening.

openings

Most terran openings needed adjusting. The adjustments were largely timing changes to take advantage of the extra resources provided by mineral locking, corrections to avoid bad queue reordering, and getting supply depots in time. The more complex openings (and terran has a lot of complex openings) I repaired to a higher standard than they were built to in the first place, so as a set the terran openings are better than they have ever been. They work more reliably and hit earlier timings.

• I couldn’t resist adding the terran opening 8-8-10Vultures, which rushes a factory and pokes with a vulture that the enemy may not be expecting so early. Terran has more than a few fast barracks and fast factory tricks; when strategy adaptation is mature enough it will be able to try many of them.

• The protoss openings did not need as much adjustment; only the high tech openings needed fixes. DTRush is slightly faster, and CorsairDT and DTDrop are tweaked. With surplus resources from mineral locking, CorsairZealot and CorsairZealotDT move out with 2 zealots more at the same timings as before. I think CorsairZealot is particularly tough on zergs.

code

• Use UnitType::canBuildAddon() instead of checking the building types by hand. Use UnitType::requiresPsi() instead of UnitUtil::NeedsPylonPower(). Know your BWAPI (unlike me)!

• The option Config::Debug::DrawMapInfo shows another bit of info: It draws a red circle over the “front base” that is to be defended (e.g., the natural if Steamhammer owns a main and natural), and another over the “front point” near it where the defense is to go. It connects the circles with a red line, making a red dumbbell. The dumbbell is there to show you how dumb Steamhammer is, and what a weight it still has to lift.

• If you turn on the right debug option, terran and protoss will print a “building supply” message when inserting a supply depot or pylon into the production queue. I moved it from the option DrawBuildOrderSearchInfo (which talks about BOSS stuff) to DrawQueueFixInfo, as a portent of the removal of BOSS in the coming corporate restructuring.

• I made another pass through the Visual Studio settings and found lingering references to BWTA and SparCraft (!). How did those get by? I was sure I’d gotten them all. They didn’t have any bad effect that I know of, but they’re gone now.

• I set the floating point mode for code generation to “fast” instead of “precise.” Not doing numerical analysis here. It doesn’t matter at all if some bits to the far right of the binary point are a little funny.

• In FAP, I changed instances like sqrt(x) < y to x < y * y. Multiplication is cheaper than square root. The VS2013 optimizer may do the strength reduction for me, especially with “fast” floating point, and I’m pretty sure the more powerful VS2017 optimizer does. Anyway, the code seems just as clear to me, so it’s no loss.

Steamhammer 2.3 uploaded

I’ve uploaded Steamhammer 2.3, including Randomhammer. The previous uploaded version was 2.1.4, the tournament version online since December; version 2.0 played in AIST S2 and I did not upload it to SSCAIT because “it would just be a few days” while I fixed up terran and protoss. Famous last words.

For this version I turned on 2 different debug options. It draws map info, which is mainly where the bases are and who it believes owns them (it says “mine” or “yours” on some bases). It overlays a red dumbell shape which points out the “front line” base where the main static defenses will go, and the point that it tries to center the defenses on; you get to see how wrong it is. I also turned on drawing of reserved building tiles in dim gray. Some tiles around the minerals and gas are permanently reserved so that no buildings interfere with mining. Otherwise, tiles are intended to be temporarily reserved until a building goes there. Rarely, a bug causes causes a building to be canceled without unreserving its tiles, wasting the space.

I’m expecting zerg to be modestly stronger because of bug fixes, and terran and protoss to be significantly stronger. Once the elo numbers start to settle, I expect to see Randomhammer moving up.

Stay tuned for the change list and stuff.

next will be Steamhammer 2.3

I thought this release of Steamhammer would take only a few days, but I had “some” scope creep. It is no longer a small release to restore terran and protoss to their shabby glory, but a full release with improvements and a range of fixes. Instead of version 2.2.1, I’ve renamed it version 2.3.

Today I added a feature which changes the current “main base” at times when it makes sense. New buildings go into the main base by preference. Formerly there was a feature that changed the main base at random times, in order to spread out buildings and alleviate the protoss construction bug. It led to a lot of poor decisions.

Now the building placer attempts to maintain at least one pylon near each nexus, so it can build there if necessary. It keeps track of how often building placement fails at each base—how many times the building ends up not placed because no available space had pylon power, or placed outside the base because there was no room inside. When a failure happens at one base, it looks around to see if there is another base which has fewer placement failures, and if so, switches to that as the main base. I hope that switching when necessary will work better than switching randomly.

I just finished a series of tests on the map Transistor, which has small starting bases with room for few buildings. Steamhammer protoss usually overflows into the natural before it puts a nexus there. I got a modest number of slow frames when building placement failed, but nothing disastrously bad. It’s getting close to good enough for now.

I have one more building placement feature to add for protoss, which I expect will make it actually good enough for now. Then I need to review the last few protoss openings. It shouldn’t take more than a few days, so based on recent experience, come back in a couple months.

After that will be Steamhammer 3.0, unless I change my plans again. Release schedule undecided. I’m planning to work on production goals next, a key part of turning abstract strategies into concrete build orders.

experience with the larva trick

I wasn’t planning to implement the larva trick now, because it’s not important. But after testing it for yesterday’s post and finding it easy, the extra work to complete the feature took only minutes, so I went ahead.

Steamhammer determines “the direction to the minerals” from a base’s hatchery by averaging together the offsets of the mineral patches from the center of the hatchery, giving the location of the average mineral patch. (There was already code to do this for another reason, I only factored it out, and it’s barely a few lines anyway.) Once every 20 frames, starting on frame 1 (not 0), it sends Stop indiscriminately to every larva at a base hatchery whose minerals are to the left. It desists after an early game time limit, or when the spawning pool finishes; almost all benefit should be in the early game, and zerglings may be slowed down by starting at the edge of the mineral line.

There are weaknesses to the implementation. When morphing a drone, Steamhammer does not select the larva closest to the minerals, so it often chooses a larva that is still in the process of moving left. Judging by eye, I think this weakness eliminates a third to half of the potential benefit. It’s possible that the indiscriminate Stop commands could sometimes prevent a drone from morphing on time; I didn’t check, but if so it should be rare. Probably a little extra benefit could be captured by doing it all game long, but more smarts would be needed.

The benefit is tiny, as I mentioned yesterday. I tested by playing Steamhammer versus itself with the same opening on Heartbreak Ridge, so that the left base uses the larva trick while the right base does not. In Steamhammer’s implementation, the edge gained by moving drones to the left, so that they begin to mine minerals earlier, is smaller than the natural variation between runs caused by the game’s random differences in timing and position. The technique is worth something, but so little that it was hardly worth implementing, even though it was easy.

An ideal implementation would be better. If you know what units you want to produce in the near future, and what their jobs will be, you can select the larvas of left-facing hatcheries that will become local mining drones and use the larva trick only on them, and do it in time to get them moved into position before you morph them. But that is no longer a simple job, and the benefit will still be small.

The other major use of the larva trick is to control egg blockades. If you have a wall consisting of a hatchery on the right and terrain or another building on the left, and zealots come knocking, you can move a larva to the left of the hatchery and morph it into an egg that blocks the gap. See the game Shark vs Reach (2008), explained on this TeamLiquid thread (it’s an example game on Liquipedia's larva trick article). I’m also fond of a famous 2x2 game from 2006—it may be from before the larva trick was discovered, but the video segment is short and worth seeing.

A larva follows a predictable path after it is tricked. If it is next to the upper half of the hatchery, it takes a route around the top; if in the lower half, around the bottom. The larva moves smoothly, so you can predict where it will be when. With good timing, you can morph an egg where you want, for example to push a unit away from your hatchery to gain time.

six months of bug fixing

Today I fixed what I think is the last serious bug that I introduced in version 2.0 last September. It was the one that causes innocent lurkers which are trying to walk across the map to move stutteringly or to vibrate in place. The bug turned out to also affect medics and defilers, though in a minor way that’s not easy to see.

How can it take half a year to fix serious bugs? I can list several reasons. I think that is a bad sign in itself.

1. Some of the bugs, including this lurker bug, were introduced in 2.0 but did not have visible bad effects until later—in this case, when I updated the micro system and taught it a stricter attitude toward executing commands. Experience says that Steamhammer has many invisible bugs that rarely bite or that have hard-to-see effects or that are complex and hard to trace back to a cause, but still need to be fixed. In any case, bugs that visibly hurt play get priority.

2. Some parts of Steamhammer inherited a UAlbertaBot habit that close enough is close enough, if some expected prerequisite is not there after all... eh, ignore this part of the job. As time goes on I’ve been making Steamhammer more strict about checking its arguments and preconditions, and complaining if anything looks wrong. But today I ran a test: How often does Steamhammer try to issue the same unit different commands during the same frame? That should only happen by mistake, and yet the UAlbertaBot code ignores it. The answer was that it happens many times per game—not often enough to break micro regularly, but there are sure to be bad effects. At some point I have to find time to chase down the causes and fix them, and then add an assertion so it can’t happen again without drawing attention. Bugs must be made visible!

3. Version 2.0 was a major reworking of squad-level tactics. Of course a big ship needs a long shakedown cruise. It didn’t help that I was hurrying to prepare for AIIDE.

4. The squad data structure and code structure are not designed to support the more complicated behaviors that I implemented in 2.0. I added a bunch of awkward bug-prone special cases. In Software Development Utopia, I would have redesigned the Squad class first, and then had a much easier time with the squad behavior. That would be more efficient in the long run, but it wold also stunt my short-term progress. Sometimes we prefer to accrue technical debt.

And the lesson is... um... take a systems view? That sounds good.

There are still a bunch of unfixed weaknesses (as opposed to outright bugs) that were introduced in version 2.0.... And yet the advances in 2.0 were necessary to keep progress going.

Steamhammer’s building placement

Well. So. I sort of got carried away and refactored the building placer. I guess it is a side effect of my instinct to improve any code that I touch. I fixed 3 different bugs. I squeezed out some redundant calculations, so the basic operation “can this building go here?” is faster. And I added a little mechanism to make it easy to throw in different special case building placement algorithms. I see the work as preparation for the more general placement planner that I’ll eventually write.

I think the 3 bugs were all inherited from UAlbertaBot. One was an off-by-one error that could give bad results for the right edge and bottom edge of the map. Another was a different off-by-one error that, half the time, caused the minimum space between buildings to be 1 tile more than configured. Fixing that makes building placement more compact and helps all races. The third bug was so rare that I have never seen it, but theoretically a macro hatchery might be bumped out of one base because it did not fit, and be placed in a neighboring base in a way that blocked the expansion hatchery location. Now I’ll never see it.

I wrote special case building placement for terran. Supply depots, academies, and armories are all 3x2 tiles and can be tightly packed. These buildings are placed in a single row along the edges of the map, as long as there is room. On most competition maps, the main bases are on the edge, so the special case kicks in. In between the slightly more compact building placement because of the bug fix, and the much more compact placement of early supply depots, Steamhammer’s terran gets farther into the game before it starts to sprawl buildings all over the map like Los Angeles. There’s no shortage of room for improvement (puns always intended), but I’m calling it good enough for now.

Now I want to figure out something equally effective... and equally simple for protoss.

attack by defense

Many bots have been in this situation: You’re playing against Tscmoo—and you’ve broken into its base! You’re winning! Your units zero in on the mining workers, which flee... and they’re good at running away. Sometimes they lead the attackers on a goose chase until rescue arrives, and Tscmoo survives to win after all. I’ve seen it happen. Iron is also particularly good at leading goose chases. How do you get your bot to avoid wasting time and concentrate on what’s important?

There are a lot of ways. You can give buildings priority and chase nothing that you can’t immediately hit. You can refuse to chase any unit that is fast enough to escape. You can chase only a certain distance before you turn back. You can attack only units that are approaching, not those that are out of firing range and fleeing (watch out for kiting). You can chase only units that are inside the perimeter of your formation, so that they are partly surrounded and have to dodge well to escape, and then they leave the perimeter and you stop.

I want to suggest another way: Don’t attack, defend an area. Draw a box or a circle around the area you want to attack, and defend it from enemies. Add a margin for hysteresis, so you don’t vacillate if an enemy moves into and out of the area. If you are trying to stop mining, defend an area around the enemy’s resources, including the resource depot. Your squad’s goal is to clear the area of all enemy units and buildings—to defend this part of the enemy base against enemies. If the workers run away, let them; you’ll destroy the resource depot that much sooner. If you’re trying to camp the enemy production (assuming it’s grouped together somewhat), the same idea works by defending a different area. (If your goal is to destroy key tech, then prioritizing those buildings is probably what you want instead.) You’re really attacking, not defending, so once you’ve cleared the defended area, move or enlarge the box and keep up the fight.

Unlike yesterday’s trick strategy that is useful only in special cases, the “defend a box” skill is good in many situations. You may already have the skill and use it for defense; the idea is that “defense” and “offense” are a matter of point of view. To defend overlords from corsairs with hydras, draw a box for the hydras to defend and keep your overlords there. Similarly to defend a mineral line against, say, wraith harass. For an offensive example, take a slow tank push. You can implement it by telling the tanks to defend an area, and gradually moving the area forward. Tanks in the rear will unsiege as the defended area goes out of range, move to the front, and siege again, and that’s a push. The same thing works for a lurker push.

note on Steamhammer bugs

Revising building placement for terran and protoss is taking longer than I expected. My simple plan was so simple that it was wrong (listen to Einstein!), and I had to throw out some bad work. I’ve decided that’s my last mistake; from here on out, I will be right every time. Even though I’m right now, I still need to put in the work.

I fixed the bug that causes dark templar to move erratically. It turned out that hiding behind it (how do you hide behind something invisible?) was a second bug that affects dark templar and makes DT drops useless (after fixing the bug that caused the transport to fail to load). Haven’t diagnosed the second bug yet.

Working through the openings helped me catch the major bugs, some of which have been around since September. The upside is that terran and protoss will be that much better.