archive by month
Skip to content

looking at CherryPi

There’s a ton of code in CherryPi, more than I can read in a day. I tried to pick out key parts to look at.

blackboard architecture

This comment from the file CherryPi/src/upc.h explains an important part of CherryPi’s high-level architecture.

/**
 * Who, where, what action tuple (unit, position, command).
 *
 * UPCTuples are used for module-to-module communication (via the Blackboard).
 * Posting a UPCTuple to the Blackboard is akin to requesting a specific action
 * from another module, and consuming a UPCTuple from the Blackboard implies
 * that the consumer implements this action. The implementation can also consist
 * of refining the UPCTuple so that a more lower-level module can actually
 * execute it.
 *
 * For example, a build order module might post a UPCTuple to create a certain
 * unit type, a builder module might wait until their are sufficient resources
 * before consuming it and then select a worker and a location. The
 * UPCToCommandModule takes care of translating executable UPCTuples (with sharp
 * unit, position and command entries) to actual game commands.
 */

I think it’s an excellent architectural choice, especially for a project carried out by a team rather than an individual. Communication between modules is managed in large part automatically by software rather than manually through the calling conventions of each module. It’s flexible and easy to modify and extend.

relation to Tscmoo

People have speculated that CherryPi may borrow a lot from Tscmoo the bot, since Tscmoo the person is on the team. The speculation even made it into ZZZKBot’s source code, as we saw a couple days ago. I compared 2 slices of code that do similar jobs in CherryPi and the CIG 2017 version of Tscmoo. I looked at the combat simulator in both, and code implementing the 1 hatch lurker opening in both.

Note well: If I had looked at different code, I might have drawn different conclusions. I deliberately selected code with related purposes that might be connected. In some places, CherryPi uses ideas from the old BroodwarBotQ that was written up in Gabriel Synnaeve’s PhD thesis.

1. I think CherryPi directly copied nothing from Tscmoo. I didn’t expect it to. The overall architecture was likely decided before Tscmoo the person joined the team. Besides, an academic usually wants credit to be clear, and a corporation usually wants ownership to be clear. The code in detail looks quite different.

2. In the parts I looked at for this comparison, some structure and ideas in Tscmoo were carried over and seemingly reimplemented in CherryPi, with (I should repeat) great differences in detail. It’s clear that somebody familiar with Tscmoo wrote this CherryPi code. For example, in the combat simulator one has addUnit() and run() in that order, and the other add_unit() and run() in that order. They both refer to “teams”, both count up frames from 0 (I would have counted up from the current frame, some would have counted down to 0), and other shallow similarities.

3. CherryPi, in the parts I compared, seems to be simpler and more cleanly written. In the lurker opening in particular, I think CherryPi encodes the opening a little more abstractly. Sometimes Tscmoo has more features. Tscmoo’s combat simulator simulates splash damage, and CherryPi’s does not.

4. OpenBW is another source of ideas, and it is of course also connected with Tscmoo the person. For example, the FogOfWar class says it is based on OpenBW. It calculates visibility depending on ground height and so on.

the openings

I always want to know, “what openings does it play?” In the directory CherryPi/src/buildorders I see 16 classes that look like they could be build orders. The opening learning files include 15 build orders. The in_use.txt file lists these 8 build orders as active or possibly active:

  • 12hatchhydras
  • zvp10hatch
  • 5pool
  • 2basemutas
  • 3basepoollings
  • 1hatchlurker
  • meanlingrush (9 pool speed)
  • ximptest (it says this one is “unknown status”)

I will watch games and find out what openings it plays in practice. Come back tomorrow!

As a sample of how openings are defined, here is a snip from the file CherryPi/src/buildorders/meanlingrush.cpp showing the basic definition of 9 pool speed:

    buildN(Zerg_Drone, 9);
    buildN(Zerg_Extractor, 1);
    buildN(Zerg_Spawning_Pool, 1);
    if (countPlusProduction(st, Zerg_Hatchery) == 1) {
      build(Zerg_Hatchery, nextBase);
      buildN(Zerg_Drone, 9);
    }

It writes on the blackboard: Make drones until you have 9, extractor and spawning pool, then add a second hatchery at an expansion and rebuild the drones to 9. Simple and concise. Details like spawning the overlord and figuring out exactly when to start the second hatchery are left for other code to fill in (in Steamhammer, you have to specify it explicitly). On the other hand, here is how it says to collect only 100 gas to research zergling speed:

    if (hasOrInProduction(st, Metabolic_Boost) || st.gas >= 100.0) {
      state->board()->post("GathererMinGasGatherers", 0);
      state->board()->post("GathererMaxGasGatherers", 0);
    } else {
      state->board()->post("GathererMinGasGatherers", 3);
      state->board()->post("GathererMaxGasGatherers", 3);
    }

More writing on the blackboard. That’s a complicated test, where in Steamhammer you’d simply specify "go gas until 100". It’s fixable. They could, for example, write goals to the blackboard like “collect 100 gas for zergling speed” and have another module collect only enough gas to meet the goals.

machine learning

