archive by month
Skip to content

Steamhammer skill kit

As a teaser, here is a code snippet from the Steamhammer 3.0 development version.

void SkillKit::update()
{
    for (Skill * skill : skills)
    {
        if (skill->nextUpdate() <= the.now())
        {
            skill->update();
            if (skill->feasible() && skill->good())
            {
                skill->execute();
            }
        }
    }
}

(the.now() is short for BWAPI::Broodwar->getFrameCount(), the current frame number.)

The skill kit doesn’t actually add skills as such, it adds control of skills; to “execute” a skill means to set flags or post goals or otherwise send orders to the rest of the program to carry out the skill as directed. The skill controller can run every frame if it likes and change its orders at any time. There are two main features. 1. The skill kit is extensible: It is easy to write your own skill controllers as subclasses of Skill and drop them in. This is good for modularity and especially good for people who fork Steamhammer. 2. The skill kit provides an easy interface to the persistent opponent model files. A Skill can include one method to write its data to a string, and one method to parse a string that it wrote back into data, and the skill kit takes care of the I/O plumbing.

As I use the skill kit for more things, Steamhammer will become much more flexible in adapting to its opponents.

Soon: Tournament design is hard.

Steamhammer and bugs

I’m pleased with Steamhammer’s reliability this tournament. I watched all 88 games, and for the first time ever I saw no losses caused by crippling bugs. There were 2 games with terrible play due to bugs, but in both cases Steamhammer recovered and won anyway. There was also a bug that occasionally caused multiple drones to be sent to fail to scout the enemy: Lose one, send another, repeat. That would lose games for sure against an even opponent, but in practice it occurs in games that Steamhammer will lose no matter what. I think I know the cause, and it will be fixed soon.

I surmise that Steamhammer is especially prone to reliability problems because it aspires to do everything. It plays over 150 opening builds of all kinds; failing to adapt to all the misadventures of the opening causes a lot of snags. I count only 3 zerg abilities yet to be implemented: Nydus canals, infested terrans, and lifting off an infested command center, all on my list for coming months. (Drop is technically implemented even though not yet used by zerg. I guess overlord sight range is not implemented either, but it’s trivial if I ever want it.) More features means more bugs; fixing bugs in defiler play was a big time sink last year.

Another source of reliability hitches is my habit of swapping in new plans as fast as I can make them. A number of Steamhammer’s internal modules are half-rewritten, stuck in the middle of a transition from an old design that is not flexible enough to a new design that is not finished enough. It would be more efficient to complete one task before moving on to the next. But then I would be making progress on only one front at a time, and it wouldn’t be as much fun. I like the variety.

Anyway, after burnishing the 2.x versions for over a year, I’ve remedied almost all of the worst bugs. It is past time to get back to major features and structural work, so that I can add a new round of bugs. To symbolize that, I’ll be calling the next version 3.0 even though it doesn’t in reality have any new feature worthy of a major version number. It will in time.

Steamhammer in the SSCAIT elimination phase

As I write, the SSCAIT round robin phase is close to its end, and though the top 2 places are undecided, most of the top 16 ranks are either mathematically fixed or highly unlikely to change. I am able to forecast part of Steamhammer’s path through the final elimination phase with only moderate risk of error.

Steamhammer will finish #11, the same as last year. It’s the middle of the range #10-#12 that I predicted on 29 December.

BananaBrain and Iron are tied for #5-#6. I don’t know how the tie will be broken for pairing purposes, because they scored 1-1 against each other. Perhaps it will be broken randomly. It matters for Steamhammer, because if the pairings work the same as last year, #11 Steamhammer will be paired against #6.

suppose #6 Iron

A stroke of luck. #11 Steamhammer 3-0 #6 Iron in the first round, or at worst 3-1, the same round 1 result as last year. In the second round, Steamhammer should face #3 BetaStar, which will win crushingly, still the same result, pushing Steamhammer into loser’s bracket round 2. There, Steamhammer is likely to face #16 TyrProtoss, which will be a close match that I can’t call. In the last two weeks on BASIL, the score is Steamhammer 13-11 TyrProtoss. In the SSCAIT round robin, the score is Steamhammer 1-1 TyrProtoss. Last year, this is that match that eliminated Steamhammer. If Steamhammer does win, surviving longer than last year, its next opponent in the loser’s round 3 is harder to forecast, and the match could again go either way—though I think Steamhammer is more likely to drop out here than to go on. It’s funny that this path is so nearly the same as last year, and maybe longer even though Steamhammer is probably relatively weaker.

suppose #6 BananaBrain

