archive by month
Skip to content

blocking expansions

Today I wrote about a dozen new burrow openings (mostly easy variations of existing openings). If burrow has many uses, there should be many timings to get it at. I also made a first pass at resource tracking, so that Steamhammer can finally stop expanding to bases which are mined out—and also as an input for strategy decisions. The last seen mineral amounts are a starting point to estimate the enemy’s total minerals mined, which is valuable for figuring out what they’re doing. It worked on the first try for minerals; vespene geysers are more complicated. I figure that just after a tournament deadline is the ideal time to add new basic infrastructure.

blocking an expansion

What is the ideal place for one of your units—a spider mine or a burrowed zergling or a pylon or e-bay in blocking position—to block an expansion? Steamhammer uses the center of the resource depot location, and as far as I’ve seen so do all other bots. But play a game against the built-in AI and burrow a unit there: It cheats and knows about the burrowed unit, and simply offsets its resource depot to avoid it. Krasi0 also has the skill to offset its resource depot, it just has to scan first. Mining is less efficient, but the base does go up.

Maybe the ideal place is one that forces the opponent to offset its building as far as possible from the right location. If the minerals are in a line to (say) the north of the base location, then let your blocker overlap the southernmost row of tiles where the resource depot should go. That forces a displaced resource depot as far from the minerals as possible. Similarly for the geyser. In a circular base layout, as found in the center of some maps, one or two burrowed units could block the entire circle.

I’ve seen pros place blocking buildings off-center like that to dissuade the opponent from displacing their resource depot. A bot could do a search to find the placement that forces the depot as far as possible from minerals and gas. The answer is different for a spider mine or burrowed zergling than for a building.

There are other considerations. The blocking unit provides vision; maybe you can place it so that it sees everything that comes down the enemy ramp. For a pylon, maybe you want to put it as near the natural choke as possible so that you have a better chance to send units in to defend it, or to use it to power a gateway or shield battery that might be out of enemy sight. That kind of thing.

Eh, not a big issue for any bot. But it reminds me how complicated the game is. In the long run, we have to seek methods that can weigh many considerations—and nothing stops us from seeking the methods now.

Steamhammer 2.4.2 change list

Steamhammer 2.4.2 is available as source from Steamhammer’s web page. The documentation is updated too. As far as game play goes, it is identical to version 2.4.1 in the SSCAIT annual tournament. The main difference is that Steamhammer can automatically recognize when it is running under SCHNAIL and treat its opponent as a human.

I hope SCHNAIL testers will let me know how it goes, so I can adjust the behavior against human players.

Here is what’s new.

configuration

  "Skills" :
  {
    "SCHNAILMeansHuman"       : true,
    "HumanOpponent"           : false,
    "SurrenderWhenHopeIsLost" : true,

    "ScoutHarassEnemy"   : false,
    "AutoGasSteal"       : true,
    "RandomGasStealRate" : 0.0,

    "Burrow"             : true,
    "MaxQueens"          : 1,
    "MaxInfestedTerrans" : 0
  },

• The SurrenderWhenHopeIsLost, ScoutHarassEnemy, AutoGasSteal and RandomGasStealRate items are moved from the Strategy section to the Skills section of the configuration file.

• An internal flag Config::Skills::UnderSCHNAIL is added. It does not appear in the configuration file but is set by code. It is true when Steamhammer detects SCHNAIL’s schnail.env file in the read directory. Code can use this to do something different when running under SCHNAIL; it may be useful someday.

• A flag SCHNAILMeansHuman is added. If UnderSCHNAIL and SCHNAILMeansHuman are both true, then Steamhammer overrides the configured value of the HumanOpponent flag and sets it to true.

In other words, if you set SCHNAILMeansHuman to true, then whenever Steamhammer is running under SCHNAIL, it will assume that its opponent is a human. That should almost always be what you want. If it’s not what you want, you can turn off SCHNAILMeansHuman and set the HumanOpponent flag by hand.

• Steamhammer messages “gl hf” at the start of the game if it thinks the opponent is human. It actually wishes the human to have bad luck and suffer torment (BLAST), but it doesn’t mind lying. The real purpose of the message is so you can tell whether the HumanOpponent flag is turned on when it should be.

• The game message was formerly messed up. I think I finally fixed it.

• In the IO section of the config file, I separated Config::IO::PreparedDataDirectory (bwapi-data/AI/om/ for prepared opponent model files) from Config::IO::StaticDirectory (bwapi-data/AI/) for reasons of what I prefer to call clarity. The change doesn’t affect anything but a name in the code.

Steamhammer 2.4.1 change list

Steamhammer 2.4.1 is the version for the SSCAIT 2019 tournament. Version 2.4.2 with source release should follow shortly with only one significant change, SCHNAIL recognition.

For the tournament, I replaced the SSCAIT learning data with data from BASIL, because BASIL has more games that are recent. There are downsides; it could be that it was a mistake. I made a small number of hand edits to the learning data. For example, for StyxZ, I deleted games from before Styx was updated and suddenly became strong. I edited data for 7 opponents in total, usually just altering one game record.

First an explanation of my supposed Killer Feature, then the change list proper.

the Killer Feature

The “Killer Feature,” which is no more deadly than your average killer app, is to burrow zerglings at bases that the opponent is likely to want to take soon. CherryPi did this a couple of years ago, and I was surprised that bots which know how to cope with spider mines laid to block base locations were often unprepared for burrowed zerglings doing the same thing. Two years later, bots which remain unprepared may get into trouble. Burrow has many many uses, and Steamhammer will take advantage of more of them in the future; in choosing a burrow skill, I am looking ahead. There are 3 parts to the system.

