archive by month
Skip to content

Steamhammer status

As I write, in SSCAIT Steamhammer is tied with BananaBrain for #8-#9, at just under 80% win rate. I’m happy with it. But Steamhammer is one of only 2 bots (the other is #26 NLPRbot) which has not yet played any games against the current top 5. I expect to take losses and slip down in the ranking. A finish near the middle of my predicted range of #7-#13 seems likely.

For some reason I don’t feel like analyzing CherryPi and SAIDA yet, though I’m sure I’ll get around to it. I’ve been working on the upcoming Steamhammer 2.2 instead. I’ve removed all references to BWTA other than regions, and I’m making good progress in implementing my own regions; the outline of the code is there, and supporting data is calculated and looks correct. Finish that and replace references to BWTA regions, and only a few small odds and ends remain before I can drop BWTA. Debugging time is unpredictable, because my regions will be different and may affect play. But I’m likely to be done in time for the end of the tournament.

Today I decided to take a break from that and fix a critical bug instead. I verified what I have been suspecting: There is a deadly one in the new micro system, causing it to reissue commands far too often. That makes the APM shoot over 90K and probably saturates Starcraft’s queues so that commands are lost in transit. That explains unit freezing and misbehavior once the zerg army grows large, and I hope it explains the production freezes too. The bug is resisting me so far; something behind the scenes is mysteriously resetting the “yeah yeah, I already did that” marker. But the code is simple and I’ll nail it before long. The bug is responsible for a lot of bad play during the tournament, but strangely for only a few losses.

As the tournament approached I was trying to make low-risk changes. The code is simple, but I still misstepped. I think my first cut of the micro system was OK, since it passed tests then, and the bug was introduced in later changes as I “improved” it.

the “no shallow forks” rule in SSCAIT

The SSCAIT rules include 2 items about copying other bots. Here’s the first item:

The SSCAIT admins reserve the right to disqualify any bots that are implementation-wise too similar to previous entries (e.g. clones and forks) without adding anything novel / original to the table.

On the upside, it openly admits that the decision is a judgment call, and otherwise it is simple. On the downside, it offers little guidance on what counts as “too similar.” On balance, I like this rule.

The second item is listed separately and seems to have been inserted independently:

If you copy other bots or use IP/files/source code/logic/techniques etc from other bots, you must familiarize yourself with their licenses and ensure that you are not infringing their licenses. Copying other bot(s) is allowed, so long as it does not infringe their licenses and so long as you modify their logic or if you use/wrap it without modifying it and add some of your own logic on top of it, similar to how MegaBot used Skynet/Xelnaga/NUSBot in year 2017, or wrapping/modifying Randomhammer/UAlbertaBot/CommandCenter etc. If you do something like this, you must provide the source code and compilation instructions etc of the bots that you use, so that we can compile them. We decided that to foster research it is best to have the next generation of programmers stand on the shoulders of giants, rather than re-invent the wheel. We encourage authors to take code from old years of this competition and improve it. If you copy a bot, please uphold the spirit of competition and ensure you make significant modification or addition before you submit it. We don’t want multiple apparently near-identical copies of the same bot competing! Additionally, please contact the original bot (in case it has been updated at least once during the past year) author and ask them for permission to upload it.

This paragraph looks like it was partly copied from CIG rules, which were themselves partly copied from old AIIDE rules. Not all of it makes sense for SSCAIT. It gives the impression of having been dropped in without careful thought. It declares multiple rules. In more detail:

The rule about obeying licenses is good, except that borrowing a “technique” from another bot is universal. If you use MCRS, do you have to obey the license of UAlbertaBot because UAlbertaBot with SparCraft invented the technique of making Starcraft decisions by combat simulation? The technique that has been accepted as standard and used nearly everywhere? That makes no sense at all. The word “technique” without qualification is too vague to interpret; leave it out.

Next is some wording about when it’s OK to copy other bots. It’s unclear how much this is establishing a rule and how much it is providing guidelines to interpret the rule already established that something must be “original / novel.” In any case it overlaps with the first item, and if it’s worth keeping at all, part at least should be combined into that paragraph.

Then there’s a strange and awkwardly-worded rule that seems to require source code of the original bot that you wrap or modify, “so that we can compile” it. I don’t think SSCAIT does this. Am I wrong? Is anything similar to this rule enforced at all, or even understood by the organizers? It has the smell of a copy-paste error.

