archive by month
Skip to content

a Steamhammer versus PurpleTickles game

PurpleTickles of Dan Gant’s purple family is a bot that plays exclusively worker rush builds. It’s not highly ranked; it is #50 on BASIL, well below average. Steamhammer’s score against it is 29-1 on BASIL, and the rest of the BASIL top 10 have similar scores. But I thought this one Steamhammer-PurpleTickles game was fun and instructive.

Steamhammer’s counter to worker rush is spawning pool at 9 drones (sometimes less if drones are lost) followed by creep colony. The intention is that when the spawning pool finishes, the combination of zerglings and a morphing sunken colony will be insuperable. If the sunken completes, zerg is safe against almost any number of workers, so the opponent must prevent that at all costs.

Tickles selected a late worker rush this game, starting its attack when it had 8 probes. It left 1 probe mining and attacked with 7. The late rushes are more effective against Steamhammer, and I assume that Tickles has figured that out (it has won 1 game, after all). At the time of the picture, 1 further probe has been manufactured and is now crossing the map to join the attack.

probe rush!

Tickles does not have perfect worker micro, but it does know how to coordinate its probes so they attack together. Steamhammer has inferior worker micro, because each drone defends itself individually and the drones don’t work together. Its forces can be defeated “in detail,” as the military jargon goes. Also Steamhammer has sent a drone to scout, a questionable decision, so its mining workers barely outnumber the attackers. In general, when Tickles attacks the mineral line from one end, it is very successful in killing drones. When it approaches from a different direction, the probes do not coordinate as nicely and Steamhammer has less trouble. Tickles is not smart enough to figure out the right angle of attack, so the outcome of the battle can depend on the orientation of the mineral line relative to the base entrance.

probe victory?

The probes destroyed all mining drones with 5 surviving attackers (and 1 probe still mining at home). Steamhammer gave priority to zerglings, so 2 pairs of zerglings are about to hatch, but there is no money to morph the sunken. Vanquishing 4 zerglings with 5 probes is a tall order, but protoss is still mining and it’s definitely worth a try.

probe defeat

Well, the probes didn’t stand a chance, though it’s hard to tell with the overlord blocking the view. They held their ground and fought instead of trying to maneuver, a fatal mistake. Steamhammer’s scouting drone canceled its mission and returned immediately to mine, so zerg did not find the protoss base yet. The 4 victorious zerglings aimed for the wrong base at first, and also returned home to clear followup probes, but they were able to win by themselves after these little delays.

I see lessons in the game. 1. Coordinated micro is a key skill. PurpleTickles has it. 2. Steamhammer covered for its lack of that skill with a belt-and-suspenders strategy. It works. In games where the sunken starts to morph, PurpleTickles must stop it with the probes, but then mining continues and zerglings win anyway. One skill can often substitute for another. 3. When you have an advantage, try to fight head on, as Tickles did probe versus drone. A probe is a stronger fighting unit than a drone. When you have a disadvantage, go guerilla. Probes are not bad at fighting zerglings, but they have to avoid head-to-head combat, unlike Tickles. A probe can attack at will as long as it is taking only shield damage, then run away; protoss shields regenerate faster than zerglings heal. Use drills (the minerals are right there), or maneuver to separate one zergling and gang up on it. If the purple probe-versus-zergling micro were as superior as the probe-versus-drone micro, the probes might well have won.

I’m pretty sure it’s possible to defeat Steamhammer most of the time with a worker rush, though no bot has shown it yet. As in this game, gather your attacking workers at one end of Steamhammer’s mineral line and coordinate them to defeat the uncoordinated mining drones, sweeping down to the other end of the mineral line. I believe it can be done more efficiently than in this game, and if it’s quick enough then after wiping the drones there should be no minerals for zerglings, or to morph the sunken—Steamhammer tries to replace drones and doesn’t save up for future needs.

If some bot proves my theory, then I’ll have to update Steamhammer’s worker defense.

Starcraft AI Ladder

I signed up Steamhammer for the Starcraft AI Ladder run by Dave Churchill and his group. After Rick Kelly fixed a bug in the ladderware, I got the bot signed up and activated.

bots on the ladder

From my point of view, the main advantage of this third ongoing ladder (after SSCAIT and BASIL) is that it runs with the same tournament manager software that is used for the AIIDE tournaments. So for those who plan to participate in AIIDE 2020, it should be a good test to make sure your bot is compatible. In particular, it checks the frame time limits strictly. For now, the ladder has few participants and most of them are above average strength, which could be an advantage or a disadvantage depending on what you’re looking for. Games run at a high rate and there are fewer participants, so each participant gets more games than on BASIL.

It’s a bit barebones. It doesn’t even have a name—”Starcraft AI Ladder” is more a description, and has been used before. In particular, it does not come with any documentation, so the signup process and the features you get for it may come as a surprise. I thought it was worth writing up.

Signing up has a few steps. First, fill out the form. You have to authenticate your e-mail address. Then, according to the authentication success e-mail, you have to wait for an administrator to approve your account. For me, approval was almost instant, which made me wonder whether there was an administrator at all—but then, Steamhammer and I should be familiar. The message also says “You may upload your bot files to your profile before being approved, but you won’t be able to activate your bot to play in the ladder.” What you upload is a zip file that will be unzipped straight into the AI/ directory. You specified your BWAPI version at signup, so unlike SSCAIT you don’t include a copy of bwapi.dll.