The strategy boss by default researches burrow after Steamhammer takes its third base. If the opening build takes three or more bases, it starts the research shortly after the opening. This is late enough in the game that the research is not a major expense, but it does lose the opportunity to exploit burrow early on.

The 9PoolHatchBurrow opening researches burrow much earlier. It is for use against opponents which may be vulnerable. 9PoolHatchBurrow is a variant of the Styx opening which collects 200 gas instead of 100 and researches burrow immediately after zergling speed, trading some of the zergling punch for the burrow ability. The two upgrades finish at almost the same time, because burrow (1200 frames) doesn’t take as long as speed (1500 frames). The aim is to get burrow early while pressuring with zerglings, and try to burrow a zergling in the enemy’s natural to delay or displace the expansion and mess up the enemy’s build. Burrow normally finishes before the enemy has scan or observers, so even an opponent which knows how to react may be delayed.

I tweaked the learning data so that this build will be given a try against a few opponents.

The Watch squads are for scouting and base denial. The design attempts to gain the most impact with the least investment. The combat commander creates from 0 to 4 Watch squads (depending on Steamhammer’s ground strength and other factors) of 1 zergling each, and assigns the squads to watch the bases closest by ground to the enemy main. It may also assign overlords to watch further bases, but usually by the time there are overlords to spare, it is not safe to assign them. A Watch squad behaves like any other squad, except that if a squad member finds itself at the order position and is able to burrow, it does. (It’s literally a few lines of code.) Once at least one zergling is under the ground, the combat commander attempts to keep the most important Watch squad on station despite any reverses, because to disband it would throw away the investment.

When Steamhammer decides to expand to a base, any Watch squad stationed there is disbanded immediately. The zergling unburrows and joins the ground army long before the drone trundles in to make the hatchery. The disbanding is 1 line of code in a condition in the combat commander, and the unburrowing and leaving is standard behavior that was written into the squad long ago. The whole Watch implementation is as simple as I could make it.

I’m eager to see how the Watch implementation works in practice. When Steamhammer faces a full range of opponents, the results of a new feature are always more complicated than I see in my testing.

configuration

• I added a new configuration section Skills which is intended to separate out some items from the long Strategy section. For now, it has Burrow, MaxQueens and MaxInfestedTerrans items. Later I’ll move some items over from Strategy.

For this SSCAIT, Burrow is true, MaxQueens is 1, and MaxInfestedTerrans is 0. The strategy boss is willing to make up to 2 queens, but I decided that the second one probably costs more than it is worth for now. Steamhammer has a bug in the production system that prevents it from making infested terrans, so it’s better not to try.

infrastructure

UnitInfo keeps track of whether a terran building was lifted when it was last seen. For some reason I resisted doing this earlier, but it’s important information. The first use is in squad targeting, when checking whether an enemy building is a good target for a given squad.

• Another use of the terran lifted building info is a new skill: If all known enemy buildings are lifted and all known enemy units are air units, Steamhammer will only include anti-air units in its unit mix. It sounds obvious, but ultra-ling is a potent way to finish off a zerg or protoss which has only air units left, and is only weak versus terran that has lifted buildings. This skill is intended to solve a rare difficulty in finishing off a terran—I have seen it happen in only 2 games.

• Don’t try to expand to a base which is known to be blocked by an enemy building or burrowed unit. There’s no point in sending a drone to build on top of a spider mine which has been scouted. This also, of course, checks whether the building is lifted.

• Efficiency fix: Use base->getTileDistance() precalculated ground distances in some places instead of recalculating the distances.

Config::Debug::DrawMapDistances now draws tile distances from its current main base rather than from its starting base. You can see what Steamhammer considers to be its current main base, which may change during the game.

• I removed the unfinished and unused Region and Regions classes. I had forgotten they were there.

zerg

Research Burrow, if enabled in the configuration, usually just after the third base is taken. The strategy boss will delay the research for some emergencies or if the economy doesn’t justify it.

The Watch squad scouts and denies bases. It is implemented for zerg only. Other races don’t have enough cheap units to dedicate some to sitting around, and are better off with the Recon squad alone.

A critical bug in upgrade checking could cause production freezes. Ouch. Fixed.

• In ZvZ, get +1 melee attack instead of +1 ground carapace as the first ground upgrade. It’s cheaper, and it’s what humans usually do. I think the theory is that killing drones faster is more important than a small edge in resisting mutalisk bounce attacks.

openings

The 9PoolHatchBurrow opening is added. See above.

• Minor tweak to ZvP_3BaseSpire+Den. It gets 1 more drone.

Steamhammer and next steps

Last night I dreamed that my hatcheries came under air attack by fire-breathing dragons. I had to defeat them with scourge. Did you know that dragons are as tough as battlecruisers or carriers? Six scourge each, it takes a lot of gas to shoot them down!

Meanwhile, Steamhammer is ready for SSCAIT, and has been for a few days. I have been running tests, finding that all is good enough, and refusing to make other fixes or changes. SCHNAIL recently added a file so that bots can recognize when they are running under it, and I even skipped checking for that to reduce the small risk. The Killer Feature works as intended and causes headaches for some opponents, though by no means all. I expect it will be good for several wins, and certain amount of “Ah, now I have to be ready for THAT?” Whatever makes Steamhammer’s play more complex and interesting is worth having.

Immediate plans: Steamhammer 2.4.1 change list shortly after SSCAIT submission closes. Then Steamhammer 2.4.2 a little after with one more change, SCHNAIL recognition so that running under SCHNAIL automatically turns on the HumanOpponent flag. I’ll release source for the 2.4.2 version.