Finally, there is a rule about asking the author for permission. I don’t like that rule. I deliberately chose a license that does not require forks to ask permission, or even to let me know. As far as I am concerned, the license grants permission; to require anything from me is a waste of my time. Instead of this rule, I propose a suggestion to authors that they may want to consider whether to add a permission requirement to their license. Then the obey-the-license rule is all that’s needed.

ambiguous cases

Here are 3 edge cases related to Steamhammer. Maybe they can get people discussing what guidance to add to the rules.

Steamhammer 0.2 was forked from UAlbertaBot and played in SSCAIT 2016 after about 3 weeks of development. I made bug fixes and macro changes and stuff, but 3 weeks is not long enough for substantial code changes. Nevertheless, from the start Steamhammer’s strategy and game plan were very different from UAlbertaBot’s. Nobody suggested that it might be too similar, even though the tactics and micro were nearly identical.

NLPRBot is a 2017 fork of Steamhammer, and has not been updated. The link is to a blog post where the comments discuss what to do about shallow forks. It was accepted in tournaments in 2017 under the rules of the time. SSCAIT chose to accept it in 2018 too, though if today’s rules had been in effect in 2017 it probably would have been rejected then. I think that allowing it in 2018 is fair; NLPRBot plays like an old Steamhammer, differently from the current one, so it still adds variety. But it is an edge case, and there is an argument for rejecting it.

insanitybot is a recent fork of Steamhammer. It adds wraiths and minelaying skills and has other changes, but you could argue that it is a shallow fork; its play is in many ways closely similar to how Randomhammer plays terran. I think it is right to accept it into the tournament, because of the improvements and because Steamhammer doesn’t play terran except as random. But again, it is an edge case.

SSCAIT runs of upsets

I am amused: As I write, Steamhammer is ranked #11 in win rate in SSCAIT. It has played 4 games against players ranked higher—and won all of them. The games are 1-0 Krasi0P (I expect the second game to also be a win), 2-0 Killerbot by Marian Devecka, and 1-0 Hao Pan (the second game could go either way). All the losses that pushed Steamhammer down to #11 were against weaker players. That’s a change from last year, when Steamhammer performed consistently and had comparatively few upsets in either direction. Some losses are caused by bugs (0-1 XIMP by Tomas Vajda) or by Steamhammer’s standard weak play (0-1 AILien), but I think the lion’s share are due to poor opening choices by the opponent model: Steamhammer is performing inconsistently because it is thinking on its own.

Current #14 LetaBot by Martin Rooijackers has a similar record: It has played 6 games against higher-ranked players and won 5 of them, including its game against Steamhammer (it happens sometimes).

Overall, I think the biggest upset so far is #50 KillAll 1-0 #2 PurpleWave. Of course the tournament is only about 1/4 over.

another building delay bug

Constructing buildings efficiently is one of Steamhammer’s most fragile behaviors, because it involves coordinating across modules in an ad hoc way. In my change list for the latest version 2.1.4, I described a bug that delayed the start of buildings. The bug was an interaction between the production manager, the building manager, and the building placer.

Now I’ve discovered another bug with the same symptoms—an already placed building is re-placed elsewhere, causing a delay—that also involves 3 modules. This time it’s the building manager, the production manager, and the worker manager. This bug bites when a pre-positioned worker arrives too early at its designated location and has to wait for resources or tech to be ready. How clever of the first bug, to hide the second one from my view. I think computer bugs evolve camouflage, just like living bugs; hide or die.

Now I’m trying to restructure the interaction to be more robust. My preliminary plan is to pass control to the building manager as early as possible and put it in charge of the rest, to try to keep the module interactions simple and organized. It will involve storing a little more state.

Meanwhile, in SSCAIT I’m not seeing many surprises. Not enough games have been played to firm up the rankings, so my range of expectations is still wide—I guess it’s normal that nothing much is unexpected. Steamhammer has about the right mix of wins and losses in games that could have gone either way. It is entertaining that the score table only gives a rank to bots that have played 30 games, while the games are doled out randomly. At the moment, #1 is Soeren Klett with 15-15, and #2 is Jakub Trancik with 11-19. Visiting space aliens will not understand how the competition works.

AIIDE 2018 - 2 locutusoids dropped