I’ll take two cases, online learning during the tournament, and offline learning before the tournament starts, producing data that can be fed to or compiled into the bot.

For online learning, the win rate over time graph for CherryPi shows a rapid increase in win rate from .4 to .7 within the first 10 rounds, then a gradual slight decline to the end of the tournament. It looks as though CherryPi rapidly learned how to play against each opponent, then more or less froze its decisions and allowed slower-learning bots to catch up a tiny bit. (Though swings in score in early rounds can also be due to statistical noise.) The readme file says:

CherryPi is a TorchCraft Zerg bot developed by Facebook AI Research.
It uses bandits to select learn strategies that work against a given
opponent.

“Bandits” refers to the n-arm bandit problem, which is behind most bots with opening learning. Looking at the file CherryPi/src/models/bandit.cpp, I see that that is exactly what CherryPi is doing too. It uses the classic UCB1 algorithm to learn which opening to play against each opponent, just like many other bots.

I looked at the opening learning files, one for each opponent. They are in JSON format and are written by a general-purpose serializer that leaves the data a little hard to interpret by eye. It looks like value2 maps between the 15 opening names and 15 opening index numbers. value3 is 15 zeroes, and value4 and value5 are the learned data for the 15 indexes 0 through 14.

The only offline learning that I found is the same opening learning, performed for certain opponents ahead of time.

  • Iron
  • LetaBot
  • Skynet
  • Xelnaga
  • ZZZKBot

I can’t guess how they came up with that set of 5 opponents to pre-learn openings against. For these opponents, CherryPi relied on its offline learning exclusively; it did not write new learning data for these opponents. It’s such a strange decision that I have to wonder whether it’s a bug. In any case, we saw yesterday that it backfired against ZZZKBot, which did not play as expected: Unable to learn, CherryPi played the same unsuccessful opening every time, and lost over and over. Both ZZZKBot and CherryPi had incorrect prior knowledge about each other, and only ZZZKBot adapted.

conclusion

It is clear to me that CherryPi the project is not far along compared to where they are aiming. There are plenty of TODOs in the code. The big machine learning ideas that (if successful) could make CherryPi superhuman are not there yet; only some foundations are laid. CherryPi is still a scripted bot like others, not a machine learning bot. Even so, with (as I understand it) 8 people on the team, they have done a tremendous amount of work. They implemented ideas—most of which I didn’t write about—that I wish I had time to do myself. If they can maintain the rate of progress, then within a few years individual coders won’t be able to keep up. On the other hand, progress may slow when they get to the hard part. We’ll have to stay tuned!

Next: Games by CherryPi.

ZZZKBot’s games

The AIIDE 2017 win rate over time graph shows #1 ZZZKBot slowly and steadily learning throughout the tournament. It’s easiest to see if you turn off most of the lines of the graph (click on the bots in the legend). #2 PurpleWave shows a different pattern: Without prior knowledge, it starts lower, learns fast at first, then more slowly, and seems to level off before the end of the tournament (though it might be only a temporary plateau).

McRave

McRave upset ZZZKBot 64-46, so watching the games versus McRave lets us see the learning algorithm in action. ZZZKBot does not have a prior strategy versus McRave, possibly because none of its 4 strategies can win reliably against the early cannons. (There are ways to bust the cannons, so it is a limitation of ZZZKBot.)

There are 10 maps in the tournament, and they are played one map per round, so it takes 10 round robins to play all maps once. ZZZKBot played its 4 pool against McRave for the first 10 rounds to see how it did on each map. The answer: It won some and lost some, depending on whether it scouted protoss quickly and whether McRave pulled probes in time to shield the cannons when necessary.

On maps where the 4 pool won, ZZZKBot repeated it when the map came up again. On maps where the 4 pool lost, ZZZKBot switched to its next strategy, the overpool speedlings. The speedlings did not usually do well, because McRave had 3 cannons up in time. ZZZKBot tried to follow up with mutalisks, but McRave scouted that coming and was more than prepared.

I watched the sequence of games on Benzene. The speedlings mostly lost, except for occasional games where zerg managed to kill the scout probe and leave McRave in the dark and unable to react in time. But ZZZKBot kept trying the strategy, only occasionally switching back to 4 pool. I didn’t watch every game, but ZZZKBot’s other 2 strategies didn’t seem to come into play at all.

Iron

Versus Iron, ZZZKBot mostly stuck with its 1 hatch hydralisk strategy, an unusual opening. One odd point is that ZZZKBot researched hydralisk range before speed, which is rare and usually seen only when attacking a protoss wall. As we see in the per-map crosstables, ZZZKBot scored poorly against Iron on 2 player maps and tended to win on 3 and 4 player maps. The difference was that on 2 player maps, Iron was expecting to be rushed and was more willing to build a bunker, which held the hydras.

ZZZKBot sometimes fell back to its sunken defense into mutalisks, but that was less effective. Iron could stop the mutalisks, and its vultures were able to find gaps in the sunken defense.

Iron is the only opponent configured for the hydralisk build order. ZZZKBot doesn’t seem to use it at all, otherwise. I think the build must have been specially developed for Iron.