What should I do next? Here are the fixes that would make the biggest difference in the short run:

• Overlords, surprisingly, are not handed out for free. Keep them safe.
• Don’t transfer drones by the dozen through the enemy army.
• Correct gross weaknesses in defense.

The development plan calls for strategy adaptation work. Next steps I could take toward that include:

Improved scouting.
Production goals.
• Opening timing for decision making.

Then there’s the fun stuff. I wrote down about a dozen different tactical tricks suitable for zerg (and a few more that only other races can play), ranging from proxy hatchery to stop lurkers. Three of them are played by current bots, one other in past years by a long-defunct bot, so there is opportunity to surprise opponents. I imagine a bag-of-tricks meta-skill that combines information to decide when to try a trick: Map analysis (“aha, the main has a back door”), cost estimates and success rates, the game situation, and opponent model data. Of course it would record results in the opponent model, so it could learn how to exploit different opponents. That would be cool in itself, and it would also provide a way for forks to customize their behavior.

Looking over the possible next steps, I see some overlap. Overlord safety, drone transfers, and improved scouting all benefit from pathfinding skills. Pathfinding is basic and can improve all movement decisions, so sooner is better. Path analysis skills are useful for many of the tricks. Also overlords are good for scouting, so overlord safety and scouting are closely related. I think I will add pathfinding and overlord skills soon.

prep for the SSCAIT annual tournament

I’ve disabled Randomhammer on SSCAIT in preparation for the tournament. It’s less than a week away.

For Steamhammer, I am trying to overcome my usual deadline intolerance. I fixed one critical bug, and now I am concentrating all my effort on my Killer Feature. Whether it will kill anything is an open question, but it will be fun, and above all it should be finished and tested and tuned by the deadline. I’ll be satisfied if it can catch out a few strong opponents.

As usual, the big improvements promise to arrive after the tournament. I am adding, or on the verge of adding, basic skills that promise stronger play, and it is impossible to exploit new skills fully in a short time. I expect to soon have the infrastructure needed to add nydus canal support, though I don’t know whether I’ll actually add it soon. Nydus canals make island bases much more useful. I am very tempted to delay strategy adaptation work to add a bag-of-tricks meta-skill that knows how to select from a bag of tactical ideas to pose problems to different opponents. I know a lot of tricks that promise to be effective in specific situations.

In the arena of unimportant abilities, it’s tempting to fill out the queen skills. Steamhammer is capable of controlling a fleet of queens simultaneously without blowing out the per-frame time limit, but they tend to all simultaneously broodling the same target. Queens tend to carelessly fly into danger and die, and at the same time do not know how to fly around freely and seek their own targets—they wait for targets to wander into range. A bug in the production system prevents Steamhammer from producing infested terrans, but I’m not fixing the bug yet because it doesn’t have the skill to control them properly. Infested terrans are powerful but must be used carefully, for example dropped from overlords or coordinated with defilers. That’s something I may work on gradually over the next year.

Upcoming: I want to time a few other openings the same way I timed the Styx opening, to compare them. I think it will be enlightening. And I still hope to get back to the AIIDE tournaments and do more analysis; we’ll see if I can pull it off.

timing the Styx build and variants

Here’s a table of variants of the Styx build. The left one, 7 drone hatchery first, is the original; the other 3 are natural variations. The story from Dan Gant is that an entire population of machine learning agents all converged on the original build, so according to those agents, it’s the best choice. I won’t accept their opinion without checking. Steamhammer, of course, includes all 4 of these builds, and has additional decorations beside.

step7 drone
hatch 1st
7 drone
gas 1st
8 drone
hatch 1st
8 drone
gas 1st
15 x drone (9 total)
2spawning pool
3drone (back to 9 total)
4extractor trick (10th drone)
5overlord
63 x zergling
7hatcheryextractorhatcheryextractor
8extractorhatcheryextractorhatchery
94 x zergling2 x zergling4 x zergling2 x zergling
10metabolic boost (zergling speed)
11keep making zerglings

The 4 variants differ in 2 features. Feature 1: The 8 drone variants, on the right of the table, throw in an extractor trick (step 4) to get an additional drone, leaving the zerg with 8 drones mining at the end of the build rather than 7. Feature 2: You’re making both a hatchery and an extractor, and you can make them in either order (steps 7 and 8). Hatchery first means the hatchery finishes a little earlier and may be able to raise the zergling count a little. There are also timing effects because of the different number of zerglings made (step 9) before starting zergling speed (step 10).

The machine learning agents concluded that the leftmost build is best. My intuition is that the rightmost build is best: The extra drone from the extractor trick doesn’t help at first, but provides more flexibility in the long run (in case you don’t win outright). Getting gas before hatchery barely delays the hatchery, because the extractor does not finish before the hatchery starts, but gets zergling speed significantly earlier, which I feel outweighs the couple extra zerglings you may gain by getting hatchery first.

I’m not trusting my intuition without checking, either. This post is about measurements. I can measure timings. I can’t measure which variant is objectively stronger, because it depends on the skill of the player. PerfectBot is not available to tell us. Are you better at taking advantage of zergling numbers, or zergling speed?

All tests were run on the map Heartbreak Ridge. I didn’t use Steamhammer’s existing builds, but wrote special ones to reduce variance. Each build calls for exactly 40 pairs of zerglings. There is no drone scouting. The second hatchery goes in the main base because long drone movements will vary more. Overlords were inserted by Steamhammer’s usual method, which is heuristic but consistent. Zerglings stayed put and did no fighting so that overlords were made at predictable points; I disabled the Recon squad and convinced the opponent to also stay home. Building placement is different between the 2 starting bases on the map, so I only timed games from the 9 o’clock base. Zerglings gathering around the main hatchery interfered with drone movement when placing buildings and caused variations in the timings, so I had them gather at a distance. I recorded data for 5 runs of each build.