the registration form

Finally, go to your profile page, change “ladder participation” from “inactive” to “active”, and save changes. You can also fill in extra info if you like, like your URL. After the current queue of games runs out, your bot will get games queued up.

And that’s about it. The Detailed Results page is not as polished as BASIL, but it does offer the same replay download and replay viewing features. At the far right of the page (I had to scroll right) is a “Download Search Results” button that gives you a CSV file with the same information. At the bottom of your profile page, you can download your bot’s game history in the same format, or your bot’s read/ directory. For other bots, there is no access to anything but the games and results.

detailed results page

Since it’s running the same software, games are played in the same order as in the AIIDE tournaments. There are 10 maps—the AIIDE maps, which include 2 maps that are not among the 14 SSCAIT maps. The tournament manager takes each map in turn and plays a round-robin among the participants on that map; that is called one “round”. Then it moves on to the next map. Every so often the “tournament” is declared over and game records are reset; as I write, that happened last on 1 April.

What do I think so far? The tournament manager part of it is of course industrial strength. The UI is not great, but it’s good by the standard of academic projects, which are often done under severe time constraints and sometimes by students who are still learning. It could use an About page, or a paragraph of description somewhere. I hit one bug, so there are sure to be more that I did not see. Attention to detail is a little lacking; for example, the Home page aka Server Status lists the game number followed by the round number, while the Detailed Results page lists round number followed by game number.

the overlord shuffle

Let’s suppose there are two safe spots where you want to station overlords to watch for enemy movement, a nearer one A (perhaps near the entrance to your natural) and a farther one B.

    *     A     B
    1

Overlord 1 hatches. Of course you send it toward A. It’s slow, so it takes a while to get there.

    *     A     B
      --> 1

Then overlord 2 hatches. You could send it toward the next spot B, but that’s silly.

    *     A     B
    2     1

It’s faster to send overlord 1 from A to B and replace it with overlord 2 at A.

    *     A     B
      --> 2 --> 1

Humans do this kind of overlord shuffling all the time. Here’s a TeamLiquid post showing the overlord repositioning patterns of a few pro games. I don’t know of any bots that do it. It seems like it might be tricky, no? But in fact the problem of assigning overlords, or scouting units in general, to locations is an example of the mathematical assignment problem, and there is no shortage of known algorithms to solve it either exactly or approximately. I’m still thinking about how I want to solve it in Steamhammer.

Scouting in the presence of an enemy is not as simple. To do it optimally you’d have to understand how scout timings interact with possible timings of the opponent’s build, and consider the risk to the overlord from early marines, and take into account that an enemy that sees your overlord learns something about you too—and they might actively look for the overlord, humans often do (“if you’re at that base and scouting the most efficient way to the closest natural, your overlord will be here at this time; if I see it I found you, and if not, I ruled out one base”). The optimal strategy is surely a mixed strategy, meaning that you don’t play the same every game, and there’s no way it’s possible to calculate it on the fly. You have to either pre-calculate a plan or figure it out heuristically.

next for Steamhammer: a scout boss

The major feature for Steamhammer 3.1 will be a new scout boss which will take care of scouting, detection, and providing vision. It’s next because it meets 2 needs: First, scouting must be improved for strategy adaption. You can only predict the opponent’s moves from what you have seen, and the current scouting does not see enough. As one example, the initial scouting overlord hovers in a fixed position over the enemy base, and some protoss bots are smart enough to hide buildings from it. Second, a major weakness in current Steamhammer is that it loses overlords at a high rate. Today, overlords for different purposes are controlled by different code, and only some of the code knows about avoiding dangers. An overlord being sent to watch over a distant base goes there in a straight line, and then so does its replacement and so on, and sometimes an overlord is unassigned from a task and simply stops in a random location. With unified control, overlords will both see more and expose themselves to fewer risks.

The scout boss will also simplify the codebase. It will centralize the functions of the current scout manager and the overlord squad, and of scattered code that controls squad detectors. I expect the 3.1 version to have less total code even though it will be more capable. It will definitely have simpler interfaces with the rest of the program, I have already implemented most of that aspect.

The current scout manager that handles early game scouting knows how to control one worker and one overlord, and that’s all. The scout boss will take command of all flying detectors (including overlords) and floating buildings (I want to add this feature soon), plus whatever units you assign to it. In the opening it will automatically send the second overlord to scout a different base. If you do BBS in the center of the map, you’ll be able to scout with both SCVs to find the enemy faster, as Liquipedia recommends. Many bots already have these skills, Steamhammer has lagged behind.

3.1 should not take as long as 3.0. Unless I fall to temptation and add more features.

sudden Steamhammer 3.0.2

Another day, another fatal bug. I just replaced Steamhammer 3.0.1 with 3.0.2 to fix an infinite recursion that caused a stack overflow. It could happen if Steamhammer was facing a random opponent, depending on the order in which enemy unit types were encountered. Gotta love conditions like that. That’s 2 bugs down, who’s keeping score?