Dave Churchill decided to drop the locutusoid bots BlueBlueSky and ISAMind from the AIIDE 2018 tournament results. They were, he must have concluded, too similar to Locutus. The locutusoids #3 CSE and #5 DaQin were kept. The change switches the order of 2 pairs of participants who had close scores: Iron moved ahead of McRave, and Steamhammer moved ahead of ZZZKBot. Steamhammer is now officially #8 out of 25 (no longer #11 out of 27). #12 Tyr, with a 48% win rate, is now in the upper half of the rankings, which reinforces how much stronger the top bots were.

I only noticed today. No doubt I’m behind the times.

The change puts my analysis out of sync with the official results. You may have to refer to my result tables instead of the official tables, to see the ranking numbers I used.

SSCAIT 2018 prospects

The tournament starts with the usual round robin. There are 72 players, down from 78 last year due to stricter rules and a trend to disable weaker bots. The top 16 round robin finishers will go on to a final in a knockout format. Last year I predicted—not with full confidence—that Steamhammer would finish between #4 and #8. It ended up dead in the middle at #6. My predictions are not usually that accurate, but I won’t let that stop me.

First, let me get a handle on the top bots. Some familiar players are out, either by the author’s choice or by the rules.

Krasi0Wacky Krasi0P, not powerful Krasi0 terran.
SAIDAUpdated with scary new skills.
LocutusUpdated.
BananaBrainUpdated.
PurpleWaveSwitched in on the last day as a “surprise”.
Killerbot(Marian Devecka) Updated.
CherryPiOut, due to shared author rules.
Tscmoo familyTscmoo protoss.
DaQinOut, presumably because it is too like Locutus.

SAIDA is of course the favorite for #1. Obviously the protoss trio Locutus, BananaBrain, and PurpleWave should finish high. PurpleWave is a black box. It was left disabled in the run-up to the tournament, no doubt while the author prepared, then switched in on the last day: It is prepared for us, we are not prepared for it. Killerbot by Marian Devecka is the other top contender. These 5 will fill in the top 3 almost surely, and likely the next 2 places as well.

Iron should do well too, though it has fallen behind. Hao Pan has improved greatly. McRave seems to remain inconsistent against weaker opponents, but should perform well. I find it hard to foresee how the lower end of the top 16 will fill out. For the Tscmoo family, the author did not disable unwanted variants, so the organizers did it instead, choosing Tscmoo protoss as the representative. (Last year they said they’d choose randomly; this year I don’t see a decision criterion stated.) Tscmoo can surprise, but this version doesn’t look like a contender. XIMP by Tomas Vajda is no longer likely to finish in the top 16, since its rivals have learned to cope—that is a change, last year it finished #12.

For Steamhammer, I am disheartened by a couple of undiscovered serious bugs in its very last game before the tournament began. Still, I did fix other bugs, and the buggy test versions performed fairly well. I am predicting Steamhammer to finish from #7 to #13 and safely make it into the final—again, not with full confidence.

“Good luck to everyone” is a wimpy wish. Absolute victory to everyone! Crush all in your path!

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.

tournament-ready Steamhammer 2.1.4

Steamhammer 2.1.4 is uploaded. It’s ready for the tournament, and it will be the tournament version unless I slip in a few last minute changes. Source release and change list tomorrow, or thereabouts (I have stuff to do that might cause a delay).

This version does have a known bug with transport loading that affects terran and protoss, so the terran and protoss drop openings are not working properly. Also unit clustering interacts with behaviors for a few protoss units: Reavers move far from home before they start to build scarabs; carriers before they start to build interceptors; high templar before they merge into archons (which is all they can do for now). I may make an interim release in the 2.1.x series before the tournament is over to fix these for anybody who wants to download from my site. Otherwise, I expect the next version to be 2.2 after the tournament; headline feature: Dropping BWTA.

the 4 cooperating bugs

Yesterday I pointed out a game versus CUBOT where Steamhammer failed to finish off the enemy and had to win on points when the game timed out. It was due to 4 bugs, and I listed the bugs. Today I want to expand a little: The bugs were all necessary for the bad result. If any one of the bugs were not there, Steamhammer would have destroyed the last building, the enemy extractor, and finished the game on its own.

These are important bugs, because it is important to kill CUBOT fast so it stops spamming how much it needs gas.