Zergling speed timing: The frame when metabolic boost finished upgrading. The gas-first builds get zergling speed over 330 frames faster, around 14 seconds at 24 frames per second. That’s a long time during a game, but don’t forget that it is only a window of opportunity. If there is any advantage to be gained by a gas-first build, it must be gained during that interval. With hatchery first, the extractor trick to gain the 8th drone causes a 100 frame or 4 second delay, which is much more than I expected. With gas first, the extractor trick causes no measurable delay. Now that’s interesting.

buildmedianlowhigh
7 drone hatchery595559416003
7 drone gas562155915659
8 drone hatchery605560546092
8 drone gas562155945633

Zergling timing. For each build, I took the run with the median timing of zergling speed from above, and put only that data into these charts. The initial 6 zerglings happen after the extractor trick and before the hatchery/extractor decision, so only the 7 drone/8 drone division between the builds is meaningful. The numbers say there may be added variance in the extractor trick causing some delay on average, but no necessary delay. The 8th zergling (the fourth pair) comes after the hatchery and extractor and before zergling speed. For the hatchery first builds, zergling speed finishes after 22 or 24 zerglings have been produced. For the gas first builds, after 16 or 18 zerglings.

This scatter chart shows how similar the four variants are. The x axis is time, y is the zergling count. Blue is the original 7 drone hatchery first build. The other colors are visible a bit around the edges of the blue. The initial 6 zerglings appear at the lower left in a column. There are visible gaps where overlords are made.

scatter chart with very little scatter

Here is a more useful visualization. I subtracted the blue timings from each build and plotted the residuals. A point above the horizontal zero line means that that build was later than the 7 drone hatchery first build to reach that specific zergling count. A point below the line means it was earlier. The vertical axis gives the frame difference. 40 points are plotted for each build, one point for each zergling pair from 1 to 40.

scatter chart with very little scatter

The original blue build is quite good at pumping out lings. It is ahead of the others at more timings than not. But every build is ahead at some timings. The regular spikes are related to overlord spawning. If you are fighting hard you will not need to make overlords, and the results may be different. You can think of each timing when a build is ahead as a window of opportunity, and compare the zergling count windows to the zergling speed window of 14 seconds for the gas-first variants.

Extra minerals. The 7 drone variants are just about perfectly tuned to balance mineral income and larva production from 2 hatcheries. After starting 40 pairs of zerglings, less than 50 minerals are on hand and the larva count is 0 or 1. That means that the 8 drone variants cannot gain an edge in zergling production, even in the long run. Instead, the 8th drone allows flexibility in case the build does damage but does not win outright; the player has more resources to switch into another line.

After starting 40 pairs of zerglings, the 8 drone variants have banked about 400 minerals. Normally, you’d prefer to spend them before getting that far. One idea is to spend the minerals on a sunken—perhaps an offensive sunken against zerg. Another is to add a third hatchery, pause zerglings briefly to make a few drones to keep the hatchery fed, and go back into zergling production at a higher rate. Another is to put the 8th drone on gas and prepare to suddenly switch into a tech build.

a couple games to distract you while I work

I have to write some code to analyze the Styx opening and its variants. Don’t get bored though, here are a couple Steamhammer games to tide you over.

When Locutus plays its dragoon unit mix, it knocks over Steamhammer with no apparent effort. In Steamhammer-Locutus on Heartbreak Ridge it instead chose a zealot-archon unit mix, and brought about a tempestuous brawl. Something similar happened in one AIIDE game.

In Steamhammer-Ecgberht on Fighting Spirit, Steamhammer was clumsy and fell into a losing position. But Ecgberht was not faultless either, and Steamhammer held and started to fight back. Watch the adventures of the zerg queen.

Steamhammer scouting thoughts

Good scouting is a prerequisite for strategy adaptation, and right now Steamhammer’s scouting skills are mediocre.

The ScoutManager controls early game scouting. In the original UAlbertaBot, the scout manager controls the worker scout. Steamhammer extends it to also control one overlord—and that’s all. It coordinates the two units to scout efficiently, but it can’t make use of the zerg’s second overlord, much less any other unit. Furthermore, the scouting paths don’t extract as much information as possible. When the scouting overlord arrives, it sits in one place and has no chance to see buildings started far away. The scouting worker does explore the base, but doesn’t evade defenders and often gets stuck on buildings. The enemy natural is scouted incidentally, if at all.

Steamhammer scouts in the middle game with its Recon squad, a small group of combat units that visit empty bases to see if perhaps they are no longer empty. The squad attacks any undefended buildings or weak enemies that it happens across, and the behavior of zipping across the map in every direction incidentally finds and interferes with a lot of enemy activity that would otherwise go unnoticed: Enemy scouts, abandoned proxy buildings, worker transfers, isolated reinforcements.... The Recon squad is effective, it doesn’t need any changes for now. But it should not be the only form of middle game scouting.

Steamhammer does no deliberate scouting of known enemy bases (after the early game scout), or of possible enemy movements (ever). It sees the enemy move out because its army is normally trying to push forward as far as it can, and it learns what is in the enemy base when an attack reaches that far (or occasionally when it parasites a unit that returns home).

There is a connection with overlord safety. Steamhammer does not know that most maps provide overlord posts where a correctly positioned overlord can safely watch passing enemies. Wraiths or corsairs spoil the party, but until then overlords can ordinarily see when the natural is taken, and see enemy units move out, and spot them as they pass key points, without danger and often without being seen in return. I don’t know of any bot that takes proper advantage of overlord posts.