XIMP

ZZZKBot chose to bust XIMP’s cannons with its speedling build. The zerglings commonly arrived when XIMP had 2 cannons, versus McRave’s 3, and XIMP is not as skilled with probes. It didn’t help that XIMP likes to leave its gateway units in its main “to defend against drops.”

XIMP won only 4 games out of 110. In 3 of its wins, it got its first zealot into the fight in time to save the day. In the fourth, XIMP expanded to another base rather than its natural. ZZZKBot brilliantly scouted the undefended nexus and chose to attack it first, which allowed XIMP’s third cannon time to finish.

CherryPi

CherryPi is an interesting case because ZZZKBot’s “prior knowledge” was guesswork, “Guessing it is like tscmooz.” CherryPi consistently played a sunken defense into mutalisk build against ZZZKBot, with 2 to 4 sunkens. Where did this trend come from? In any case, Tscmoo doesn’t play any such build as far as I’ve seen.

ZZZKBot’s learning kicked in. It tried the 4 pool; no good. It tried the speedlings; no good. It tried its own sunken defense into mutalisks, building only 1 sunken, and that worked perfectly. The sunken was often poorly placed so ZZZKBot tended to lose a few drones, but its mutalisks were out faster.

the Steamhammer forks

ZZZKBot chose its sunken defense into mutalisks versus Steamhammer (with 5 sunkens), Microwave (3 sunkens), Arrakhammer (9 sunkens, because Arrakhammer likes to bust with zerglings), and KillAll (1 sunken), with great success. The sunken count is hand-configured for each opponent. I found it frustrating, because Steamhammer knows in principle how to respond: It makes drones instead of zerglings and goes air. Unfortunately, Steamhammer’s strategy adjustments are sloppy, and it almost always got its own mutalisks too late. It did things like start another hatchery when the spire finished, and then a queen’s nest, and then—well, then it was irretrievable. I knew all along that opponent modeling is crucial for tournaments.

conclusion

Watching games, I was struck that ZZZKBot’s builds are not tight. It doesn’t expand, and in the middle game (when it gets that far) it ends up with more drones than its hatcheries can support. It suffers mineral excess that it can’t spend, and gas shortage because it has only 1 geyser.

Its micro is not tight either. ZZZKBot doesn’t have a combat simulator (well, it would probably be in a subroutine, and as the ancient Greeks declared, only straight lines and circles will do). If the 4 pool leaves cannons alive, then the next 2 followup zerglings will die to the cannons, accomplishing nothing. Then the next 2, etc., until protoss moves out and wins. Followup is minimal; the bot is about winning right away.

ZZZKBot has a lot of clever little skills, but it is missing some big ones. The weaknesses mean that stronger cheese bots are possible. I don’t think the cheese era is over yet.

Next: CherryPi.

looking at ZZZKBot

I read the source of the AIIDE 2017 version of ZZZKBot. It comes with a github repository but at the moment the repo is 2 years out of date.

As a reminder, #1 ZZZKBot was upset by McRave and Tyr which open with cannons to stop most cheeses, and by LetaBot which has extensive work to recognize and respond to different cheeses. ZZZKBot had a plus score against every other opponent, including #2 PurpleWave and #3 Iron. Interestingly, ZZZKBot scored only a little better than even against #26 Slin.

coding style

ZZZKBot is written in a straight-line style, as if subroutines had not been invented. Most of the code is in the onFrame() method. It’s kind of hard to read.

At one point, ZZZKBot checks the name that it is running under using Broodwar->self()->getName(), and if it is not exactly “ZZZKBot” it sets transitionOutOf4PoolFrameCountThresh to 0. The effect depends on the strategy, but it looks as though it usually switches out of its cheese build into late-game play. I have to suppose it is an obfuscation. [Update: Comments suggest that the reason is to avoid lazy copying.] Anyway, be aware that the bot may behave differently depending on the name it is playing under.

strategies and learning

This version of ZZZKBot has 4 strategies.

  • its old standard 4 pool
  • 9 pool speedlings
  • a 1-hatchery hydralisk rush with 11 drones
  • sunken defense into mutalisks

Besides the basic strategy choice, ZZZKBot keeps a few variables that control details of the strategy’s execution.

        bool isSpeedlingPushDeferred;
        bool isEnemyWorkerRusher;
        bool isNumSunkensDecidedAfterScoutEnemyRace;
        int numSunkens;

ZZZKBot also has hardcoded default strategy data for specific opponents, so that against a known bot it can choose a presumed good strategy on the first game. It has values for 17 of the 28 bots in AIIDE 2017, including itself (so 16 of the 27 possible opponents). Considering how thorough the list is, I suspect that Chris Coxe tested against every available bot and manually chose strategies against the ones that ZZZKBot did not defeat on the first attempt. (Against itself it sets all strategy variables to false—I didn’t check what that does.)

  • UAlbertaBot
  • ZZZKBot itself
  • XIMP
  • GarmBot
  • Overkill
  • CherryPi (comment “Guessing it is like tscmooz...”)
  • Ziabot
  • Skynet
  • MegaBot (considered the same as Skynet)
  • Steamhammer
  • Arrakhammer
  • Microwave
  • KillAll
  • ForceBot
  • Juno
  • Iron
  • HannesBredberg