Source is updated too. Only a few lines changed.

Steamhammer 3.0.1 source

Source code for Steamhammer 3.0.1 is now up on Steamhammer’s web page. It will take a little longer to update the documentation, since I made a passel of changes to the configuration stuff.

Part of putting up a new version is calculating the win rate for the previous version. Based on that, the SSCAIT 2019 tournament version 2.4.1 (which plays the same as version 2.4.2) was the strongest relative to the field since version 2.3 from summer 2019. Both are unequalled in Steamhammer history since version 1.2 from early days. In absolute terms, the latest version is of course the strongest.

ack, Steamhammer 3.0.1

First deadly bug eradicated! Only n more to go!

Steamhammer 3.0 has a crashing bug. It’s a null pointer dereference that can happen on startup when the gas steal skill analyzes past game records. Fixed, now I’m on Steamhammer 3.0.1. Next please!

Steamhammer 3.0 change list

I’ve uploaded Steamhammer 3.0 to SSCAIT. Expect source tomorrow. Here’s the change list. Some items I already wrote up, so I only link to the posts here.

There’s a ton of new stuff. This is an infrastructure version, aiming to set the stage for future progress. The new features mean there is a high risk of arthropod vermin. If the new bugs are not too deadly, I expect this version to be only slightly stronger than the current Steamhammer 2.4.2 (which plays identically to the SSCAIT tournament version). Watchers may not be able to tell the difference, except when there is more than one queen on the screen, when it will be obvious. I expect only occasional proxy openings.

skill kit

The skill kit makes it possible to extend the opponent model with any kind of game data, so that in principle Steamhammer can adapt any aspect of its play to the opponent based on experience. To implement a new skill under the skill kit, write a subclass of the abstract class Skill. You need to set a name for the subclass (_name) and implement methods enabled() (should the skill be loaded for this game?), update() (which may carry out the actual skill, or may only collect information), and putData() and getData() (write or parse your data for the game record). You also have to implement feasible() (check quickly, is it possible to execute the skill?), good() (is it a good idea to execute the skill?), and execute() (carry out the skill when feasible and good), but if you prefer you can let update() do all the work, just have feasible() return false, and supply trivial implementations for the others.

• There is a new game record format that records modestly different information about each game, and includes skill kit data. Steamhammer can read records in the old format or the new format, so existing data can be kept.

• The updated gas steal skill uses the skill kit and knows a lot more about Starcraft than the original gas steal skill.

• There is a new enemy unit timing skill. All it does it record data.

• Base ID numbers from the map analysis are now constant across games, so that the numbers can be stored in the opponent model and used to remember the initial base positions.

proxy skills

Steamhammer now has basic proxy skills so that you can write a wide variety of proxy openings. There are new macro locations such as @ enemy natural and new things that you can do with macro locations, like send workers there ahead of time.

It is also possible to use the skill kit to write more sophisticated proxy openings which make dynamic decisions.

production

Fixed: A bug caused distant buildings to start later than necessary because of an error in figuring out when to send the worker.

• Improvements to the handling of production jams: Don’t declare a jam when we are out of resources. Don’t declare a jam when we are maxed on supply. Also, clearing a jam correctly resets the timer so we don’t loop on declaring production jams. The changes correct occasional minor problems, nothing serious.

• If a worker sent to build gets locked down or maelstromed, we immediately assign a new worker. For some reason, Steamhammer used to assign new workers only for stasis.

• If we’re protoss, and we’re constructing a building with a worker from the same base as the building, it’s OK to assign a worker that is carrying minerals or gas. It will barely delay our income. Terran and zerg continue to insist on builders with empty hands.

• Some production code is slightly simplified.

other infrastructure

Mine out blocking minerals on maps that have blocking mineral patches. In the SSCAIT map set, that is Destination and Heartbreak Ridge. WorkerManager sends a worker to mine out the minerals once there are 2 bases and at least 18 workers total. The implementation is not complete and works only for mineral patches with 0 minerals; it doesn’t work on maps where blocking minerals actually contain minerals (mostly quite old maps).

Resource tracking: Keep track of the remaining minerals and gas on the map, as information allows. Since writing those two posts, I extended the code to also keep track of whether a mineral patch is mined out; formerly, the code could not tell the difference between a 0 amount mineral patch and a mineral patch that no longer existed (it’s used in mining out blocking minerals).

• Decide on the value of taking a base, possibly a base that is partially mined out, based in part on the remaining count of mineral patches and the remaining total resources. To get this tuned right I had to retune other aspects of expansion decisions too, or else it would take center bases too soon.

The internal The system to simplify information access is extensively reworked and lets you reach much more information.

Maintain global unit counts every frame. I removed the old UnitUtil::GetAllUnitCount() and UnitUtil::GetCompletedUnitCount() calls. The information is used so often that it’s better to compute the numbers once and for all each frame.

Infer the existence of enemy buildings that we haven’t seen if the buildings are prerequisites of things we have seen. When we see a dark templar, we know there is a templar archives, among other buildings.

Accurately predict enemy building completion times in the UnitInfo system that tracks enemy units. Steamhammer now knows when enemy static defense that it saw under construction will complete, and uses the information in combat simulation (it formerly used a gross approximation that sometimes caused misbehavior). The info is not yet used in recognizing the enemy plan, but it will be valuable when it is. The info also affects squad targeting sometimes.