what do do?

Some bots have a thing like a scout squad: If you want to look around the map, throw whatever units you prefer into the squad, and the squad coordinates them. That’s a step up in generality from Steamhammer’s scout manager.

The scout squad has decisions: Run around the map, or post units at key locations, or some of both? Just look, or harass or fight when it makes sense? If planning to fight like the Recon squad, how big should the groups be? For example, when a pro scouts with scourge, they commonly send 2 scourge, and if a corsair shows up, the scourge have a point to make. The simple scheme of dispatching individual units around the map doesn’t do everything you’d like.

I’m thinking of implementing scouting with 2 squads plus a scout boss that can hand off scouting goals to other squads.

The Recon squad would be little changed from now.

The Watch squad would be responsible for watching over key points with more or less stationary units. It might post single zerglings in likely enemy expansions, or set units to oversee choke points. Like the Recon squad, it would feel free to fight when it saw an advantage—you want to expand here? Fine, see if your probe can beat my zergling. The Recon squad would only visit bases that are not being watched. The size of the Watch squad might be 1 zergling early, and it might replace the Recon squad entirely later in the game. Steamhammer already has some code for the Watch squad, but it’s not in a usable state.

The Scout Boss would maintain goals: A set of things that we’d like to find out if we can, with priorities. At the start of the game, the goals would be to explore starting bases to find the enemy. When the enemy is found, some goals disappear and new goals appear to seek out what the enemy is doing. Later in the game the set of goals could become extensive—we saw a zealot at (x, y), is it still there? Ideally, in Candide’s best of all possible worlds, the Scout Boss should be smart enough to tell when it is worthwhile to sacrifice a unit for more information: How big is the enemy army? Run one zergling in. How many barracks are in that base? Sacrifice an overlord to find out.

A Scout squad can ask the Scout Boss for its goals. So can other squads. When Steamhammer’s mutalisks back away from an over-strong enemy force, they assume that the force will stay there and they remain backed away, wasting time doing nothing. The Scout Boss will want to know where that force goes, and can prompt the mutalisks to return and look. Or the Scourge squad can accept scout goals, and the Scourge squad has the knowledge that scourge should commonly fly around in pairs—or in triples if expecting scouts, something a general scout squad should not have to understand. (2 scourge kill most flyers, but a scout needs 3.)

Anyway, that’s the line I’m thinking along. Maybe I’ll start on it in January.

Soon: Analysis of the Styx opening and variations.

Steamhammer’s documentation is updated

I have updated Steamhammer’s documentation for the first time since version 1.4.2 in May last year. There’s not much to it: One page lists important classes in the code to help people get started, and the other explains the configuration file in great detail.

The documentation is so little used that many people do not seem to realize that it exists. Or maybe it’s the other way around. But now there is no longer an excuse.

Steamhammer 2.4 change list

Steamhammer 2.4 is uploaded. Since I never got around to releasing the source of the previous version, I’ll release source shortly.

This version has 2 points of focus: First, fixing critical bugs that are responsible for many of the losses against weaker opponents, and second, coping with the deadly Styx opening. I wrote 18 new openings, 10 of which are connected to the Styx opening.

configuration

Added the Config::Strategy::HumanOpponent flag for games against humans. It’s false by default, since almost all games are versus bots.

critical bugs

Cancelling mutalisks just before making them is fixed. It happened intermittently in some openings. A spire takes 1800 frames to build, and Steamhammer is configured to conclude that production is jammed if there are 1440 frames without production. In some cases, the opening build might pause production for longer than the limit while saving up for mutalisks; a jam clears the queue and drops out of the opening, so the mutalisks were never made. I fixed it with a special case test, since only this one case was failing.

Gas deadlock is an emergency that happens when Steamhammer needs gas for the next item, and has the extractors for it, but finds itself unable to collect the gas for some reason (for example, the base is under attack so the drones ran away). It clears the queue to try again. The condition for recognizing gas deadlock was too loose, and sometimes declared deadlock falsely. I tightened it up.

Tracking of geysers and refinery buildings seemed to be wrong in some cases. I was not able to pin down any bad behavior it caused, but the code looked confused and I’ll be surprised if nothing is corrected.

Combat sim is more fixed. In the previous version I fixed bugs in setting up the combat sim, related to short-circuit judgments when facing cloaked units. There was still a bug, and I traced it to... checking whether a unit’s air weapon was unequal to BWAPI::UnitTypes::None rather than BWAPI::WeaponTypes::None. Thank you C++ for your “helpful” type checking. This fix will make mutalisks more aggressive, as they should be, in certain situations.

macro

• Restored the setting of 2 workers per mineral patch for terran and protoss. It had been accidentally set to the zerg value of 1.6.

operations

• Against certain stronger enemy units, send more defenders. If an archon is in your base wreaking havoc, 2 marines will not stop it.

• The natural expansion cannot be one of the map’s starting bases. This fixes a problem that came up on the map Baekmagoji.

StrategyBossZerg::enemySeemsToBeDead() works more nearly as intended. There was a minor bug. The enemy is considered to be dead and ready to be buried when all these conditions are met: 1. The enemy starting base has been found (so it can’t happen immediately when the game starts). 2. The enemy has no known surviving bases. 3. The known enemy ground army is not strong enough to threaten to win. 4. The enemy has no known anti-air units or anti-air static defense. When the enemy looks dead, Steamhammer techs to mutas and switches to a unit mix of drone + mutalisk. The mutalisks can efficiently hunt down any hidden or floating buildings.