After each game, ZZZKBot may update its strategy data and save the result to a file. The algorithm is long and includes random choices, and I didn’t try to puzzle it out. There are special provisions for playing against random; it checks when the opponent’s race was discovered to see whether the stored info will be useful for the current game. It also, oddly, saves information about the processor it is running on.

scouting a zerg opponent

When your opponent is zerg, a couple tricks can make scouting easier. One is that if you spot the enemy’s first overlord, you may be able to infer the location of the enemy base. I added this knowledge to Steamhammer for the next version, and it frequently helps. Another is that you do not have to scout the enemy hatchery itself. The base is found when you see the creep, and a scouting unit can turn aside as soon as it sees bare ground where creep would be if the base were there. I didn’t add this knowledge to Steamhammer (yet), because it’s tricky. For one thing, I’m not sure of the exact rules for creep spreading; the way unbuildable ground blocks creep is not obvious (commonly creep extends beyond the geyser on both sides but not around it, leaving a notch). For another, a map could have neutral zerg buildings that spread static creep, and BWAPI won’t tell you where the static creep is; an example is Colosseum (though on that map the static creep doesn’t affect scouting). The complications were enough to keep the idea off the top of my priority list.

ZZZKBot implements both of these tricks. How does ZZZKBot know where the creep spreads to for each base location? Simple: It hardcodes the data for each starting position of all 10 maps!

ZZZKBot also tries to guess the location of the enemy base from other enemy units it sees. As a cheeser, ZZZKBot scouts early, so it has a good chance of guessing right.

a comment from the code

            // TODO: support making more than one building of a particular type.

Waste no time on the unimportant!

conclusion

ZZZKBot is very specific and narrowly coded. Its sole purpose is to defeat other bots, especially opponent bots that it knows playing on maps that it knows. Everything possible is hardcoded, above all prior knowledge about opponents and maps. ZZZKBot teaches strict lessons to its opponents, but there are not many lessons in the bot itself. It’s about the cheesiest, most exploitative bot possible.

If ZZZKBot had stuck with its classic 4 pool, I’m convinced that it would have finished in 4th place or worse rather than first. Too many of its opponents in this tournament were ready for the 4 pool (I know Steamhammer was). Next year, I expect that top bots will be prepared for all 4 of ZZZKBot’s strategies this year (it won’t be difficult), and ZZZKBot will have to go to even greater lengths if it is to finish strongly. I see it as a sign of progress: Even the most exploitative bot is forced to play multiple strategies and to rely on machine learning—it is forced to use smart methods to play better. The space for cheap exploits is gradually narrowing.

Next: Looking at how ZZZKBot played against different opponents.

fixing a lurker micro bug

Change of plans. I solved a bug that others should know about, so Steamhammer-Tscmoo games will wait until next time.

Steamhammer has a bug that shows up when a lurker starts attacking a building: The lurker becomes unable to switch targets until the building is destroyed. So, for example, if a lurker doesn’t see any other immediate target, it will go after a supply depot. Once it has started in on the depot, marines can wander up behind the lurker in perfect safety, and perhaps scan and kill it. Until the depot is destroyed, all the marines have to do is stay out of the line of fire between the lurker and its target.

The bug is not in targeting. MicroLurkers correctly recognizes that the marines are higher priority targets and issues a command to target them. The bug is that Micro::SmartAttackUnit() does not act on the command. The code is inherited from UAlbertaBot, so probably many UAlbertaBot and Steamhammer forks have the same problem. Here is the inherited code:

// if we have issued a command to this unit already this frame, ignore this one
if (attacker->getLastCommandFrame() >= BWAPI::Broodwar->getFrameCount() || attacker->isAttackFrame())
{
    return;
}

I think the isAttackFrame() test may be a good idea if you are controlling, say, a dragoon. Retargeting a dragoon while it is in the middle of its animation will at best cause extra delay before the dragoon can fire again. But when I looked closely, it turned out that for a lurker attacking a fixed target every frame is an attack frame. Steamhammer drops the command because the lurker is constantly attacking.

Instead, it should accept the slight delay of retargeting lurkers. I rewrote it like this:

// Do nothing if we've already issued a command this frame, or the unit is busy attacking.
// NOTE A lurker attacking a fixed target is ALWAYS on an attack frame.
if (attacker->getLastCommandFrame() >= BWAPI::Broodwar->getFrameCount() ||
    (attacker->isAttackFrame() && attacker->getType() != BWAPI::UnitTypes::Zerg_Lurker))
{
    return;
}

Lurkers behave better, and other unit types are not affected. I see improved lurker micro in test games.

In Starcraft, every unit type behaves differently. UAlbertaBot, and therefore Steamhammer, tries to treat different unit types the same, which is sure to be causing other weaknesses. At some point I will analyze micro thoroughly and make sure that every unit type realizes its potential. For now, I’m happy that lurkers are a little smarter.

Steamhammer doing well