1. The extractor was considered inaccessible by ground, so ground units did not try to attack it. The tactical targeting does not send a squad to attack a target that the squad can’t reach. Steamhammer decides ground accessibility like this: If the map were empty, could a unit walk to the position? You can’t walk on a geyser, so the check is incorrect in that case.

If ground units happen to wander close enough to the extractor, they will attack it anyway. The micro targeting takes over. The can’t-reach-a-refinery bug is a known problem, and I never fixed it because, until this game, Steamhammer’s other behaviors had always been robust enough to eventually find and kill any leftover refinery buildings.

2. The Ground squad knows there’s an enemy building left but believes it can’t reach the building, so its job is to explore the map for other enemies. It decided to check the enemy’s mineral-only expansion. Unfortunately, the shortest path there is blocked by a mineral block, and Steamhammer does not know how to path around the block or remove the block. So the Ground squad could make no progress.

3. The Recon squad cannot get jammed in the same way, because if it fails to reach one target, it times out and switches to another. It should eventually check everywhere and kill the extractor. But in this game, a single zergling froze in place at the top of the enemy upramp, and the other zerglings of the Ground squad were positioned so that they could not pass it to reach the downramp that they could not pass because of the mineral block. Stuck units are less common, I think, but still happen. The Ground squad was effectively frozen into a position where it blocked the Recon squad from ever reaching the enemy extractor.

4. Mutalisks don’t care about ground reachability. Despite all the above problems, Steamhammer still would have finished the game on its own if it had continued normally, teching up to mutalisks. But a never-before-seen bug froze production, and Steamhammer stopped making units altogether. I think I’ve diagnosed the bug, and I will fix it.

None of the 4 bugs is critical on its own. They had to cooperate closely, even to the point of freezing one zergling in the right position and placing others around it carefully so they could not move forward or back. Maybe instead of fixing the bugs, I should find the coordinating committee and disband it.

academy and cybernetics core are special

A thought while I work on more bug fixes, like the 4 bugs responsible for this absurdly bad game. (1. The enemy refinery was considered inaccessible by ground and not attacked. 2. The squad tried to take a path down the ramp which is blocked by minerals. 3. A zergling was stuck in a key position at the top of the ramp. 4. Production froze due to an undiagnosed bug which I have never seen before.)

Although an academy has zero gas cost, terran has no reason to build an academy without taking gas first, or at latest around the same time the academy is built. All uses of the academy require gas. (A refinery has half the build time of an academy, but you also have to gather some gas. The rule relies on the fact that you never want to spend that much to get 1 medic or 1 firebat.) Protoss has no reason to build a cybernetics core without taking gas first, for the same reason. These are the only 2 buildings with that property. All other buildings either require gas to be built at all, or if they have zero gas cost, they enable production that also has zero gas cost. For example, an engineering bay, forge, or evolution chamber might be used to get static defense, which costs only minerals. Zerg has no building with this property. (Imbalance!)

If you generate your own build orders automatically, you can use this as a constraint on which build orders are acceptable.

Steamhammer 2.1.3 test version

Yesterday’s version 2.1.2 turned out to have a serious bug that occurs on SSCAIT and not in my test environment. The problem is that a drone on the way to start a building may be reassigned in the middle of its journey, and another drone sent to build instead. If it happens once, it causes a delay in construction—and it often happens repeatedly. Today’s version 2.1.3 tries to fix the bug. I can’t reproduce the bug, even though it happens in every game on SSCAIT, but I know what code is responsible for reassigning drones. I made a couple of improvements that are valuable in their own right, in the hope that one of them will also fix the bug. If not, I don’t know what I’ll do. I don’t want to roll back because my changes are improvements otherwise, and the deadline looms.

Update: Whew, my fixes worked.

Steamhammer 2.1.2 test version

I’ve uploaded the next test version to SSCAIT, Steamhammer 2.1.2. The biggest change is that stuck units are less common. There is still an issue where units can occasionally freeze en masse in the late game, but it’s rare in my tests and so far I’ve only seen it when Steamhammer was winning and it didn’t matter. To fix sticking, I finished up and enabled part of Steamhammer’s new unit control infrastructure, work that has been inching forward for months. I didn’t change anything else or take any unsticking actions; the new structure usually works better by nature than the classic structure inherited from UAlbertaBot.