unit control

The micro system drops positional orders for which the position is “close enough” to the previous order. If code orders a move to (x, y) and then on a later frame a move to (x, y+1), the second order is dropped. The tolerance is a few pixels for short-range moves, larger for long moves. This reduces command spamming and smooths unit flow. In the long run, I ideally want to reduce Steamhammer’s APM to human levels.

• Targeting priority for enemy buildings is factored out and more nearly standardized across unit controllers. Streamhammer should destroy enemy bases a little more purposefully, with fewer cases of zerglings tearing down the spire while mutalisks tackle the spawning pool.

• Enemy observers are a higher priority target for ranged units and scourge, depending on the situation.

zerg

• When in book, if we have no zerglings, do not change a planned zergling into a drone. Steamhammer likes to skip zerglings when it foresees no danger. A similar change was already made for the case when it was out of the opening.

• Queen bug: The queen believed it could infest an unfinished command center. “It has few enough HP, let me fly across the map and take it.” When the building SCV was killed, the queen might sit over the unfinished command center forever. “This infestation sure is taking a long time. Oh well, stick with it!”

• Queen bug: When using broodling, the previous version was intended to be willing to make 2 queens, not the usual 1. After testing the feature, I introduced a typo that prevented it from working. Fixed.

Prevent production freezes related to queen upgrades. Rare but painful.

• Allow air upgrades in any order, using 1 or 2 spires. Formerly, air carapace had to be first (or else production freezes were possible) and Steamhammer could upgrade in only 1 spire. This any-order-is-OK improvement was made earlier for ground upgrades.

openings

ZvZ_Overgas8Pool fills a small gap in the repertoire. A very small gap.

973HydraBust is a precise modern build versus protoss forge expand. The name is because you are supposed to end up with 9 drones in your main, 7 in your natural, and 3 at a third base. Steamhammer does not have the skills to distribute the drones efficiently as intended, but it does get the right total count.

2HatchMutaForever covers a weakness in the zerg strategy boss: There are times when growing the mutalisk flock is best, but the strategy boss doesn’t understand the value of stacking air units and prefers to switch into a different unit mix. The opening doesn’t really make mutalisks indefinitely, only for a long time. It also gets air upgrades.

2HatchFakeHydra and 2HatchFakeMuta are deceptive openings that make both hydralisk den and spire. The fake hydra opening makes a hydralisk den when the enemy scout should see it, chases the scout away, and adds the spire later. The enemy may predict hydra play or lurkers. The fake muta opening is similar but in reverse.

5HatchPool makes 5 hatcheries before spawning pool, an extremely greedy opening for use against opponents that move out late. (See my mention of Locutus in what Steamhammer learned. I’ve seen 6 hatch before pool work against a bot.) ZvP_5HatchPoolHydra is a specialized version that follows up with mass hydralisks. 5HatchPoolLing gets an evolution chamber before the spawning pool, so that when the first zerglings hatch, melee attack +1 is already half finished. The flood of lings is late but massive.

The powerful Styx build. The openings 9PoolHatchSpeed7Drone, 9PoolHatchSpeed7DroneB, 9PoolHatchSpeed, 9PoolHatchSpeedAllIn, 9PoolHatchSpeedAllInB are variations on the Styx build of 9 pool, 3 pairs of zerglings, hatchery, extractor (leaving yourself with 7 drones), zergling speed, and deluge the enemy with zerglings (7 drones are enough). That exact build is 9PoolHatchSpeed7DroneB. The versions without a B have the extractor before the second hatchery, which gets faster zergling speed and a slightly slower hatchery. The versions without 7Drone use the extractor trick to end up with an 8th drone without losing time. The 8th drone does not make the zergling attack stronger, because 7 drones are enough to keep the 2 hatcheries completely busy producing, but it does establish leeway to lose a drone to accidents, make an emergency sunken, or transition into another plan. The plain version 9PoolHatchSpeed makes fewer zerglings and leaves the followup to the strategy boss, which is not too helpful because the strategy boss doesn’t understand the idea.

Anti-Styx openings. If you know your opponent’s exact build order, you can always choose your own build to gain an advantage. (Don’t be predictable!) If the opponent is too greedy, you can clobber them with a rush or a bust before they’re ready. The Styx opening is not greedy; 9 pool is what you play to be safe against cheese. When the opponent is not greedy, you gain an advantage by being greedier, to a calculated extent. AntiStyx_9Pool is the simplest try. It uses the extractor trick for the 8th drone, and turns the drone into a sunken. The zergling wars are about equal, but the sunken at home is safer and allows more freedom of action because there is less need to hang back in defense. At Steamhammer’s level of play, the advantage is slight. However, this anti-Styx build is valuable as a general-purpose anti-cheese build; I configured it for that too. AntiStyx_3Hatch and AntiStyx_Muta are more successful at defending against the Styx opening. They open with overpool rather than 9 pool, gaining a lead in drones. Both make 1 sunken and use the safety it provides to scrape out more drones. The 3 hatch followup adds a third hatchery to eventually win the zergling wars, while the muta followup techs instead. The safe path is narrow; I’m curious to see how well the openings perform in practice.

Turtle openings can also cross the river Styx. Some turtles swim well, and they do not have the same weakness as Achilles. See wins over StyxZ by ZurZurZur, by Simplicity, and by Microwave with its turtle build.

9PoolHatchSpeedSpire and 9PoolHatchSpeedLurker are my try to pre-empt bot authors who may add a recognizer for the dangerous Styx opening so they can counter it. These builds start out with the Styx opening stem but transition into tech.