• A proxy enemy supply depot, engineering bay, or pylon by itself is not an emergency and doesn’t cause the enemy plan to be recognized as Proxy. Don’t overreact.

InformationManager::enemyGasTiming() remembers the time that the enemy first visibly used gas. For example, if we see a vulture, then we know there’s a factory, so we know that the enemy has spent some vespene. It’s one piece of data that goes into deciding whether to steal gas: It’s recorded in the new format game records, and the gas steal skill looks at the numbers from games with no gas steal to get an idea of the enemy’s usual behavior and decide whether stealing gas now will interfere with it.

• A minor improvement to UnitUtil::EnemyDetectorInRange(), which tries to judge whether a cloaked unit of ours is detected by the enemy: Now it knows that a blinded detector can no longer detect.

• I fixed a bug in InformationManager that could cause it to believe that a destroyed pylon existed for 1 extra frame, a bug with no detectable consequences. There should be a Rammstein song about that.

• I removed the WorkerData::depots data structure and its updating code, which was not only unused, but unusable because it was incorrect. I also removed the MacroAct::_race value, which is redundant and was used only for error checks that have never failed in Steamhammer’s life and would be easy to debug if they did.

• I removed various unnecessary includes.

configuration

New macro location @ gas only makes it possible to take a gas-only base (with no minerals) on those rare maps that have them. The zerg strategy boss knows how to do this and will automatically do it sometimes. This is on top of the new macro locations for proxies.

• I removed the macro location @ macro in favor of @ main, which so far means the same thing. Though I’m thinking of splitting them again in the next version, when they may no longer mean the same....

• Added configuration option Config::Debug::DrawResourceAmounts which draws Steamhammer’s idea of the remaining minerals or gas in each mineral patch or geyser—the amount and, if it’s not current, the frame it was last observed. It’s turned on in the uploaded instance, so you’ll see it on the SSCAIT stream. Also Config::Debug::DrawTerrainHeights which draws a number for the terrain height at each tile, with doodad terrain levels in purple.

• Changed the configuration option Config::Debug::DrawWorkerInfo to draw the worker’s job code on each worker and target lines to show where the worker is going. Formerly it drew the worker jobs in a column, which was hard to relate to the game situation. Updated Config::Debug::DrawStrategyBossInfo to add the target queen count to its display of information.

• Renamed configuration option Config::Skills::AutoGasSteal to Config::Skills::GasSteal; it sets whether the gas steal skill is enabled. Renamed Config::Debug::DrawUnitTargetInfo to Config::Debug::DrawUnitTargets because it is shorter.

• Removed configuration options Config::Debug::DrawResourceInfo which was confusing, Config::Skills::RandomGasStealRate which appears to have never been used by any fork, Config::Debug::DrawMouseCursorPosition which I don’t find value in, and Config::Debug::DrawBOSSStateInfo which is not very informative even when using BOSS.

squads

Fixed reaver stuttering, which was caused by a bug. Reavers are slow enough without spending half their movement time stopped.

InformationManager::getNearbyForce() uses the predicted completion time for enemy buildings. This is how the info gets into combat sim.

• The recon squad: Don’t form it before 6 minutes into the game. Formerly the squad was constituted whenever there were “enough” units, which could cause a zergling rush to be weakened by diverting some lings to reconnaissance.

The watch squad: Even if a watch squad zergling is the last zergling on the map, it will remain on station. Formerly, in an emergency it would rejoin the main army, where it did no good and no longer guarded its base from being taken by the enemy. More importantly, stay above ground when detected and endangered. IceBot dealt successfully with the burrowed zerglings: When the SCV could not start construction, it scanned and attacked. The zergling was forced to the surface but instantly reburrowed again and soon died. Oops.

• Squad targeting in the cleanup phase of the game: Don’t send a squad after enemy units that the squad can’t attack. This prevents some cases of zerglings following a floating ebay and the like. There are advantages to following, but I hope this way Steamhammer will be less often distracted from finishing the game.

• Be willing to pull workers a longer distance versus an enemy proxy than versus zerglings. Keeping the workers near their minerals saved many games against zergling attacks, but prevented the workers from engaging proxy buildings. That’s fixed.

• I made changes to try to retreat behind static defense instead of retreating to around static defense, but it didn’t work. Also unsuccessful were changes to cluster regrouping to get late-arriving units to join the fight as they should. The changes are still in the code, though; they’re no worse.

queens

• The maximum count of queens is configured to 6. The previous Steamhammer was already able to handle that, but I didn’t have time to test it before the tournament so I kept it configured to 1 queen max. When 2 or more queens are near each other, they fly in wacky looping patterns to keep away from each other so that they don’t all engage the same target at the same time. That’s a pre-existing behavior, but with 1 queen you don’t get to see it.

• Queens are better at avoiding danger. I widened the safety margin by 1 tile, which made a bigger difference than I expected. Also, queens now recognize science vessels and dark archons as dangers and try to stay away from them.

• I fixed a bug (introduced in tournament version 2.4.1 in December) that made queens less likely to succeed in infesting a command center. Moving around was taking priority over infesting.