Apparently I fixed enough bugs and added the right features to Steamhammer, because the bot is doing well on SSCAIT. It’s ranked #5 right now, high up in the mass of bots in the 2200 range. This Steamhammer version has negative scores against Krasi0 and Iron, and against several protoss bots—and that’s all. It is equal or winning in head-to-head score against every other opponent it has faced on SSCAIT, including all other zergs.

I don’t expect to do as well in AIIDE, because there are unfamiliar opponents that I can’t prepare for. But I hope to finish fairly high.

The upcoming 1.4 version has changes to improve results against protoss. The following version 1.5 plans mutalisk skills beyond other bots, which will be especially dangerous to terran. It should be able to notch some wins against Krasi0 and Iron. But it will take time, and everybody else is improving too....

Next: Games versus Tscmoo.

Steamhammer is getting better at scouting

I have improved Steamhammer’s early game scouting and added midgame scouting. It’s far short of human scouting skill, but much improved. The first overlord and the drone scout cooperate to locate the enemy base as quickly as possible, and the overlord moves there to watch as long as it can. I also taught it to notice the enemy’s overlord and try to infer the enemy’s base location (ZZZKBot is the only other bot that I know has that skill). The effect is that Steamhammer finds the enemy sooner and collects more information. The midgame scouting ensures that enemy expansions are found, and in my testing often destroyed quickly.

I already find it hard to watch the release Steamhammer play on SSCAIT, since the new one is so much less clumsy at finding things. I think that Steamhammer 1.4 will be stronger than the current Steamhammer even with opponent modeling turned off, because of the improved scouting, slightly more flexible tactics to take advantage of the improved scouting, and a couple of key bug fixes.

It still doesn’t overlord scout versus terran, though. It has no concept that marines are dangerous or that cliffs are useful.

Overlord scouting, by the way, is a deep topic. Pros often send their first overlord straight to the closest possible enemy natural base. But they sometimes move the overlord on a complex path, trying to catch proxies, or avoid the enemy scout, or spot activity without being seen, or just to stay flexible so the overlord can move to its choice of locations depending on the situation. An overlord that always follows the most efficient scouting path is predictable, and the enemy may be able to save time by scouting for your overlord instead of directly for your base.

Overkill’s CIG 2017 replays

Well, I looked at Overkill’s replays from CIG 2017 as I said I would, but I was disappointed. Not only was the CIG 2017 Overkill carried over from the previous year (which I forgot), but the CIG 2016 was also carried over from the year before (which I also forgot). Overkill from CIG 2017 is the same as Overkill from AIIDE 2015, from before the big learning feature was added. It plays the same as the Overkill version on SSCAIT.