• I made tweaks to AntiZeal_12Hatch and 12HatchTurtle. They should be a trifle stronger.

Steamhammer 2.4 tomorrow

Steamhammer 2.4 is almost ready. I’ll upload it tomorrow. It has accumulated enough critical bug fixes and changes; it is time to put it on the road and see how it drives.

New are 4 variants of the “Styx” opening (popularized, not invented, by StyxZ—as Ogden Nash said, “Don’t be a discoverer, be a promoter.”). A later post will analyze and compare the variants. And I wrote 3 counter-Styx openings designed to defend and come out ahead.

Steamhammer’s SSCAIT prospects

In 2016, when Steamhammer was brand new, it squeaked into the SSCAIT final playoffs through a tiebreaker match. In 2017 and 2018, it was clear to me ahead of time that Steamhammer would make it into the finals. This year, my life and schedule have been disrupted and I made less progress. Some of the work I put in, like queen skills, was more about having fun than about playing better, because I need some fun. The bottom line is, Steamhammer is failing to keep up with advances. Its rank is sliding.

I think that Steamhammer is still likely to make it into the SSCAIT round of 16, but it may be a close call. There’s a real risk that it may fall short. The fixes in version 2.4 may claw back a rank or two, but not enough to eliminate the risk. It’s possible that I can change Steamhammer’s prospects before then by implementing a killer skill (I have one in mind), but that’s risky in itself, and my past attempts to code killer skills before a deadline have fallen short. But we’ll see.

the HumanOpponent flag

I watched the SCHNAIL video and I’m pleased. Today I added a HumanOpponent flag to Steamhammer’s configuration file, to make the bot more fun for humans to play against. If you set it to true, the flag has 2 effects:

1. It tells the opponent model that the opponent is unpredictable, which the opponent model takes to mean “I’d better be unpredictable too so I can’t be exploited.” It chooses openings more randomly. This is just turning on a standard Steamhammer behavior that already existed.

2. When losing, Steamhammer ggs out much earlier. Steamhammer in the past assumed that its opponent is a bot and may mess up the win, so it surrenders barely one step before it is provably unable to win. (Even so, I’ve seen a couple games over the years when it might have won if it hadn’t given up—when it had no drones and no combat units and no money, but had units in production that could outfight the opponent which was also near death. It’s extremely rare.) Versus a human, that gg timing is way way too late, unacceptably late. When HumanOpponent is turned on, Steamhammer follows a two-part rule: A. The enemy is much stronger than me—my supply is less than half the enemy’s known supply. B. I have been hurt—my supply has fallen below half of its high water mark. The B part ensures that Steamhammer doesn’t give up without a fight merely because it has been grossly outmacroed.

I’m curious to find out how well the gg rule works in real human games. In test games, I thought the gg still came later than a strong human would prefer. But perhaps it is a good fit for human players who deem Steamhammer an interesting opponent. Also, the enemy’s known supply is generally less than the enemy’s true supply, and Steamhammer is weak at scouting so often it is much less. But I will need to improve scouting as part of my strategy adaptation project, and gg timing may improve when I do.

About SCHNAIL: Obviously we can’t expect SCHNAIL users to edit Steamhammer’s config file and set the HumanOpponent flag. The file won’t be exposed to them at all; they don’t need to know it exists. I asked Sonko if there will be a way for a bot to tell that it is running under SCHNAIL. Whatever the final arrangement ends up being, I will help Steamhammer fit into it so the bot does sensible things in human games.

an amusing Steamhammer bug

I was testing a macro build on the map Baekmagoji, a 2 player map where macro builds are fitting because each main base has 2 geysers and 18 mineral patches—double the usual. (I routinely test on all kinds of maps.) Early in the game, Steamhammer suddenly panicked and canceled a hatchery to get its spawning pool immediately. What was going on?

The bug, or I should say bugs, turned out to be this: Because the enemy main was so rich in resources, Steamhammer decided “well, it’s a bit far away, but still it’s the best choice for my natural base.” When the scout found enemy buildings (“hey, that’s in my natural”), Steamhammer concluded that it was getting proxied. Panic! That was easy to fix; the chosen natural is not allowed to be a starting base. But wait, why was the first expansion hatchery already started somewhere else, so that it could be canceled? Because the choice of the natural base and the actual first expansion taken are not quite coordinated correctly; in some cases they can be different for no good reason. Ack!

Baekmagoji is a difficult map for bots. Another issue is that some of the mineral patches in the main are not reachable until other patches are mined out. It’s a clever design, but Steamhammer doesn’t understand it and tries to assign drones to minerals that they can’t reach. Well, that’s not important to fix yet. I can’t think of another competitive map that would trigger this bug (though more than a few Blizzard maps do). Also notice the neutral sunken colonies spreading creep, and the blocking temples and blocking minerals. Lots of tricks lying in wait.

AIIDE 2019 - looking into AITP

AITP follows an “aggressive defense” game plan, similar to SAIDA, where it builds up a strong ground army, then sets up tank lines in forward positions to constrict the enemy like a boa (preferably a snake, sometimes a feather boa). Its overall skill is far less, though; it finished second to last in AIIDE 2019. Overall, after reading some of the key code, I am not impressed (maybe you shouldn’t expect me to have been). The plans are ambitious but the work looks hasty, as if the authors underestimated it and had to rush to make the tournament. Still, the plans are ambitious, and that makes it somewhat interesting.

tactics