There’s also a fix for a building manager bug that incorrectly turned expansion hatcheries into macro hatcheries. The bug sometimes made play much worse, but also sometimes made it better, so testing is called for.

AIIDE 2018 - what CherryPi learned

Here is a table of how each CherryPi opening fared against each opponent, like the tables I made for other bots. Reading the code confirmed my inference that the learning files recorded opening build orders, not build orders switched to later in the game; see how CherryPi played.

#bottotal10hatchling2hatchmuta3basepoollings9poolspeedlingmutahydracheesezve9poolspeedzvp10hatchzvp3hatchhydrazvp6hatchhydrazvpohydraszvpomutaszvt2baseguardianzvt2baseultrazvt3hatchlurkerzvtmacrozvz12poolhydraszvz9gas10poolzvz9poolspeedzvzoverpool
#1saida13-90  13%-----1-19 5%------1-15 6%9-37 20%2-19 10%----
#3cse73-30  71%-----0-2 0%24-5 83%--16-8 67%----33-15 69%----
#4bluebluesky89-14  86%-----0-1 0%29-8 78%-------60-5 92%----
#5locutus84-19  82%--63-11 85%-----14-3 82%-2-2 50%---5-3 62%----
#6isamind99-4  96%--1-0 100%-----98-4 96%----------
#7daqin103-0  100%--------------103-0 100%----
#8mcrave87-16  84%--9-2 82%-----31-4 89%-14-4 78%---33-6 85%----
#9iron97-6  94%----97-6 94%--------------
#10zzzkbot93-10  90%58-4 94%--0-1 0%-------------35-4 90%0-1 0%
#11steamhammer81-21  79%22-7 76%----16-5 76%---------0-1 0%-43-8 84%-
#12microwave94-9  91%----------------0-1 0%4-2 67%90-6 94%
#13lastorder85-18  83%45-7 87%----0-1 0%------------40-10 80%
#14tyr98-5  95%------98-5 95%------------
#15metabot94-2  98%---------94-2 98%---------
#16letabot101-2  98%0-1 0%-97-0 100%--1-1 50%-----3-0 100%-------
#17arrakhammer92-11  89%-----------------92-11 89%-
#18ecgberht102-1  99%--------------102-1 99%----
#19ualbertabot99-4  96%---96-2 98%-3-2 60%-------------
#20ximp98-5  95%-------1-0 100%-97-5 95%---------
#21cdbot103-0  100%-----96-0 100%-----------7-0 100%-
#22aiur100-3  97%---------100-3 97%---------
#23killall103-0  100%102-0 100%-----------------1-0 100%
#24willyt103-0  100%-103-0 100%-----------------
#25ailien103-0  100%-----------------103-0 100%-
#26cunybot100-3  97%-----------------100-3 97%-
#27hellbot103-0  100%------31-0 100%--72-0 100%---------
overall-  90%227-19 92%103-0 100%170-13 93%96-3 97%97-6 94%117-31 79%182-18 91%1-0 100%143-11 93%379-18 95%16-6 73%3-0 100%1-15 6%9-37 20%338-49 87%0-1 0%0-1 0%384-28 93%131-17 89%

Look how sparse the chart is—CherryPi was highly selective about its choices. It did not try more than 4 different builds against any opponent. It makes sense to minimize the number of choices so that you don’t lose games exploring bad ones, but you have to be pretty sure that one of the choices you do try is good. Where did the selectivity come from?

The opening “hydracheese” was played only against Iron, and was the only opening played against Iron. It smelled like a hand-coded choice. Sure enough, the file source/src/models/banditconfigurations.cpp configures builds by name for 18 of the 27 entrants. A comment says that the build order switcher is turned off for the hydracheese opening only: “BOS disabled for this specific build because the model hasn’t seen it.” Here is the full set of builds configured, including defaults for those that were not hand-configured. CherryPi played only builds that were configured, but did not play all the builds that were configured; presumably it stopped when it hit a good one.