• The strategy boss knows how to make higher numbers of queens. It decides how many, and what research to do in the queen’s nest, by looking at enemy unit counts. It used to get the queen energy upgrade too early; now it only gets the energy upgrade if there are many queens to benefit from it, or if the economy is so booming that the expense is negligible.

• The top priority broodling target is a nuking ghost. That brings queens in line with other attacking units.

• Be willing to broodling a less important enemy unit if the enemy doesn’t seem to have any of the most important ones. For example, if you make a lot of tanks Steamhammer may make queens to broodling them. Later in the game, if there are no tanks to be found the queens will be happy to broodling goliaths instead; formerly it would insist on tanks no matter what. Broodling becomes more valuable.

• If there is high total queen energy (because there are a lot of queens), be more willing to use ensnare and parasite instead of broodling when more than one spell is available. Formerly the broodling > ensnare > parasite hierarchy was more rigid. The more flexible queen behavior is better.

• Steamhammer is able to make infested terrans. I fixed the production system bug that prevented it. It uses them very weakly, so I set it to make only 1 at a time and leave time between them.

zerg

Emergency reaction: If too many drones are lost, or a hatchery is lost, break out of the opening. Steamhammer as zerg is too inclined to stick to its opening when it is busted.

Fixed a bug in spellcaster micro that could cause a queen or defiler to go idle. That was painful when it happened; a potentially valuable unit would stop on the map and do nothing for the rest of its life.

Lurker micro improvement: A lurker will not unburrow if an unseen or undetected enemy is thought to be in range to attack it. Formerly, a lurker with no target would unburrow immediately to seek a target, then die to the bunker it could not see at the top of the ramp, or to an untargetable dark templar that was standing next to it. The ramp misbehavior especially caused a lot of unnecessary lurker losses (we saw an example of the same misbehavior from PurpleSwarm in a recent broadcast), but the DT misbehavior is also a key blunder in some games. There is a bug that will still cause a bad unburrow below a ramp sometimes, but at least it’s less common.

• In ZvZ, be less eager to make a defensive evolution chamber right away. Steamhammer has made a lot of unnecessary evos.

• In ZvT and ZvZ, when adding spore colonies, place the first spore in the location closest to the enemy, considering Steamhammer’s main base, natural, and any proxy it may have. (In ZvP, the first spore goes into the natural as DT defense.) There were cases where Steamhammer would place the spore in its main when the natural was closer to the enemy base, and it became impossible to protect the natural from wraiths. At least force them to fly around to a more distant target!

• Drones may burrow for safety when an irradiated unit comes near. This protects them against the eraser trick and other irradiate hazards. I expect that by the time irradiate is researched, burrow will usually be available.

• If the opening book says it wants a hatchery (and no drones have been lost), trust it and make the hatchery. This is to accomodate rare openings which deliberately make hatcheries early. Formerly a safety rule might fire and say “No, you don’t need that hatchery yet.”

• The unit mix versus battlecruisers is adjusted slightly. I hope it will perform better against MadMix’s battlecruisers.

• Don’t make more devourers than needed to counter enemy air. There was already an overall max devourer limit, and a limit depending on how many mutalisks there are. I hope that now, with all factors accounted for, Steamhammer will finally stop overdoing it.

• If Steamhammer is maxed and rich, get overlord sight range.

• Plaguing a cloaked ghost gets a high value, even compared to plague on other cloaked units. It’s to help defend against nukes. Steamhammer really, really does not want to get nuked.

openings

A lot of new openings mysteriously appeared. Where do they come from?

new bot Hopark

We’re getting a steady trickle of new bots. It’s a good sign. The first upload of new zerg Hopark had a consistent crashing bug, but it was fixed by the next day. Hopark’s description is “skeleton Version” so more work is planned.

Hopark starts out with a 5 pool zergling rush, like many other beginning bots. Rather than the standard recipe of more-more-more zerglings, Hopark soon makes an extractor. It mines gas with 2 drones and minerals with 3, and techs to mutalisks. So far I’ve seen one game where it successfully spawned mutalisks, but unfortunately the enemy was already in its base and the mutas did not live long.

Teching on the back of the economy of a fast rush is not exactly a persuasive winning plan, but at least it is unique. I take it as a sign that Hopark may take more interesting paths as it develops.

Hopark is a Java bot. Unpacking the jar file, I see a lot of familiar filenames: It looks like a derivative of the SAIDA team’s tutorial BasicBot (with some documentation in Korean), which is itself inspired by the C++ design of UAlbertaBot. That means it should start with a solid foundation, and we can hope for fast progress.

I think it’s a good idea to start with something like BasicBot that provides the fundamentals and not too many advanced features. If you start with a performance program like Iron, or Locutus, or Steamhammer, then your first draft may be stronger, but you also find yourself dropped into a complex environment where many decisions are already made for you. It will take a long time to find your way around and begin to blaze your own trail. A basic starter bot gives you an easy launch, and you get a sound but simpler and more malleable frame to build on.

Cadenzie versus Locutus 5 game showmatch