Though Steamhammer 2-0 BananaBrain in the round robin thanks to my preparation and/or luck, BananaBrain is likely to win this match. BASIL shows Steamhammer 2-14 BananaBrain. In this case, Steamhammer is likely kicked out at once.

Yeah, elimination tournaments are very sensitive to pairings and lucky results. Which means that something entirely different could yet happen.

experience with Steamhammer’s burrowed zerglings

Steamhammer’s ostensible Killer Feature or tournament surprise was burrowed zerglings to block the opponent from taking nearby bases. The lings are assigned to Watch squads to watch the bases, and burrow there as soon as burrow is researched. How did it work out? Do the Watch squads contribute to Steamhammer’s strength?

My conclusion: Yes. The cost is the expense of researching Burrowing (100/100) plus the assignment of up to 4 zerglings to watch enemy bases, taking them from other duties. It’s reasonably low. To pay that back, burrowed zerglings have to gain intelligence or delay enemy expansions. A few opponents, like Tscmoo, know what to do about burrowed zerglings and send detection and force. Even so, the intended expansion is delayed, and Tscmoo then starts the base immediately so that the zergling’s residual vision sees it and Steamhammer can react instantly. It’s already worth the cost. Other opponents treat a burrowed zergling the same as a spider mine, which is not always astute. XIMP sends a zealot to “trigger the mine” with no effect; the burrowed zergling continues to block the base until carriers fly over with an observer. It’s no better when terran scans and tries to kill the “mine” with an SCV. Some opponents don’t react and can’t deliberately expand at all; they are stuck until the zergling is removed by mischance. In many cases, I think Steamhammer would benefit by researching burrow earlier. There is virtually no effect against zerg, because by default the strategy boss researches burrow when Steamhammer has 3 bases, and that is late game in a ZvZ.

A couple issues limit effectiveness. First, the zergling unburrows when it knows that it is detected—and then it forgets and reburrows at once, going into a loop. The intention was to stand up and fight or flee when necessary, but there was a bug. Second, in an emergency when all other zerglings are killed, the last watching zergling unburrows and races home to join in the defense. But one zergling running in from afar barely affects the fight, and the enemy becomes free to expand without hindrance. After seeing games, I concluded that it will be more effective to leave the last watcher in place. Both issues are fixed in the development version. Overall, this is a low rate of errors compared to other new features I’ve made; care in testing paid off.

Against some opponents, the Watch squads do little good and represent mostly cost. The best example is Tyr protoss, which against Steamhammer plays one-base cannon defense into timing attack. When the attack comes, it includes an observer and the oncoming steamroller incidentally rolls up the zergling burrowed in the natural. Steamhammer pays the cost, and Tyr bypasses it with no effort. Blocking later bases sometimes helps, though. Another example is Iron, which likes to build turrets before it starts the command center. The burrowed zergling at most gets to see a turret or two. At some point I will put the Watch squads into the upcoming opponent model skill system and have Steamhammer collect data on how well they work. That will help Steamhammer research burrow at the right timing, as late as possible while effective against this opponent, and never if it is never effective.

I have more uses in mind for burrow. In particular, I want to use it to protect drones from some attacks. Reaver drops usually don’t include an observer, so when the reaver lands, the drones should poof out of sight before they can be targeted. When the reaver is picked up, the drones casually reappear and continue work. That will be funny to watch.

Steamhammer’s opponent model

In the tournament, the ranks are starting to resolve. Outside the collapse of McRave, I don’t see any big surprises. Locutus has played fewer games than other bots, so the top position is less clear than others. Steamhammer is likely to finish around rank #10-#12, in the range of past performances, so it is safely in despite my worries.

In development, resource tracking was soon working. When destroying an enemy base you normally get to see the resource counts, so used bases should be evaluated accurately.

I started writing a scout boss, then I got distracted by another project. I am refactoring gas steal into one skill in a skill system that retains data in the game records of the opponent model. You subclass a Skill object, fill in around 8 virtual methods of which most are simple, and you get opponent modeling that estimates when the skill is useful against whoever you’re playing now. Since it’s implemented in code, it’s highly flexible. You can choose what data to record, including measurements of how successful a skill was in the current game, and how to interpret the data.

The immediate effect I hope for is better gas steal decisions. Steamhammer’s new queen skills are also good candidates, because queens vary from useful to wasteful. Will a queen be good? How many queens? Will ensnare be good? Even tactical decisions like “play defensively until time t” should be possible, with t adjusted in real time as the opponent model watches the game unfold. I like the idea of learning to adapt tactical play to the opponent.

The game record file format will change. The format has to change soon anyway, because it doesn’t record all the information needed for some important strategy decisions. I designed it so that I can add new format game records without having to discard old ones.

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.

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.