botsbuildsnote
AILienzve9poolspeed zvz9poolspeedreturning opponents from last year
AIURzvtmacro zvpohydras zvp10hatch
Arrakhammer10hatchling zvz9poolspeed
Ironhydracheese
UAlbertaBotzve9poolspeed 9poolspeedlingmuta
Ximpzvpohydras zvtmacro zvp3hatchhydra
Microwavezvzoverpool zvz9poolspeed zvz9gas10pool“we have some expectations”
Steamhammerzve9poolspeed zvz9poolspeed zvz12poolhydras 10hatchling
ZZZKBot9poolspeedlingmuta 10hatchling zvz9poolspeed zvzoverpool
ISAMind
Locutus
McRave
DaQin
zvtmacro zvp6hatchhydra 3basepoollings zvpomutas
CUNYBotzvzoverpoolplus1 zvz9gas10pool zvz9poolspeed
HannesBredbergzvtp1hatchlurker zvt2baseultra zvt3hatchlurker zvp10hatch
LetaBotzvtmacro 3basepoollings zvt2baseguardian zve9poolspeed 10hatchling
MetaBotzvtmacro zvpohydras zvpomutas zve9poolspeed
WillyTzvt2baseultra 12poolmuta 2hatchmuta
ZvTzvt2baseultra zvtmacro zvt3hatchlurker zve9poolspeeddefaults
ZvPzve9poolspeed zvtmacro zvp10hatch zvpohydras
ZvZ10hatchling zve9poolspeed zvz9poolspeed zvzoverpool
ZvR10hatchling zve9poolspeed 9poolspeedlingmuta

I read this as pulling out all the stops to reach #1. They would have succeeded if not for SAIDA.

banditconfigurations.cpp continues and declares some properties for builds including non-opening builds. It looks like .validOpening() tells whether it can be played as an opening build, .validSwitch() tells whether the build order switcher is allowed to switch to it during the game, and .switchEnabled() tells whether the build order switcher is enabled at all.

The build orders themselves are defined in source/src/buildorders/. I found them a little hard to read, partly because they are written in reverse order: Actions to happen first are posted last to the blackboard.

The opening zve9poolspeed (I read “zve” as zerg versus everything) has the most red boxes in the chart—it did poorly against more opponents than any other. It may have been a poor choice to configure for use in so many cases. In contrast, zvz9poolspeed specialized for ZvZ was successful. It gets fast mutalisks and in general has a lot more strategic detail coded into the build.

They seem to have had expectations of the zvt2baseultra build against terran. It is configured for HannesBredberg, WillyT, and the default ZvT. It was in fact only tried against SAIDA. I didn’t notice anything that tells CherryPi what order to try opening builds in. Maybe the build order switcher itself contributes, helping to choose the more likely openings first?

Steamhammer 2.1.1 test version

I’ve uploaded Steamhammer 2.1.1 to SSCAIT. It’s a test version that I don’t expect to release source for. I’ll follow a plan similar to last year’s: Upload a series of test versions as the tournament approaches, in hope of catching any new bugs and weaknesses before it is too late. The version that goes into the tournament will be whatever is ready by then, and that is the version that I’ll do the full release job for. Last year I used “1.4a1” and so on as test version numbers. This year it will be “2.1.x”.

There are quite a few changes, and they are probably not what you are expecting. I count 5 fixes for serious bugs that could easily lose games, plus mitigations to reduce 2 important weaknesses, plus a bunch of quick corrections and tweaks for less important issues that came up. There is still 1 major bug at the top of my list to fix before the tournament, and we’ll see whether I’ve introduced any awful new weaknesses—some of the changes are risky, it would not be a surprise.

I disabled Randomhammer until after the tournament. It’s not broken or anything, I just felt like turning it off in plenty of time this year. Everybody else will get in a few more last-minute games.

I changed the debug drawing options to show off a new option that I added, DrawHiddenEnemies. It draws 4 symbols to represent known enemy units and buildings that cannot be seen at the moment: An enemy that is out of sight gets a small green circle at its last seen location, or a yellow circle if it was burrowed when last seen (especially useful for spider mines and lurkers). An enemy that is out of sight and is known to be no longer at its last seen location gets a red X instead. And an enemy which is in sight and is not detected gets a larger violet circle (a faint color that I think of as representing the cloaking field) and is labeled in white with its unit type. The picture is from a game versus Iron. The yellow circles are known spider mines outside detection range, and the green circle and red X mean that at least 2 more enemies are out of sight, likely nearby.

hidden enemies symbols

I’m not finished with AIIDE 2018. I want to analyze aspects of CherryPi and SAIDA. I’ll squeeze that in too, sooner or later.