Thanks to SCHNAIL we’re starting to see bot matches against strong human players. Today I write about the Cadenzie (Z) 5 game match versus Locutus from last Tuesday. It comes with an interview on Making Computer Do Things. Watch the games first if you’re interested.

Cadenzie is not a top pro; to me her play looks a little slow and awkward compared to the best. But she is very strong, and when the match was announced, I expected her to win every game. In the event, she scored 3-2. I thought all the games were one-sided: Either zerg won fairly easily, or else Locutus collected enough dragoons and was able to overpower her hydralisks with superhuman dragoon micro. In the game where Locutus chose to go zealots instead, the zealots looked wimpy; Steamhammer has had the same experience.

I also felt that the first game was the only one that Cadenzie played with 100% seriousness (and she said as much in the interview). She played a well-rehearsed build with mass hydras and drop. Locutus (lacking PurpleWave’s strategy skills) did not understand how to read her build in order to cut corners in the opening, and it fell behind (slightly behind in bot terms, “massively” behind according to Cadenzie—the distance varies by skill level!). When hydras collected outside its natural, Locutus trickled units out through the narrow opening in its wall and let them be picked off, falling further behind. The front gateway fell, and then overlord speed finished. She’d gotten overlord drop first, and she picked up hydras and put them in the protoss main, where they cleaned up with little effort. I judged that she could as easily have skipped drop and powered through the front door.

I thought the most interesting answer in the interview was “I played in a tournament before where there was a team melee relay style with a mix of progamers and beginner level players and they would take turns every 2 minutes, in a way it was most similar to that.” In other words, Locutus was extremely good at some aspects of the game, and extremely weak at others. That is similar to other games where computer programs were good enough to play humans and not good enough to win every time; for example, chess programs in the old days were superhuman at tactics and weak at strategy in a very similar way.

She repeatedly emphasized that bots need to adapt more to what they scout. I think that’s the main takeaway.

Compare Artosis versus top bots on Twitch: Notice how often Artosis says “In this build, when I see such-and-such, I do so-and-so.” Human players have extensive knowledge of how to play in specific situations, and no bot comes close. SAIDA may come closest, with its one all-purpose build and numerous reactions, but its understanding is shallow by comparison. PurpleWave I think has the greatest strategy knowledge of any bot, but it has a weak understanding of tactics. Locutus relies on its strong micro and aggressive tactics, which cover for the weaknesses in other aspects. Bots not only know less, they don’t integrate their knowledge into a theory of how the game works—they don’t understand what they know, so they a weak at drawing inferences and their adaptation ability is shallow. See for example the Artosis game versus Killerbot by Marian Devecka, where Artosis was able to guess that a third zerg base at an early timing was likely, and scouted for it specifically. In other games, he did not spend effort scouting for expansions he did not see as likely.

By the way, the last game is the best, Artosis vs McRave starting at ~2 hours in.

Filling the knowledge gap I believe will require machine learning. Writing rules and reactions by hand will take a day or two less than forever, and search will not solve all problems if it has only handmade evaluations to rely on. For Steamhammer, I’ve figured out a way to put together familiar algorithms that will execute fast, and I expect it will also learn fast (from little data) and be reasonably accurate (not amazing like deep learning, but adequate). It’s part of my strategy adaptation goal. If it works as well as I hope, I WILL CRUSH YOU PUNY MORTALS BENEATH MY STEEL THUMB BWAHAHAHAHA, or something like that. Actually the first application will be nothing more than an evaluation function to choose openings and strategies, valuable but extracting only a little of the potential, and if it’s successful then after I explain how it works everyone else will get ahead of me again. That will be good too.

Steamhammer 3.0’s new gas steal

Steamhammer’s existing gas steal skill works by UCB, like the opening selection of many bots. It has a bias toward not stealing gas, but the basic behavior is that it will steal gas if that wins more games than not stealing, or if it hasn’t tried a gas steal in a while and it’s time to give it another spin. It pays no attention to what the opponent is doing, so it will try silly things like stealing gas while under zergling attack from a 4 pool. The silly decisions hurt.

Steamhammer 3.0 will have a completely recoded gas steal skill using the skill kit, and it will be much fancier. In fact it got too complicated; today I finished rewriting it to simplify the rules. I’ll upload 3.0 as soon as it passes tests.

The skill records three values for each game: The frame a gas steal was decided on (0 if never), the frame when the refinery building was started, and the lifetime of the refinery (when did the opponent clear the gas steal?). Also newly recorded in the game record is the frame when the enemy was first observed to have spent gas on a unit or building.

All this information, plus a few other items like the recognized enemy strategy, goes into the gas steal decision. The outline is that various checks are made first to see whether stealing gas makes sense (“no, this enemy doesn’t take gas for a long time anyway”). If so, attempt a gas steal randomly with a certain probability until we’ve accumulated 5 games of experience with it. Once the data is in hand, adjust the probability up or down depending on the amount of evidence, with fudge factors to try to take certain strategic points into account (“versus 2 barracks? are you sure about this?”).

It’s still crude compared to the analysis I’d like to do. The ideal would be to figure out the effect of stealing gas on the enemy’s strategy and play, infer the best time to attempt it, and send a worker then to threaten it. But it should avoid many of the blunders that the current method makes.

Next: The Cadenzie-Locutus match.