Nothing to see here. :-(

Of the tournaments in yesterday’s table, AIIDE 2016 has the least-old version of Overkill. Last year’s version is the first version with unit choice learning, not only opening learning. The other tournaments all played the AIIDE 2015 version which has only simple opening learning like many bots. The AIIDE 2016 result is not far out of line with the other results, so we can guess that the new learning feature worked adequately but not great.

Overkill AIIDE 2017 updates

PurpleWaveJadian pointed out in a comment that Sijia Xu had updated Overkill’s repository, from last year’s version to bring it up to date with the AIIDE 2017 version of Overkill. There are extensive changes. The bot looks substantially more capable.

Here’s what caught my eye. This is based on a quick read through a long list of diffs, so I may have missed or misunderstood a lot.

• New machine learning library with a couple different learning methods.

• Support for lurkers, scourge, guardians, devourers, ultralisks—all the regular zerg combat units. Only queens and defilers are omitted, the spell units. Last year it supported only zerglings, hydralisks, and mutalisks.

• Major changes in tactical calculations. Well, there would have to be to support new units.

• Changes to building construction. I’m not sure what the idea is here.

• It detects production deadlocks, to avoid freezes. The method is written to be simple and general-purpose rather than extensive and tweakable like Steamhammer’s, so it may be worth a look. See ProductionManager::checkProductionDeadLock().

• Scouting seems to be completely rewritten. Overlords scout around using an influence map. It looks like 1 zergling can be singled out to be a scout zergling.

• Overkill 2016 learned a model of when to make zerglings, hydralisks, or mutalisks. The actions in this Overkill version include the combat units except for guardians and devourers, sunken colonies, tech buildings to construct, upgrades and tech to research, expanding, attacking versus harrassing with mutalisks, and doing nothing. So it not only has a wider scope for strategy with more unit types, it looks as though it learns more of its strategy. I see declarations for 4 neural networks, which apparently cooperate to do different parts of the job.

• It chooses from among the same 3 openings as before: 9 pool, 10 hatch, 12 hatch. The details of the build orders have been revised.

How does it play? Well, I only looked at the source, and not too closely. Overkill first got a learning model in the CIG 2016 tournament. Here are its historical tournament results:

tournament#%
CIG 2015381%
AIIDE 2015381%
CIG 2016 qualifier471%
CIG 2016 final551%
AIIDE 2016762%
CIG 2017760%

The CIG 2016 qualifier is a better comparison to the other tournaments than the final, since it includes all entrants. The CIG 2017 entry is probably not much different from the AIIDE 2017 one. [Update: Not so. CIG says that the CIG 2017 was the same as the CIG 2016 version. See comments.] It seems that Overkill’s win rate has been falling as it learns more on its own. Is that a sign of how hard it is to do strategy learning? I would like to see it play before I draw conclusions!

Next: I’ll look at Overkill’s CIG 2017 replays and see what I can see.

an awful game Steamhammer-Microwave

Steamhammer and Microwave are both rated close to 2200 at the moment, putting them in the top ranks on SSCAIT. There is a big pileup at that skill level; only Krasi0 and Iron are significantly stronger. And yet a few days ago, Steamhammer and Microwave struggled against each other in a game with so much nonsense play that I was raging at them the whole time, sitting on the sofa with a laptop and shouting “Why are you doing that? DON’T DO THAT! Aargh!” Facepalm. There is so far to go.

It had better be true that you learn the most from mistakes, because there are a lot of painful mistakes here that are hard to look at.

The game is Steamhammer vs Microwave on Icarus. Steamhammer opened overlord, then hatchery, spawning pool, and gas all on 9/17. Microwave opened spawning pool on 9 and gas. Steamhammer’s worker defense is poor and the 9 pool will win if Microwave sends its zerglings immediately in the right direction. Microwave scouted with a drone and found Steamhammer’s base on the second try, which was too late. But Steamhammer's zerglings were distracted by the scouting drone, and Steamhammer did well to survive with 2 drones lost. Microwave could have won on the spot if it had scouted with an overlord, or if it had inferred Steamhammer’s base location when it saw Steamhammer’s overlord.

pulling drones while zerglings are distracted

Steamhammer continued to chase the scouting drone. Urgh. The misbehavior is still there because I haven’t found an easy way to prevent it that doesn’t cause other bad behaviors—if it ignores the scout, that’s bad too. My plan to fix it requires time and care, so I’m putting it off for now. Anyway, moving the zerglings out of position offered opportunities to Microwave to stab into the mineral line, but Microwave played overcautiously and did not. Aggressive play would be to rush all zerglings in to wreak havoc, then try to escape when Steamhammer reacts, so that you don’t lose to the counter. Safe play would be to detach 2 to 4 zerglings to try to catch a drone, while keeping the others back for defense; if the raiders are surrounded and lost it is no terrible setback, and if they kill a drone that is an edge.

the zerglings are even more distracted

Steamhammer finally killed the scout and started to realize the advantages of its build. With 2 hatcheries from the start, it was far ahead in zerglings, and Microwave was forced into static defense. Microwave added 2 sunkens in its main... and expanded, so then it added 2 more sunkens at its natural. 4 sunkens are a fatal expense, and Steamhammer was miles ahead. In the picture, Steamhammer has so many more zerglings that it should have attacked immediately.

four sunkens in Microwave’s bases

There are a lot more mistakes in the rest of the game, but the story is getting tiresome. Steamhammer refused chance after chance to widen its lead or win outright. It held back when it should have attacked. It built an ultralisk cavern and researched the expensive ultra upgrades, then did not build a single ultralisk until they lost their value. It made guardians and devourers and used them wastefully (partly because it hit a bug which assigned units to the wrong squads). It became so lax in applying pressure that Microwave caught up in economy. You can watch the end of the game yourself, if you can stomach it.

units behaving unhelpfully

It was much smoother than past Steamhammer games that I have blogged as disastrously bad. The bot is slowly getting more solid.

upgrade prices in Steamhammer

Today I solved a major cause of production freezes. Steamhammer did not understand that repeated upgrades increase in cost, and I didn’t register that BWAPI requires you to specify the level of upgrade to get the prices of upgrades above level 1. So if Steamhammer wanted to upgrade zerg carapace +2 (gas cost 225) and had 200 gas, then if gas collection was turned off it would never turn gas collection on. It concluded “the upgrade costs 150 gas so I have enough.” The upgrade sat in the queue waiting for enough gas to appear, and the gas never appeared.

I think it’s the most common permanent production freeze remaining in the live version 1.3.3. Most long-lasting production freezes happen when gas is turned off and stays off.

The fix is to add code to MacroAct::mineralPrice() and MacroAct::gasPrice(). I verified that all upgrade prices are fetched via MacroAct; prices that it fetches directly are always for units or buildings, which are constant price. I added this code to MacroAct::gasPrice() (and similarly for the mineral price):

	if (isUpgrade())
	{
		if (_upgradeType.maxRepeats() > 1 && BWAPI::Broodwar->self()->getUpgradeLevel(_upgradeType) > 0)
		{
			return _upgradeType.gasPrice(1 + BWAPI::Broodwar->self()->getUpgradeLevel(_upgradeType));
		}
		return _upgradeType.gasPrice();
	}

Finally! I didn’t trace all the consequences, but the fix probably corrects other bugs too.

Steamhammer 1.4 status

I’m working on version 1.4 now, and the major feature in version 1.4 will be opponent modeling. But after the AIIDE rush I felt tired of opponent modeling, and discouraged by the bugs, so I’m improving scouting first. Good scouting is important for opponent modeling to work well, so I’m happy to do scouting in the same version. No more rush; I will take my time and do it right.

I added a Recon squad that scouts around with ground units in the middle game, as soon as there are enough ground units to spare a few for scouting. It doesn’t search efficiently, but it does catch enemy expansions sooner or later, and sends eyes around the map to places Steamhammer used to ignore. I also made a small tactical change so that the attack squads are less maniacally focused on the enemy main base and more willing to attack other bases. Besides improving opponent modeling, I think it should help directly against most opponents that try to mass expand or to hide expansions.

I also refactored code so that I can work on overlord scouting. The first overlord will coordinate its movement with the scouting drone. Currently, if Steamhammer sends out both its first overlord and a scouting drone, they both head for the same base. It’s disgusting, but it has been that way since December when I first hacked in crude overlord scouting. Overlord scouting in 1.4 won’t be a polished gem, but it shouldn’t be a dirt clod any more.

I’m also making the usual random improvements here and there to things I come across. I finally realized why Steamhammer keeps making guardians against XIMP by Tomas Vajda: It doesn’t recognize that corsairs and carriers counter guardians. Oops. I left out that bit of knowledge and only now caught the mistake.

biggest weaknesses

The worst news is Steamhammer is doing poorly against the top protoss bots lately, for two reasons. First, some are using multiple strategies, and the current Steamhammer’s random openings cannot counter all of them. Opponent modeling should help. Second, protoss is often playing forge expand or other big macro openings, which Steamhammer only partially understands. Opponent modeling will again help, because then Steamhammer should usually be able to find good strategies in response, instead of only occasionally. But opponent modeling won’t be enough (unless it sticks with timing attacks like hydra bust), because Steamhammer’s tactics tend to break down in a big macro game. To have the right units at the right time is no help if you throw them away. Fixing tactical play, the way I intend to do it, will be another major feature.

In other words, I expect to have to finish 2 more major features, opponent modeling and tactical analysis, before Steamhammer can score consistently against top protoss bots. It won’t happen soon. Opponent modeling is underway, but tactical analysis likely won’t be until next year.

I will be able to make progress on small features. The 3 biggest weaknesses that can be corrected with minor features are:

1. Better worker micro. Drills would help in a lot of defensive situations. Even without drills, better choices about engaging and running away would reduce losses.

2. Run workers away from enemy attacks. It’s a basic skill that top bots have to some degree and Steamhammer doesn’t. Steamhammer not only loses workers in a base under enemy attack, it transfers more to the base to try to keep mining gas. Keeping workers alive would be a big boost to resilience. I will likely stop the transfers as one fix, and run workers away as a later fix.

3. Pathing in the presence of map blocks. Units too often get stuck trying to take a path that is blocked. The work to fix pathing is started, and I see it as related to dropping BWTA. I’ll continue step by step.

I’m not working on any of these 3 at the moment. One goal at a time. In 1.4 I will probably take at least one more step toward removing BWTA.

Steamhammer 1.3.3 is uploaded

Steamhammer 1.3.3 is uploaded. Fixing the disastrous “I seem to be making a command center, I’d better cancel it” bug was trickier than I expected. After I did fix it, I saw the game Steamhammer vs AyyyLmao, which was just as disastrous. I had to watch closely to see what happened: At about 1:15 into the game, Steamhammer moved a drone to start its spawning pool, and simultaneously sent out its scouting drone. The scouting drone momentarily blocked the spawning pool from starting, which happens from time to time and causes a slight delay. Not this time, though. The spawning pool was canceled on the spot due to another bug, and Steamhammer’s build order was disrupted. Steamhammer recovered poorly and lost the game against an opponent it should have beaten.

Anyway, I made several tweaks to the rules for giving up on buildings, and I tested more thoroughly with games against a variety of opponents. I think it’s mostly OK now. But the change is more fragile than I realized, and I won’t be surprised if more bugs are lying in wait.

Next: The full CIG 2017 results are posted, but they don’t come with the colorful crosstable (at least not yet). I’ll supply my usual red-and-blue crosstable.

Steamhammer 1.3.2 added a severe terran bug

Ack! I fixed one bug that happens when terran gives up on constructing a building, but I missed an even more serious terran bug in the same code. In Steamhammer 1.3.2, terran is unable to build a command center! The bot gives up on the building partway through and cancels it.

See Randomhammer vs ICEbot for an example. Randomhammer expanded late, and ICE had already placed a spider mine at its expansion spot, so Randomhammer gave up on its first attempt as intended. After its vessel came out, Randomhammer eventually cleared the mine and started the command center again—and again—and again, canceling it over and over. Mega ouch! I coded up a quick 14CC opening and verified that the bot can’t finish a command center at all.

The bug is so severe that it deserves a quick 1.3.3 release with a fix. Stand by, I’ll try to get it in today.

Steamhammer 1.3.2 is uploaded

Steamhammer 1.3.2 change list. It amounts to the donated openings, 3 fixes for terran and protoss because I only tested zerg in the AIIDE version, and 2 minuscule tweaks to zerg—and of course configuration to play on SSCAIT instead of in the AIIDE tournament. Otherwise it is the same as the AIIDE version.

  • New protoss openings from Antiga, donated by Iruian. Protoss play will be more varied and hopefully stronger.
  • Version 1.3 had a configuration mistake, when Randomhammer rolled protoss or terran, affecting opponents Dave Churchill (UAlbertaBot) and Andrey Kurdiumov (a UAlbertaBot fork with software engineering changes rather than play changes). It was configured to always play a zerg opening, so when the bot was not zerg, the opening group was not set. Randomhammer played normally... except that it never built a combat unit. Combat units are made depending on the opening group. I corrected the configuration. Also see the next item.
  • Added a workaround in case the opening group is not set to a correct value and it needs to be: Set a default opening group when the error is noticed. As it always has, it puts a message on the screen when the error is noticed, so you’ll know what’s wrong.
  • The new “cancel a building if it takes too long or if too many workers are lost” feature included a bug for terran. Partly constructed buildings could be left around instead of being canceled. Fixed.
  • I changed the formula for making lurkers as aux units, so that Steamhammer makes more aux lurkers. Aux units are extra units added to the regular unit mix. This should improve play when Steamhammer has lurker tech but isn’t using lurkers as a primary unit.
  • If there are mutalisks, then devourers go into the flying squad with the mutalisks. They were mistakenly always put in the ground squad. It should improve devourer play, at least sometimes and a little.

Tomorrow I’ll update Steamhammer’s web page with the source. Then I will return to the opponent model.

Steamhammer 1.3.2 and Antiga’s openings

The biggest new feature in Steamhammer 1.3.2 will be the new protoss openings from Antiga (thanks to Iruian). I sorted the openings into a different order and renamed a few to keep to a more consistent scheme. I’m leaving out the 9-10 gate opening for now; it is only slightly different from 9-9 gate and plays worse because of BOSS, and Antiga leaves it out too.

I’m not straying too far from Antiga’s weights, either. I question some of them, but they are tested and known good and provide variety. I’m making only relatively small adjustments. And that’s the story. It’s taking time to configure and test everything, but I’ll push out the new version before long.

If anybody else would like to donate openings, I’ll include them in the Steamhammer distribution as extras if they are remotely useful. If they’re good, I’ll configure them to play in the live Steamhammer. Since protoss has just been fed, the terran openings are now begging for their handouts.

Someday, when Steamhammer is smarter than now, I’ll build the opening tree and have Steamhammer choose opening lines on the fly. Possibly I’ll have both a concrete tree of macro actions and an abstract tree of strategic plans. At that point, the bot will be able to decide for itself under what circumstances an opening is good, and it will make sense to feed it as many variant openings as possible regardless of quality. I don’t mind accumulating data now for that future day.

the donated openings from Antiga

Here’s what I found out about Antiga’s donated openings. (Thanks again to Iruian, by the way.) This post is only about the openings themselves. Also donated are weights, how often to play each opening in each matchup, which I’ll consider separately. My plan is to distribute the donated file along with Steamhammer’s source, so that whoever wants to can easily borrow the openings and weights. The openings are good, so I’ll also put most of them directly into Steamhammer’s regular configuration; Randomhammer will play them when it rolls protoss.

Going by names, Antiga’s openings are a superset of Steamhammer’s. But 3 of the openings with the same names are different, so it’s a little confusing.

identical openings

  • 1ZealotCore
  • DTRush
  • DTDrop
  • CorsairDT (weak because Steamhammer sucks with corsairs)
  • 12Nexus
  • 13Nexus

openings new in Antiga

These are good openings, so at the moment I’m thinking I’ll throw them into Steamhammer’s config for active use. The weights are a separate question.

  • 9-10Gate
  • NoZealotCore
  • 10-15GateGoon
  • 2ZealotCore
  • 2GatewayGoonExpo
  • Nexusfirst5zealotExpo

openings that are shared but different

I tested the shared openings by playing them against each other head-to-head. I’ll call it Steamhammer versus Antiga, though both sides were running identical Steamhammer code. I didn’t pay attention to the results of the games, which varied depending on how battles happened to come out. I paid attention to macro: Who had more income, who produced units more efficiently, who was able to expand sooner.

9-9 gate The 2 openings proceeded in lockstep until Antiga went out of book. Then Steamhammer’s beautifully optimized UAlbertaBot zealot rush quickly pulled ahead. It evened out somewhat after Steamhammer also went out of book, but not entirely.

10-12 gate Steamhammer’s 10-12 gate opening is not as highly optimized. Again the openings were identical until Antiga went out of book. Steamhammer pulled ahead, but not dangerously. I don’t see any big difference in strength between the openings, only a small advantage to Steamhammer’s existing opening.

ForgeExpand The openings are the same, except that Antiga adds a third photon cannon at the end of the opening. It barely delays the early zealots and dragoons, so I judge it an improvement: Safer and practically as aggressive. I will switch Steamhammer’s opening to this version.