One of AITP’s first steps is to calculate map positions for a wall (supplyPoint barracksPoint bunkerPoint), defensive locations (chokePoint1 through chokePoint4) and offensive tank lines (frontLine1 through frontLine3). How does it calculate them? They are hardcoded for every starting base on every map in InformationManager::initializePosition(). That must be part of why AITP did not want to participate in the unknown map tournament. These positions seem to be the foundation on which all the tactical decisions are laid.

Spider mines are placed in neat diagonal double lines at locations offset from the defense line calculated by CombatCommander::updateDefenseLine(), which looks mostly at the supply and the previous defense line and sets the current defense line to chokePoint2 (the natural choke) or to one of the precalculated frontLine values. It’s simple and primitive, and there is not much examination of the game situation, a good starting try but nothing you expect to be strong. It’s silly sometimes; the defense line may be in front of an empty base that is away from the fight. CombatCommander::updateSpiderSquads() sets up spiderSquad1 and spiderSquad2—and also lays mines itself, calculating the offset steps and checking every vulture itself rather than leaving it to the squads. The intended separation of functions is not observed. Micro::LaySpiderMine() is unused.

StrategyManager::shouldBuildTurret() decides how many turrets should go where. Some turrets go to occupied choke points 1 and 2, some next to command centers, and some at the front lines computed above depending on certain “squad positions.” These so-called squad positions come from CombatCommander::getSquadPosition(std::string squadID), which takes a string that is called squad ID but is actually a 2-digit code that identifies a location rather than a squad. The location is usually an offset from the current defense line, but there are exceptions. The ID seems to choose one of a hardcoded set of offsets for the final position.

Units, of course, also go to the current defense line: That is the tank line, soon furnished with mines and turrets, that is meant to restrict the enemy. At some point CombatCommander::updateAttackEnemyBase() becomes true and sets _aimEnemyBase to the location of an enemy base to destroy. When this happens depends on what the current defense line is, but it’s another set of simple calculations using the closest friendly unit to various enemy bases.

what to build

I outlined the build order and unit mix decisions in the post how AITP played. There are game stages A, B and C and “modules” A1, A2 and so on for each game stage.

A1 is the antirush module that starts a barracks at 5, whose play was described in that post. It switches to the next module when there is a bunker and 4 marines and the engineering bay is started. One of AITP’s strategies was A1-B1-B2-C2. B1 makes SCVs up to 20, barracks up to 5, academy and medics, moves out at 10 marines, and switches to the next module after 10 minutes game time. B2 makes a bunker under certain conditions—but only if the previous module was A4. The modules are not in fact modular but know about each other. Then it makes SCVs and marines up to a smaller limit than B1, makes a factory and gets the upgrades and expansion. It switches when there are 2 command centers or the supply reaches 100 (really 100, not 50: int supplyUsed = BWAPI::Broodwar->self()->supplyUsed() / 2;). Module C2 is the middlegame: It makes SCVs to the limit, adds factories, throws bunkers into the middle of the map, gets armory upgrades, and never switches.

Here’s an extract showing the strange over-specificity of AITP’s code, from StrategyManager::doSwitchModule() which switches from the current module to the next one. The code does not simply parse out the strategy module names from the StrategyName string, it handles each as a special case, sometimes taking extra actions that I would say should be factored out.

	if (_currentModule == "A1")
	{
		if (Config::Strategy::StrategyName == "A1-B1-B2-C2")
		{
			_currentModule = "B1";
			CombatCommander::Instance().clearSquad("bunkerSquad2");
		}
		else if (Config::Strategy::StrategyName == "A1-B3-C2")
		{
			_currentModule = "B3";
			CombatCommander::Instance().clearSquad("bunkerSquad2");
		}
	}

AITP’s modules are not easy to read. I think Steamhammer’s explicit build orders plus zerg strategy rules are more perspicuous and no less expressive—but then I would say that, wouldn’t I?

new bot Crona

New bot Crona is, as it says in its description, BananaBrain playing zerg. It was uploaded today and has started out well. The name “Crona” is after an anime villain.

Sp far, I have only seen Crona play with zerglings and mutalisks, no other units. Here are the names of Crona’s openings, extracted from the binary. “Main Muta/Hydra/Ling” looks like an unrelated string that sneaked into the list, but maybe it’s an opening too.

ZvZ_2hatchling
ZvZ_5pool
ZvT_2hatchling
ZvT_4pool
ZvT_2hatchmuta_12pool
ZvT_2hatchmuta_12hatch
ZvP_2hatchling
ZvP_5pool
ZvP_4pool
ZvP_2hatchmuta
Main Muta/Hydra/Ling
ZvU_2hatchling

The build ZvZ_2hatchling is the “Styx build” of 9 pool, 3 pairs of zerglings, second hatchery, extractor, research zergling speed and produce zerglings for a long, long time. (Note 1: One of the rules of naming is that the origin of a thing cannot be the name; you have to pick something later. Note 2: I’ve tested both variants, and I’m pretty sure that the PurpleSwarm variant with extractor before the second hatchery is better.) Likely the other 2hatchling openings are too.

When playing 4 pool, and presumably other builds, Crona sticks with 1 hatchery and 3 drones for a while, then transitions to 2 hatcheries and 7 drones, then later to 3 hatcheries. I assume the sequence continues. My impression is that the expansions are on a timer: I’ve done this long enough, time for another hatchery. It’s a simple way to slowly increase pressure on the opponent.

Crona’s zerg play is good—see its results—but still looks a little rough to me. It doesn’t scout with its overlord. It has a glitch where, at a certain point in the opening, all the drones move away from the minerals for a second before returning. Crona seems a little confused about drone transfers in general. These things should not be hard to fix, though.

I see terran openings in the binary too. Can anybody guess what the terran BananaBrain will be called?