KangarooBot, KasoBot, NuiBot, DTD Bot

I promised to write up the remaining newcomer bots. Despite my best efforts, I do occasionally keep my promises. Including Slater, which is the most succesful so far, we have 5 new bots that seem to be from 3 different sources. I’m surprised there are so many in such a short time. Did something happen out in the wide world?

Kangaroo Bot

Random KangarooBot from Australia—sorry, from Straya—has an attitude, and the attitude is “gosh darn it, no lazin’ around, the executive plan here calls for makin’ up more silly stuff, we have to put our backs into it.” Even in the code (github) I see variables like String currentBitch; (which can take on the value “Myself” among others) and String newDumbName = ““; (which can take on values like ShootyMcShootSiegeBot and McPurpleBraintus).

Kangaroo is developed from If Bot by the same author, which has a long history as a single .java file.

KangarooBot uses the ASS combat simulator and has a lot of tech skills. As a random player, it knows several terran builds, a few protoss builds, and one build per matchup as zerg. So far so good. The builds are all a little wonky, and the bot has a strong aversion to saturating its minerals with workers. So it often gets stomped (SSCAIT win rate 12%). But, you know, winning is not the main goal here.

KasoBot, NuiBot, DTD Bot

These are by fresh masters students at the Slovak University of Technology. They are newly created bots, so they’re simple for now. But as grad student projects, they aim to use cool tech, and if they succeed then in the future we may all learn lessons from them.

Terran KasoBot says “Currently using randomly selected strategy - automatically extracted from replay dataset (no manual build orders).” Downloading the binary from SSCAIT, I see a BO directory with JSON files of 12 build orders each for TvT, TvP, and TvZ. A build order looks like this:

{"items": [{"name": "Terran_Academy", "id": 112, "type": 2, "supply": 200, "frame": 10915}, {"name": "Terran_Armory", "id": 123, "type": 2, "supply": 200, "frame": 11431}, {"name": "Terran_Barracks", "id": 111, "type": 2, "supply": 22, "frame": 2161}, {"name": "Terran_Bunker", "id": 125, "type": 2, "supply": 200, "frame": 10529}, ...

... and so on for about 5K of text. The build order files are all roughly the same size in bytes. Presumably the randomly chosen build order file feeds to a build order interpreter that figures out how to execute the build order using some of the info in the file. In any case, since the BO items are not sorted by frame number, the files are hard to interpret by eye.

It’s a cool capability for brand-new code, though we don’t know where the replays came from, or how the data was extracted from them, or how the build order execution works. KasoBot’s play so far is weak; it is ranked last of all active bots on BASIL with a win rate of 5%. But it started off with something interesting, and I expect it will only grow more interesting as it progresses.

Zerg NuiBot’s description says nothing much. As far as I have seen, it plays a variety of pool-first builds from 5 pool to 12 pool and it attempts mass zergling attacks and techs to hydralisks. As a new bot, it often makes poor combat and macro decisions, and it has a bug that sometimes causes its drones to stop mining for no apparent reason. And that’s about all I know.

Protoss DTD Bot does not take any actions, even to send its probes mining, and wins only if the opponent plays a crash rush (that is to say, if the opponent crashes early). I hope the author is getting tech help! The description says the aim is to include a decision module using Long Short-Term Memory (LSTM).

ASL 9 games today

Today’s ASL 9 games (video) in the pro world were particularly exciting. If nothing else, I recommend the Stork-Larva game on Inner Coven. A number of the other games were also hard-fought.

I didn’t have time to watch, but I have 2 brain hemispheres, right? So I can do 2 things at once, right?

LetaBot in AIST S3

AIST S3 completed. Round up the usual suspects, you can see the results for yourself. There are replays for all the games, but no video so far. The replays are labeled “Round 1” through “Round 14”; you can read the round numbers (I would have called them match numbers) from the bracket diagram. I like seeing all the results at once, but if you prefer to follow the tournament without spoilers, you could download the replays at the top of the page, scroll down no farther than the picture of the initial bracket to match up replays with the tournament structure, and watch the replays by round number.

I was especially interested to see LetaBot by Martin Rooijackers. LetaBot, it seems to me, is strategically strong but suffers from many bugs. Its standard game plan is a classic terran strategy, defend while building up a large army, then bury the opponent in an avalanche of units. LetaBot knows a variety of specific defenses against different cheeses, and has reactions to enemy moves that threaten its game plan, so I think its potential is high. This version is updated to BWAPI 4.4.0, so possibly it has more updates besides. Martin Rooijackers long ago promised a stronger version with bug fixes.

Versus Dragon: I especially wanted to see this match because of the players’ opposite game plans. Dragon plays an early attack and harassment style. In game 1 of the best of 3, at about 2:35 into the game, Dragon sent its first 2 marines and 5 SCVs to breathe some fire. LetaBot luckily scouted in the right direction and timing to spot the force as it left Dragon’s base, and LetaBot had a bunker on its ramp in plenty of time. Fireproof. Dragon sent the SCVs home right away, but inexplicably continued to produce marines that it posted outside LetaBot’s natural. LetaBot appeared to read the situation and react; it made vultures and goliaths—a strong force when the enemy has fallen behind early—batted aside the marines, and continued straight on to Dragon’s base. Dragon had been preparing medics and tanks behind the scene for its next wave of aggression, leaving itself open to LetaBot’s timing, though LetaBot was well ahead even without that opening. Luck plus skill, 1-0.

In the second game, LetaBot crashed on startup. 1-1

In the third game, Dragon tried the same plan again. LetaBot did not get the lucky scout, but cross positions favored defense. Dragon seemed indecisive, sending SCVs forward and back before attacking with 7. LetaBot floated its barracks after 1 marine, Dragon’s SCVs defeated the marine and moved into LetaBot’s base, some blocking while one built a bunker for the following marines. LetaBot pulled SCVs to defend, and with more it easily won the worker fight, but the bunker completed. LetaBot pulled SCVs out as far as the center of the map to keep marines away from the bunker, but eventually retreated and let 2 marines enter. But the timing was OK, another SCV pull past the loaded bunker kept further marines away while a tank came out and kicked over the bunker; not perfect defense but more than good enough. Fire resistant. Dragon’s SCV attack left it far behind LetaBot’s cheaper SCV defense; LetaBot seemed to understand and quickly won with a tank-goliath attack. LetaBot advances 2-1 on its strong defensive and counter-attacking reactions.

Versus Locutus in the next round: LetaBot crashed 2 games in a row.

Versus BananaBrain in the loser’s bracket: LetaBot crashed 2 games in a row.

So... there might be bug fixes, but we didn’t get to see because there were bugs. :-(

what about the Torch Up tournament?

What ever happened with the Torch Up: FOSDEM’20 Brood War AI Tournament? All the pages saying to be ready by 25 January are still up, and I found a list of 11 participants, but I can’t find any sign of results, including at the FOSDEM 2020 conference which happened at the start of February.

It seemed like an interesting competition with some different decisions than other tournaments. The map pool, in a post that is mysteriously dated 9 March, includes Gold Rush, which has difficult features (Steamhammer struggles on that map, though it can still beat the built-in AI). Some of the optional challenge maps are extremely demanding—are these for a future iteration?

Did the tournament happen? Did it complete? Were the results perhaps announced to raised beers and general carousing so that nobody remembers them any more?

when will that enemy building complete?

Today I pretended to have enough time to play around, fired up my development vm for the first time in days, and took an hour out to solve a niggling issue: How do you correctly predict when an enemy building will finish? My first try gave inaccurate predictions.

If it’s your own building, call building->getRemainingBuildTime(), done. If it’s an enemy building, that doesn’t work. You can see the building’s hit points and predict its completion time from that, but it’s a little tricky. Today I learned the 2 tricks.

One: A building just starting construction doesn’t start at 0 hp and work up, it starts at 10% of its final hp. Over the course of construction, it progresses smoothly from 10% to 100%. Liquipedia on buildings explained it to me. Two: After the hp reach 100%, there is extra latency before the building is actually complete. I didn’t read OpenBW, but a few quick experiments let me measure the latency fairly closely. I got 2 frames for a terran building, 72 or 73 frames for a protoss building for the warping-in animation, and 9 frames for a zerg building. Knowing that, with one glance you can predict the completion of an enemy building to within a few frames (usually to +/- 1 frame in my tests).

    // Predict the completion of a building under construction from its current HP.
    // isBeingConstructed() is false for a terran building with no SCV building it.
    // It's also false for non-buildings, so we don't need to check that separately.
    // A morphed zerg building cannot be predicted this way, so skip those.
    if (unit->isBeingConstructed() && !UnitUtil::IsMorphedBuildingType(type))
    {
        // Interpolate the HP to predict the completion time.
        // This only works for buildings. The prediction is usually accurate to within a few frames.
        // Known cases where the prediction is wrong:
        // * The building was damaged.
        // * The prediction is made during the extra latency period (see below).

        // A building begins with 10% of its final HP.
        double finalHP = double(type.maxHitPoints());
        double hpRatio = (unit->getHitPoints() - 0.1 * finalHP) / (0.9 * finalHP);

        // Buildings have extra latency for their completion animations.
        int extra;
        if (type.getRace() == BWAPI::Races::Terran)
        {
            extra = 2;
        }
        else if (type.getRace() == BWAPI::Races::Protoss)
        {
            extra = 72;
        }
        else // zerg
        {
            extra = 9;
        }

        return extra + BWAPI::Broodwar->getFrameCount() + int((1.0 - hpRatio) * type.buildTime());
    }

Here unit is the building and type is equal to unit->getType().

As the code comments point out, there are ways for it to go wrong. It doesn’t work on a terran building if the constructing SCV has wandered off to play pinochle. It doesn’t work on a zerg morphed building, like a sunken colony morphed from a creep colony or a lair morphed from a hatchery, only on a zerg building made from a drone. If you shoot at the building and hurt it, you’ll get a wrong answer. If the HP have already reached 100% and the building is in its extra latency period, then there’s not enough information to know. I think all those issues can be corrected for if you make the effort, but I didn’t today.

Did I miss anything else? My short time was not enough for many tests.

Using the building completion times for enemy static defense will help the combat simulator avoid mistakes. And the times of tech buildings will help decipher the enemy build order.