archive by month
Skip to content

CIG 2018 - what AIUR learned

Here is what the classic protoss bot AIUR learned about each opponent over the course of CIG 2018. AIUR has not been updated in many years and has fallen behind the state of the art, but its varied strategies and learning still make it a tricky opponent in a long tournament. Seeing AIUR's counters for each opponent tells us something about how the opponent played. For past editions, see AIIDE 2017 what AIUR learned and what AIUR learned (AIIDE 2015).

This is generated from data in AIUR's final write directory. There were 125 rounds and 5 maps, one 2-player and two each 3- and 4-player maps. For some opponents, all games were recorded, giving 25 games on the 2-player map and 50 games each on 3- and 4-player maps. For most opponents, fewer games were recorded. AIUR recorded 2932 games, and the results table lists 318 crashes for AIUR. 2932 + 318 = 3250, the correct total game count. Unrecorded games were lost due to crashes, and for no other reason.

First the overview, summing across all opponents.

overall234total
 nwinsnwinsnwinsnwins
cheese7249%12765%13235%33149%
rush2941%26933%26155%55944%
aggressive1323%22568%18478%42271%
fast expo3324%18548%20748%42546%
macro4633%18052%13560%36153%
defensive14175%31473%37955%83465%
total33454%130056%129856%293256%
  • 2, 3, 4 - map size, the number of starting positions
  • n - games recorded
  • wins - winning percentage over those games
  • cheese - cannon rush
  • rush - dark templar rush
  • aggressive - fast 4 zealot drop
  • fast expo - nexus first
  • macro - aim for a strong middle game army
  • defensive - try to be safe against rushes

Looking across the bottom row, you can see that AIUR had a plus score on every size of map, and that it had to choose different strategies to do so well. It's a strong result for a bot which has essentially no micro skills and has not been updated since 2014. It does still have the best cannon rush of any bot, if you ask me.

#1 locutus234total
 nwinsnwinsnwinsnwins
cheese10%80%2512%349%
rush10%100%60%170%
aggressive10%40%50%100%
fast expo10%140%50%200%
macro10%70%40%120%
defensive10%714%50%138%
total60%502%506%1064%

Even against the toughest opponents, AIUR can scrape a small edge with learning. Against Locutus, it pulled barely above zero, but got a few extra wins because it discovered that its cannon rush occasionally scores on 4-player maps. Results against PurpleWave below are similar. I suspect that if AIUR had played the cannon rush every game, Locutus would have adapted and nullified the edge. Maybe it did, and that’s why the edge is so small.

#2 purplewave234total
 nwinsnwinsnwinsnwins
cheese10%80%3918%4815%
rush10%80%20%110%
aggressive10%100%30%140%
fast expo40%80%20%140%
macro10%100%20%130%
defensive30%60%20%110%
total110%500%5014%1116%


#3 mcrave234total
 nwinsnwinsnwinsnwins
cheese1100%10%10%333%
rush10%412%10%432%
aggressive00%20%30%50%
fast expo10%10%4217%4416%
macro10%30%10%50%
defensive10%20%20%50%
total520%502%5014%1059%

Against McRave, the choice is nexus first. McRave must have settled on a macro opening itself.

#4 tscmoo234total
 nwinsnwinsnwinsnwins
cheese1127%10%10%1323%
rush10%10%30%50%
aggressive10%119%10%138%
fast expo520%3315%10%3915%
macro10%20%2214%2512%
defensive10%20%2218%2516%
total2020%5012%5014%12014%

Against the unpredictable Tscmoo, AIUR wavered before settling on an unpredictable set of answers. Notice that not all the strategies are well explored: If you win less than 1 game in 5, then playing an opening 3 times is not enough. If the tournament were much longer, AIUR would likely have scored higher because of its slow but effective learning.

#5 isamind234total
 nwinsnwinsnwinsnwins
cheese10%20%40%70%
rush1100%3719%388%7614%
aggressive00%10%30%40%
fast expo10%50%20%80%
macro10%10%20%40%
defensive10%40%10%60%
total520%5014%506%10510%

ISAMind may be based on Locutus, but unlike Locutus it is vulnerable to AIUR’s dark templar rushes. It’s a sign that it is not as mature and well tested.

#6 iron234total
 nwinsnwinsnwinsnwins
cheese10%10%50%70%
rush10%2619%20%2917%
aggressive00%20%20%40%
fast expo10%10%3110%339%
macro10%195%40%244%
defensive10%10%60%80%
total50%5012%506%1059%


#7 zzzkbot234total
 nwinsnwinsnwinsnwins
cheese40%20%20%80%
rush40%40%10%90%
aggressive30%20%10%60%
fast expo30%30%10%70%
macro70%50%40%160%
defensive40%3429%4112%7919%
total250%5020%5010%12512%

4 pooler ZZZKBot is of course best countered by a defensive anti-rush strategy. Well, it helped, but the rush is too strong for AIUR to survive reliably. On the 2-player map, AIUR found no answer.

#8 microwave234total
 nwinsnwinsnwinsnwins
cheese20%20%10%50%
rush10%277%10%297%
aggressive10%10%10%30%
fast expo10%20%10%40%
macro10%10%922%1118%
defensive1822%1724%3625%7124%
total2417%5012%4922%12317%

Microwave apparently also played a rushy style versus AIUR. That’s interesting. I think that AIUR’s defensive strategy is good against pressure openings generally, so Microwave was likely playing low-econ but not necessarily fast rushes.

#9 letabot234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush10%10%333%520%
aggressive00%333%10%425%
fast expo10%4149%4349%8548%
macro1100%333%10%540%
defensive10%10%10%30%
total520%5044%5044%10543%

Fast expo makes sense against LetaBot’s “wait for it... wait for it... here it comes!” one big smash.

#10 megabot234total
 nwinsnwinsnwinsnwins
cheese10%20%30%60%
rush250%40%3811%4411%
aggressive10%30%30%70%
fast expo10%30%20%60%
macro20%3628%20%4025%
defensive1894%20%20%2277%
total2572%5020%508%12526%

Why did MegaBot have so much more trouble on the 2-player map? According to the official per-map result table, MegaBot did fine overall on Destination (the one 2-player map), so its trouble came only against AIUR. Maybe I should watch replays and diagnose it.

#11 ualbertabot234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush20%4337%20%4734%
aggressive10%20%10%40%
fast expo10%20%10%40%
macro1833%10%10%2030%
defensive10%10%4416%4615%
total2425%5032%5014%12423%


#12 tyr234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush1100%10%3281%3479%
aggressive00%3746%875%4551%
fast expo1100%333%367%757%
macro10%633%333%1030%
defensive10%20%333%617%
total540%5040%5072%10555%

I suspect that Tyr suffered here because it is a jvm bot and could not write its learning file.

#13 ecgberht234total
 nwinsnwinsnwinsnwins
cheese1100%3889%250%4188%
rush1100%10%4367%4567%
aggressive00%475%10%560%
fast expo1100%10%20%425%
macro10%367%10%540%
defensive10%367%10%540%
total560%5082%5060%10570%


#15 titaniron234total
 nwinsnwinsnwinsnwins
cheese10%10%250%425%
rush10%10%333%520%
aggressive00%4279%4288%8483%
fast expo10%10%10%30%
macro1100%250%10%450%
defensive1100%30%10%520%
total540%5068%5078%10571%

TitanIron appears to have been too predictable. Notice that the winning strategy on most maps was never tried (without crashing) on the 2-player map. It might have won there too.

#16 ziabot234total
 nwinsnwinsnwinsnwins
cheese1650%250%10%1947%
rush10%20%10%40%
aggressive10%10%333%520%
fast expo10%250%00%333%
macro10%10%10%30%
defensive333%4269%4457%8962%
total2339%5062%5052%12354%


#17 steamhammer234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush367%475%9100%1688%
aggressive3100%17100%15100%35100%
fast expo20%20%250%617%
macro1100%10100%10%1292%
defensive14100%16100%22100%52100%
total2483%5092%5094%12491%


#18 overkill234total
 nwinsnwinsnwinsnwins
cheese10%30%250%617%
rush00%250%10%333%
aggressive00%10%1060%1155%
fast expo10%367%00%450%
macro00%00%00%00%
defensive1688%4190%3778%9485%
total1878%5080%5072%11876%


#19 terranuab234total
 nwinsnwinsnwinsnwins
cheese1100%888%10%1080%
rush1100%11100%30100%42100%
aggressive00%475%250%667%
fast expo1100%16100%683%2396%
macro1100%989%1090%2090%
defensive1100%250%10%450%
total5100%5092%5090%10591%


#20 cunybot234total
 nwinsnwinsnwinsnwins
cheese10%250%475%757%
rush1100%10%20%425%
aggressive00%475%1392%1788%
fast expo10%250%250%540%
macro1100%989%13100%2396%
defensive1100%32100%15100%48100%
total560%5090%4990%10488%


#21 opprimobot234total
 nwinsnwinsnwinsnwins
cheese1100%12100%683%1995%
rush1100%5100%7100%13100%
aggressive00%7100%4100%11100%
fast expo1100%11100%17100%29100%
macro1100%8100%7100%16100%
defensive1100%7100%9100%17100%
total5100%50100%5098%10599%


#22 sling234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush1100%5100%250%888%
aggressive00%13100%13100%26100%
fast expo1100%7100%10100%18100%
macro1100%8100%11100%20100%
defensive1100%16100%13100%30100%
total580%5098%5096%10596%


#23 srbotone234total
 nwinsnwinsnwinsnwins
cheese10%250%10%425%
rush1100%9100%367%1392%
aggressive00%13100%16100%29100%
fast expo1100%10100%8100%19100%
macro1100%786%6100%1493%
defensive1100%9100%16100%26100%
total580%5096%5096%10595%


#24 bonjwa234total
 nwinsnwinsnwinsnwins
cheese1100%9100%475%1493%
rush1100%13100%10100%24100%
aggressive00%7100%10100%17100%
fast expo1100%6100%7100%14100%
macro1100%7100%8100%16100%
defensive1100%8100%11100%20100%
total5100%50100%5098%10599%


#25 stormbreaker234total
 nwinsnwinsnwinsnwins
cheese475%10%475%967%
rush00%580%10100%1593%
aggressive00%18100%7100%25100%
fast expo00%00%6100%6100%
macro00%9100%8100%17100%
defensive20100%17100%15100%52100%
total2496%5096%5098%12497%


#26 korean234total
 nwinsnwinsnwinsnwins
cheese7100%2100%10100%19100%
rush00%7100%8100%15100%
aggressive00%5100%8100%13100%
fast expo00%8100%8100%16100%
macro00%5100%6100%11100%
defensive14100%23100%10100%47100%
total21100%50100%50100%121100%

Well, if you win every game, learning cannot help.

#27 salsa234total
 nwinsnwinsnwinsnwins
cheese9100%15100%9100%33100%
rush00%00%3100%3100%
aggressive00%11100%8100%19100%
fast expo00%00%4100%4100%
macro00%8100%7100%15100%
defensive15100%16100%19100%50100%
total24100%50100%50100%124100%

CIG 2018 - bots that wrote data

The CIG organizers have released the final read/write folders for the 2018 tournament. I looked through all the folders to see if each bot recorded information. If it saved nothing, it did not learn. If it saved some data, it may have used it for learning (or it might be log files or whatever). I also added a curve “↕” column, whether the bot’s win rate moves up or down (or is approximately flat) between round 40 and the end of the tournament—is the bot improving until late in the tournament? Win curves early in the tournament are noisy, so they’re hard to compare. (If anybody can’t see the Unicode up and down arrows, let me know and I can change them.)

Some bots have files in their AI folder, which may be prepared data or pre-learned data for specific opponents. I note that too. Prepared data could be kept elsewhere, including in the binary, so I didn’t see all of it. We know that PurpleWave had extensive preparations for specific opponents.

As has been mentioned, bots in Java or Scala (bots which run on the jvm) were unable to write learning data. Those that depend on their learning data were playing at a severe disadvantage. #2 PurpleWave lost narrowly to #1 Locutus and was one of the affected bots. It’s a serious problem for a tournament that wants to be taken seriously.

#botinfo
1LocutusPrepared data for 11 opponents. Learning data very similar to Steamhammer’s.
2PurpleWave-jvm :-(
3McRave-Looks like wins and losses of each of 16 available strategies for the previous 8 games. Perhaps a sliding window?
4tscmooLooks like strategy and win/loss info for each opponent, in a hard-to-read structured format. Past years have had more elaborate data.
5ISAMindPrepared file that looks like neural network learning data. Per-opponent learned data that looks like Steamhammer data. ISAMind is based on Locutus, so that makes sense.
6IronNothing.
7ZZZKBot-Game records, one game per line, in an opaque format that looks about the same as last year.
8MicrowaveFor each opponent, wins and losses for 8 different strategies.
9LetaBotInformation about a few recent games against ZiaBot, probably not used for learning.
10MegaBotExtensive log data. The apparent learning files are MegaBot-vs-[opponent].xml and give scores for NUSBot, Skynet, Xelnaga (MegaBot’s three heads).
11UAlbertaBotWin/loss numbers for 4 protoss, 4 terran, and 5 zerg strategies. But the same strategy was always chosen for each race, so learning was turned off.
12Tyrjvm :-(
13Ecgberhtjvm :-(
14AiurThe familiar lists of numbers for each opponent.
15TitanIronNothing.
16ZiaBotOne file with data for TerranUAB, UAlbertaBot, and 3 lines for SRbotOne. Zia’s learning looks broken or disabled.
17SteamhammerSteamhammer saved data when it did not crash, and successfully learned a little bit.
18OverkillA file for each opponent, game records with opponent/opening/score.
19TerranUAB-Nothing.
20CUNYbotOne file output.txt with strategy information and numbers, naming a few opponents but not most. A prepared file in the AI folder has the same format. It’s mysterious.
21OpprimoBotNothing.
22SlingNothing.
23SRbotOneA large number of “stats” files named with date and time, apparently game records. For each opponent, another file giving the strategy “Terran_Attrition” and win/loss numbers. I’m not sure whether this could be learning data, but the bot did earn an up arrow.
24BonjwaNothing.
25StormbreakerPrepared data NN_model_policy and NN_model_stateValue, apparently neural network learning data. For each opponent, game records with 4 numbers per game. The format is like Overkill’s but records more information.
26Korean-Nothing.
27Salsa-Nothing.

Most striking is that the “nothing” bots cluster toward the bottom. If you don’t even try to record data, either you are Iron or you performed weakly. The jvm bots, which in fact recorded nothing due to no fault of their own, still placed higher than all the nothing bots other than Iron. Perhaps recording data is a proxy for how much effort has gone into the play.

Some bots had a rising win rate (an up arrow) despite doing no learning, most distractingly UAlbertaBot. I think that since UAlbertaBot plays random, its opponents can easily get confused about it. In general, I think that playing unpredictably (either being random or choosing varied openings randomly) can mess up the learning of some other bots.

I will be analyzing what certain bots learned. It will shed light on their opponents.

the critical vital absolutely essential filename question of the decade

Here is a question of virtually no importance that nevertheless has me puzzled: What should I call my learning files?

For now, Steamhammer has an opponent model file for each opponent, named om_[opponent].txt. OM for opponent model. It makes sense, or anyway it makes as much sense as an arbitrary filename can. I like names that make sense.

The next learning data I want to add is opening models, so that Steamhammer can know the timings. With knowledge of both opponent strategies and its own opening strategies, it will be able to directly compare and find counters: Your attack comes at this time, which opening is ready at that timing? It will also be able to recognize which openings are similar to others, so that when it can’t match a strategy, finding good openings by trial and error is quicker.

What should I name the opening model files? Opponent models and opening models are both empirical data, and should be updated as games are played. But the unfortunate words “opponent” and “opening” are too much alike to abbreviate nicely. Should I start with “enemy” and “build order”? “Bot” and “strategy”? Most abbreviations seem unintuitive. My best idea so far is to rename the om_* files to bot_* and use the prefix open_ for openings. Maybe OK?

What’s your idea? Because, according to the bikeshed principle, everyone should have an opinion on this....

Steamhammer’s learning results

When I uploaded Steamhammer 1.4.3 to SSCAIT on 11 June, I erased its learned data from the server. Its elo immediately plunged, partly because the voters wanted to put it through its paces against strong opponents, and partly because it needs its learning data to cope. Most rushbots, and many others, won their first or first few games against Steamhammer. That didn’t change when I uploaded 1.4.4 a week later—the improvements weren’t many.

Finally, only in the past several days, I’ve started to feel that Steamhammer has learned enough that it is closing in on its equilibrium elo. It has been wavering around the high 2100’s, not able to break above 2200 but not falling far either. It seems about right, at least for SSCAIT conditions.

The findings. As I’ve mentioned, clearing the learning data was a deliberate test to see how well the learning system works when learning from scratch. I’m fairly pleased. I see weaknesses, but only weaknesses I expected. Against XIMP, Steamhammer has settled on an opening that has won every game so far, but is not as strong as the 3 hatch before pool opening that I hand-chose for it in the old days. Steamhammer only sees that it wins. It can’t tell the difference between openings that win nearly all games because it sees only the winning rate; it needs an evaluation function that can tell it “this one wins more convincingly.” Against Proxy, it won one game with its unusual 6 pool opening. Then it played another game and recorded another win—because Proxy crashed. Steamhammer thought it had found a winner, and had to lose some games before it realized that the 6 pool was not a reliable counter. (It would be, if not for Proxy’s powerful worker defense.) Possibly 5 pool or 4 pool would succeed, but Steamhammer does not know that some openings are related to others. When I teach it that, it will be able to realize that if one opening shows promise but is not quite successful, it should try related openings.

In some cases, Steamhammer hit on surprising counters. The most striking example is against TyrProtoss, which had been winning every game with its cannon turtle into timing attack strategy. Steamhammer tried its 2 hatch lurker all-in attack, which did not make sense to me—Steamhammer’s lurkers suck when cannons are around, it has little idea how to break the cannons and no idea how to bypass them. But it won a game. We’ll see if it keeps winning!

I expected the weaknesses, and I expected the surprising counters. I feel as though I understood the learning system and its limitations fairly well. It gives me confidence that my planned improvements, when I finally get around to them, will be real improvements.

no more enemy-specific strategies for Steamhammer

Working on the opponent model today, I made one of the key changes for the next version:

    "UseEnemySpecificStrategy" : false,
    "EnemySpecificStrategy" :
    {
    },

No more openings hand-configured for known opponents. Steamhammer has to figure out everything on its own. I’ve been working toward this for a long time, and it’s good to finally take the step. I expect play to become more varied—Steamhammer is likely to discover surprising solutions for some opponents. Play should also become stronger, especially in tournaments where opponents like to prepare specially against select enemies. They’ll have to look for ways to exploit Steamhammer’s tactical and micro mistakes, because the game plans will be too adaptive.

I also wrote the terran vulture-first recognizer for the plan recognizer today. It recognizes a plan called Factory that can only be followed by terran, and Steamhammer zerg is configured to counter the plan with the AntiFactory opening. Testing against Iron, it worked perfectly: The first game, Iron won easily. The second game, Steamhammer countered and fought back hard (and happened to win). That’s how it’s supposed to work.

The recognizer was easy to write. Maybe I should write a few more recognizers and counters.

Iron should be a good test case, because Iron is strong enough to usually defeat the counter—AntiFactory puts up a tough battle, but still mostly loses. Opening learning success looks like this: Steamhammer realizes that AntiFactory is probably best, though not all that good, and explores other openings sometimes but not too often. I think I should be able to get that right.

Will playing better games against Iron entice voters on SSCAIT? I think it might happen. If so, I will quickly grow bored with similar Iron-Steamhammer games, but stream watchers may be pleased. Iron would likely lose a few elo points to Steamhammer on average, instead of gaining as it does now.

The upcoming version 1.4.2 has important improvements for all races, including some improvements I haven’t mentioned. Strategy, macro, and micro are better. Look forward to higher rankings for Steamhammer and Randomhammer.

Legionnaire’s analysis of Sparkle and Transistor

The planned post about strategy abstraction is delayed by a power outage at my house. Here’s a brief filler.

TeamLiquid has a post with analysis of new ASL maps Sparkle and Transistor by Australian protoss Legionnaire. Without drawing any strong conclusions about overall balance, Legionnaire points out how map features will affect play.

Current bots are poor at adapting to map features. More than that, it is beyond the state of the art for any AI system to adapt to maps with as few games as humans need. Humans reason out how map features affect play, and with experience they sharpen their reasoning. Machines, so far, mainly collect statistics about the course of events, and they need a vastly larger number of games to zero in on good strategy. Of course they may be able to play those many games faster, but we don’t know how to make a system that can combine reasoning with empirical learning like a human. I’m interested in Legionnaire’s expert analysis as an example that may offer clues.

adapting to the maps

Instead of configuring all of Steamhammer’s opening probabilities by hand, I want it to figure them out for itself. The starting point is data: For each matchup, keep overall statistics of all the openings in a file. Another goal is to have Steamhammer adapt its strategy to maps based on experience. So I thought, why not combine the two? For each matchup and map, keep a file with numbers for all the openings. Or for each matchup, keep a file for all the pairs (map, opening)—that way the bot has the data to generalize between maps.

Someday I also want Steamhammer to adapt its strategy reactions and its tactics to the map. At first it will analyze the map and decide what looks good (“look at that cliff over the mineral line—I should go air or drop”); later it will learn from experience what works well. I don’t expect to get to that for a long time, though.

Steamhammer has over 60 zerg openings (it doesn’t play all of them), and the count will increase. SSCAIT has 14 maps and other tournaments use fewer, so I think I should be ready for on the order of 1000 pairs (map, opening) if I keep them in one file. Each pair would be one line of data, something like “<opening> <map> <# games> <# wins>” and maybe a few more numbers like mean game length, or a statistical summary of evaluations once there is an evaluation function, or whatever else. If I want a cache of map data like number of starting positions and rush distances and so on, to use in generalizing across maps, that would be in a separate file.

In that case there would be 12 matchup files, including data for when the opponent went random: TvT TvP TvZ TvR, PvT PvP PvZ PvR, ZvT ZvP ZvZ ZvR. With up to 1000 lines per file, it seems like a reasonable amount of data. In every game, Steamhammer would read one file in onStart() and write it in onEnd(), which doesn’t seem excessive. There is one complication. If the opponent goes random, and after I give Steamhammer the ability to change its mind on the fly (which I will do), then when Steamhammer finds out the opponent’s race it may want to read that matchup file too. Reading and analyzing that much data may take more than one frame time, so it might have to go into a separate thread. Another solution for when the opponent goes random might be to read 4 matchup files during onStart() when we are allowed more time. Well, when it comes up I’ll figure out a plan. Maybe nothing special will be needed (seek time for a hard drive could exceed one frame time, but reading from SSD is faster).

That’s the data. How to use it? I haven’t decided on details. The opponent model keeps detailed records for each game against a given opponent, including the map. When we play the opponent for the first time, decisions have to be made without the opponent model, solely on the basis of the matchup+map+opening statistics. I’ll figure out a more-or-less sound way to turn the raw numbers into probability-to-play-this values, including an exploration policy. There are choices. After we’ve played an opponent many times, the opponent model will have more information (since it records more data, and it is specific to the opponent), so it can decide on its own. In between, I’ll need some kind of evidence combination procedure to blend the 2 sources of information together. I expect that a simple procedure would work fine, even weighted averaging of probabilities.

Steamhammer’s configuration file will become much shorter. I expect I’ll retain the manual configuration options for openings, for those bot authors who want to do it that way, but Steamhammer itself will rely on the data it collects. Once I have a good set, I’ll distribute it with the bot.

I’m not sure when I’ll get to all this stuff. Maybe or maybe not in the 1.4.x series.

Next: A tree of openings. It ties in.

the opponent model in Steamhammer 1.4

Today is Steamhammer 1.4’s opponent model. The features are:

  • Recognize some enemy opening plans.
  • Predict the enemy’s opening plan from experience against this opponent.
  • React to the enemy’s predicted or actual opening plan.
  • Choose openings based on the predicted plan.
  • Decide whether to steal gas. Does experience suggest it may be worth trying against this opponent?

The code that implements the features has these parts:

  1. The plan recognizer, which looks at the current game situation.
  2. Game records, which save information about past games against this opponent, including the recognized plan.
  3. The plan predictor and gas steal decider, which draw conclusions based on the game records.
  4. Strategy reactions to predicted or recognized enemy plans are in various places–they are uses of the opponent model, not parts of the opponent model.
  5. Opening choices to counter the predicted plan can be set in the configuration file.

1. The plan recognizer was first written up in December. It tries to understand the opponent’s intentions during the earliest part of the game. The code is in OpponentPlan.cpp and is less than 150 lines—it is rudimentary.

  • Unknown - No plan recognized. The plans are not exhaustive, so this is common.
  • Proxy - Enemy buildings in your base. This doesn’t include cannon contains or other more distant proxies.
  • WorkerRush - Like Stone.
  • FastRush - Basic units faster than 9 pool, 8 rax, or 9 gateway.
  • HeavyRush - 2 gate zealots, 2 barracks marines, etc.
  • SafeExpand - Static defense before expanding to the natural. Zerg can’t do this.
  • NakedExpand - Expansion with no static defense.
  • Turtle - Static defense without expanding.

My early impression of the plan recognizer was that it often failed to recognize plans, but was rarely wrong when it did. With more experience, I think it often misrecognizes plans severely. It’s crude and clumsy. Even so, when it helps it helps a lot. It’s a net win.

2. Game records are handled in GameRecord.cpp. Steamhammer writes a file for each opponent, like many learning bots. Here is one record from a file named om_UAlbertaBot.txt, where “om” stands for opponent model, with annotations so you can make sense of the list of numbers.

1.4                  <- record format version (from the Steamhammer version when it was first used)
ZvRZ                 <- matchup
(2)Destination.scx   <- map
Over10Hatch          <- Steamhammer’s opening
Heavy rush           <- initial predicted enemy plan
Fast rush            <- actual enemy plan
1                    <- we won, 1 or 0
0                    <- frame we sent a scout to steal gas, 0 if never
0                    <- gas steal happened, 1 or 0 (extractor was/was not queued)
2110                 <- frame enemy first scouted our base
3102                 <- frame enemy got first combat units
0                    <- frame enemy got first air units
0                    <- frame enemy got static air defense
0                    <- frame enemy got mobile air defense
0                    <- frame enemy got cloaked units
0                    <- frame enemy got static detection
1726                 <- frame enemy got mobile detection
12309                <- last frame of the game
END GAME             <- end mark

Theoretically, a single file could have records in more than one format. Of course, only one format exists so far. The “frame enemy got” times recorded are the time we first saw such a thing, which may be much later than it actually happened. For example, here we first saw an enemy overlord (a mobile detector) on frame 1726, but it existed the whole game. The END GAME mark is redundant. It gives us a way to recover in case data in the middle of a file is corrupted—we can skip ahead past the next END GAME and continue reading the file from there.

3. The plan predictor and gas steal decider look at the game records near the start of the game.

The gas steal decider was written up in December. Nothing important has changed since then.

The plan predictor runs once at the start of the game. It looks through the game records to see what recognized plans the enemy has played. It is close to the bare minimum: It counts the recognized plans in the game records for this matchup, ignoring unknown plans, and weighting recent games more using a discount factor so that the past is gradually forgotten. That way it reacts quickly when the enemy changes its play. Whether the game was won or lost, whether past predictions were correct or wrong—all the other information is ignored.

If the opponent was random, then when the opponent’s race is found out, the plan predictor runs again. In the first run, it counted all game records where the opponent went random. In the second run, it counts only games where the opponent was the same race as this game. The predicted plan may change.

4. Strategy reactions could be written in anywhere, but so far they are in StrategyManager for terran and protoss, and in StrategyBossZerg for zerg. So far, all the reactions are reactions to the enemy plan, not to any of the other information (even though it has obvious uses). Some strategy reactions are made when the enemy plan is recognized. Some reactions must begin in time, and happen when the plan is predicted. For example, against UAlbertaBot, the initial predicted plan is “Heavy rush”. If Steamhammer finds out that UAlbertaBot is zerg, it knows that zerg follows a different plan. The predicted plan changes to “Fast rush” and efforts to stop the zerglings begin immediately (if Steamhammer is zerg or terran; the protoss reaction is turned off because I didn’t get it working).

Good strategy reactions are the hard part. I find them much more difficult than the opponent model proper. Some of Steamhammer’s reactions are weak.

5. Openings to counter specific enemy plans can be written into the configuration file in a new subsection CounterStrategies. The opening is chosen once at the start of the game and can’t be changed (in this version), so only the initial predicted enemy plan matters.

Steamhammer first looks for counter strategies specific to the enemy race. The name is in the format “Counter [plan name] v[race character]”. The race character is “U” for Unknown if the opponent went random. You can use all the usual features of random opening selection. As you can see in the example, zerg is configured with a wider range of choices.

"Counter Safe expand vT" :
	{
		"Terran" : "14CCTanks",
		"Protoss" : "13Nexus",
		"Zerg" : [
			{ "Weight" :  1, "Strategy" : "FastPool", "Weight2" : 5 },
			{ "Weight" :  9, "Strategy" : "9PoolSpeed" },
			{ "Weight" :  0, "Strategy" : "ZvT_3HatchMuta", "Weight2" : 50 },
			{ "Weight" : 50, "Strategy" : "ZvT_3HatchMutaExpo", "Weight2" : 0 },
			{ "Weight" : 30, "Strategy" : "3HatchLurker" },
			{ "Weight" : 10, "Strategy" : "3HatchPoolMuta", "Weight2" : 0 }
		]
	},

If no counter strategy is found for the specific enemy race, Steamhammer next looks to see if there’s a general one for all races—leave out the “vX” string. This example points to a reusable strategy combo that specifies openings for each race Steamhammer might be playing, no matter the enemy race. The strategy combo feature has not changed.

"Counter Worker rush" : "AntiFastCheese",

If no counter strategy is found, Steamhammer falls back on its usual random opening selection. So if the regular repertoire is best in any given case, don’t specify a counter for that case.

For the following version Steamhammer 1.4.1, I will add at least one large piece to the opponent model. I'm likely to change my mind once I see how this version does in practice, but at the moment I'm thinking of fine-tuning plan prediction and opening selection based on wins and losses. That will raise Steamhammer's performance ceiling; with enough games, it will learn much more. Other possibilities include: • Make use of more of the information in the game records. This opponent doesn't get detection, use cloaked units; that opponent gets air units around frame X, add a spire for scourge just in time. • Have Steamhammer collect data on its own openings so it can generalize: Play a fast/slow opening; play a lurker/muta opening. • Revamp the plan recognizer with a richer set of plans, maybe a hierarchy so it can refine its conclusions over time. • Machine learning for a probabilistic plan recognizer and/or plan predictor. • Restore the originally planned extensive game records, making it possible to predict unit mixes over time throughout the game. • Add the ability to change openings during play instead of deciding ahead of time, so that decisions can be made as late as possible. • Move scout timing into the opponent model alongside the gas steal decider. • Have expansion decisions or hatchery placement influenced by the opponent model. “Hmm, historically in situations like this we do poorly if the third hatchery is at an expansion. Make it a macro hatchery instead.”

I have no shortage of ideas!

Next: Brief remarks on the SSCAIT round of 8.

strategy learning by solving the game matrix

Being unpredictable to your enemies has value. How can you do strategy learning and still remain unpredictable when you should? You can’t simply require randomness, because if one strategy dominates, then you should play it every time. At other times, you may benefit from playing 2 strategies equally, or by playing a normal strategy 95% of the time and otherwise rushing. It depends on what opponent strategies counter each of yours, and the n-arm bandit methods that bots use now don’t understand that. Here’s one way to do it. It’s a step up in complexity from UCB, but not a tall step.

You can record the results of your strategies and the enemy strategies in a zero-sum game matrix, and solve the strategy game (which is the subgame of Starcraft that involves choosing your strategy). In the first cut version, each cell of the matrix is “n times it happened that I played this and the enemy played that, and k of those were wins.” Take the observed probability of win for each cell of the game matrix as the payoff for that cell, and solve the game. The solution tells you how often you should play each of your strategies, assuming that the opponent chooses optimally.

There are a couple different algorithms to solve zero-sum game matrixes fast. I personally prefer the iterative approximate algorithm (here is a simple python implementation), but it doesn’t make much difference.

If you recognize a lot of strategies on both sides, you’ll have many matrix cells to fill in, each of which requires some number of game results to produce a useful probability. 10 strategies for each side already means that a big AIIDE length tournament won’t produce enough data. For a first cut, I recommend recognizing only 2 or 3 categories of enemy strategies, such as (example 1) rush, 1 base play, 2 base play, or (example 2 for zerg) lings and/or hydras, mutalisks, lurkers. Since you’re grouping enemy strategies into broad categories, you don’t need much smarts to recognize them.

You can group your own strategies in a completely different way, if you like. There’s no reason to stick to the same categories. Also, your bot presumably knows what it is doing and doesn’t need to recognize game events as signifying that it is following a given class of strategy.

In this method, you are assumed to choose your strategy before you scout, or at least ignoring scouting information. You can take your time to recognize the enemy strategy, and base the recognition decision on anything you see during the entire game.

How do you get started learning? You might want to start with a matrix of all zeroes and only use the game matrix for decisions after you’ve gathered enough data. Instead, I suggest keeping a global matrix alongside the ones for each opponent, with floating point game counts and win counts in each cell. The global matrix has the totals for all opponents. (Or maybe there’s a global matrix for each opponent race.) When you face a new opponent, initialize the new opponent’s matrix with scaled down game counts and win counts from the global matrix, as if only a small number of games had been played in total (I suggest 1 to 3 times the number of cells in the matrix as a try). You’ll start out playing a strategy mix that is good against the average opponent, and as you accumulate data the mix will shift to specifically counter this opponent.

There are tons of ways to fancy it up if you want to try harder. You could try a variant where you estimate the enemy’s choice probabilities instead of assuming the enemy plays optimally (you’ll need a different solution algorithm). You can keep a larger game matrix in parallel with the small one, and switch to it when you’ve accumulated enough data. Or use a hierarchical method that unfolds categories when there is enough data to distinguish them. You can try a more complicated Bayesian game solution algorithm, which realizes that the numbers in each cell are empirical approximations and takes that into account (“oh, this cell doesn’t have many games, better not rely too strongly on its value”). You can include scouting information in the strategy decision (“well, I can see it’s not a rush, so strike out that option for the opponent”). You can divide your notion of strategy into any fixed number of aspects, and keep independent matrixes for each aspect, so that your strategy choices are potentially random in many different dimensions. The sky is the limit.

AIIDE 2017 what AIUR learned

Here is what AIUR learned about each opponent over the course of the tournament. I did this mostly because it’s easy; I already had the script from last year. But it’s also informative—AIUR’s reactions tell us how each bot played, and may tell bot authors what they need to work on.

The data is generated from files in AIUR’s final read directory. AIUR recorded 111 games against some opponents even though the tournament officially ran for 110 rounds; that is presumably because the tournament did run longer but was cut back to a multiple of 10 rounds for fairness (since there are 10 maps). On the other hand, AIUR’s total game count according to itself is 2938 and according to the tournament results is 2965, so it may have been unable to record some games (it is listed with 53 crashes, so that’s not a surprise). First an overall view, totalling the data for all opponents. We can see that all 6 of AIUR’s strategies (“moods” it calls them) were widely valuable: Every strategy has win rate over 50% on some map size. AIUR’s overall win rate in the tournament was 50.46%.

overall234total
 nwinsnwinsnwinsnwins
cheese15955%5937%16144%37947%
rush13466%8755%18550%40656%
aggressive10756%10843%15530%37041%
fast expo6945%8433%19751%35046%
macro4628%6952%21137%32639%
defensive35260%18558%57055%110757%
total86757%59249%147948%293850%
  • 2, 3, 4 - map size, the number of starting positions
  • n - games recorded
  • wins - winning percentage over those games
  • cheese - cannon rush
  • rush - dark templar rush
  • aggressive - fast 4 zealot drop
  • fast expo - nexus first
  • macro - aim for a strong middle game army
  • defensive - be safe against rushes (not entirely successful)
#1 zzzkbot234total
 nwinsnwinsnwinsnwins
cheese1612%10%40%2110%
rush50%10%10%70%
aggressive30%10%50%90%
fast expo40%10%50%100%
macro30%20%30%80%
defensive30%1631%3724%5625%
total346%2223%5516%11114%

AIUR struggled against the tournament leader but was not entirely helpless. Its cannon rush had a chance on 2 player maps and its anti-rush strategy on the others. We see how AIUR gains by taking the map size into account.

#2 purplewave234total
 nwinsnwinsnwinsnwins
cheese10%10%20%40%
rush2879%333%4055%7163%
aggressive10%333%10%520%
fast expo10%1136%1060%2245%
macro10%20%10%40%
defensive10%10%10%30%
total3367%2129%5551%10951%

AIUR upset #2 PurpleWave, a surprising outcome. The DT rush and the fast expand were both somewhat successful—rather unrelated strategies.

#3 iron234total
 nwinsnwinsnwinsnwins
cheese50%10%70%130%
rush50%20%70%140%
aggressive30%20%120%170%
fast expo80%147%90%313%
macro60%10%100%170%
defensive50%20%100%170%
total320%225%550%1091%

Learning can’t help if nothing you try wins....

#4 cpac234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush40%00%20%60%
aggressive20%10%10%40%
fast expo10%10%10%30%
macro20%333%20%714%
defensive2438%1669%4850%8850%
total3426%2255%5544%11141%

Cpac was configured to play 5 pool against AIUR. It worked, but AIUR was able to compensate to an extent by playing its anti-rush build.

#5 microwave234total
 nwinsnwinsnwinsnwins
cheese20%20%40%80%
rush10%10%40%60%
aggressive2020%1513%110%4613%
fast expo10%20%60%90%
macro10%10%40%60%
defensive10%10%2612%2811%
total2615%229%555%1039%

Microwave was successful but showed a little vulnerability to surprise zealots dropped in its main. I suspect it’s a tactical reaction issue.

#6 cherrypi234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush10%10%10%30%
aggressive20%20%10%50%
fast expo20%10%10%40%
macro20%10%911%128%
defensive264%1612%4212%8410%
total343%229%5511%1118%


#7 mcrave234total
 nwinsnwinsnwinsnwins
cheese26100%560%4562%7675%
rush367%967%450%1662%
aggressive10%450%10%633%
fast expo10%250%250%540%
macro10%10%10%30%
defensive10%10%20%40%
total3385%2255%5556%11065%

AIUR upset McRave with its cannon rush, and the dark templar rush did well too. AIUR executes the best cannon rush of any bot, in my opinion. It is a sign that McRave’s play was not robust enough against tricks.

#8 arrakhammer234total
 nwinsnwinsnwinsnwins
cheese20%20%30%70%
rush10%10%40%60%
aggressive10%560%30%933%
fast expo10%10%20%40%
macro00%1250%3837%5040%
defensive2966%10%425%3459%
total3456%2241%5428%11039%


#9 tyr234total
 nwinsnwinsnwinsnwins
cheese667%10%10%850%
rush20100%10%20%2387%
aggressive333%1020%10%1421%
fast expo10%729%4935%5733%
macro10%10%10%30%
defensive250%20%10%520%
total3379%2218%5531%11043%

The DT rush won 100% of the time on 2 player maps and was tried only a few times on larger maps, losing. Was it only unlucky on the 3 and 4 player maps, or is there a real difference? With only 3 games total, we can’t tell from the numbers. It is a weakness of AIUR’s learning: It’s slow because there is so much to learn. The flip side of the slowness is that, over a long tournament, it learns a lot.

#10 steamhammer234total
 nwinsnwinsnwinsnwins
cheese20%10%10%40%
rush250%10%20%520%
aggressive10%10%10%30%
fast expo10%10%10%30%
macro00%10%10%20%
defensive2781%1788%4967%9375%
total3370%2268%5560%11065%

I was surprised to see Steamhammer upset by AIUR. I had thought that AIUR was a solved problem. On SSCAIT too, Steamhammer started to show losses against AIUR in September for the first time in months. I may have introduced a weakness in some recent version and AIUR’s learning took that long to find it on SSCAIT. In AIIDE, the tournament was easily long enough.

#11 ailien234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush30%10%20%60%
aggressive10%20%10%40%
fast expo10%250%00%333%
macro450%875%10%1362%
defensive2458%888%4937%8148%
total3447%2264%5433%11044%


#12 letabot234total
 nwinsnwinsnwinsnwins
cheese743%10%20%1030%
rush333%1354%4340%5942%
aggressive540%10%10%729%
fast expo1346%333%10%1741%
macro10%10%633%825%
defensive10%333%10%520%
total3040%2241%5435%10638%

I suspect that fast expo was the best strategy on 4 player maps, but how was AIUR to know? A weakness of AIUR’s epsilon-greedy learning, compared to UCB, is that it doesn’t realize that a less-explored option is more likely to be misevaluated.

#13 ximp234total
 nwinsnwinsnwinsnwins
cheese3435%00%10%3534%
rush00%00%10%10%
aggressive00%138%522%653%
fast expo00%90%00%90%
macro00%00%10%10%
defensive00%00%00%00%
total3435%225%552%11113%


#14 ualbertabot234total
 nwinsnwinsnwinsnwins
cheese00%00%1100%1100%
rush00%00%00%00%
aggressive00%00%1100%1100%
fast expo00%00%00%00%
macro00%00%00%00%
defensive3432%215%5227%10724%
total3432%215%5430%10926%

What’s up with all those zeroes? AIUR is coded to try each strategy once before it starts making decisions, and that did not happen here. It turns out that AIUR has pre-learned data for Skynet, XIMP, and UAlbertaBot, so its learning in those cases looks different.

#16 icebot234total
 nwinsnwinsnwinsnwins
cheese10%20%10%40%
rush10%250%333%633%
aggressive3100%367%450%1070%
fast expo14100%367%4493%6193%
macro475%250%10%757%
defensive989%1080%250%2181%
total3288%2264%5582%10980%


#17 skynet234total
 nwinsnwinsnwinsnwins
cheese1392%00%00%1392%
rush2195%2190%5188%9390%
aggressive00%00%00%00%
fast expo00%1100%00%1100%
macro00%00%00%00%
defensive00%00%450%450%
total3494%2291%5585%11189%


#18 killall234total
 nwinsnwinsnwinsnwins
cheese10%30%10%50%
rush10%20%10%40%
aggressive10%20%10%40%
fast expo10%30%10%50%
macro00%20%250%425%
defensive3080%1070%4976%8976%
total3471%2232%5569%11162%


#19 megabot234total
 nwinsnwinsnwinsnwins
cheese367%10%20%633%
rush20%1436%50%2124%
aggressive667%425%40%1436%
fast expo250%10%40%714%
macro10%10%3625%3824%
defensive1776%10%20%2065%
total3165%2227%5317%10633%


#20 xelnaga234total
 nwinsnwinsnwinsnwins
cheese9100%683%10%1688%
rush19100%475%10%2492%
aggressive10%333%10%520%
fast expo10%475%10%650%
macro20%250%5036%5435%
defensive250%367%10%650%
total3485%2268%5533%11156%

Against Xelnaga, AIUR found solutions on 2 and 3 player maps but not on 4 player maps. Is it another case of underexploration?

#21 overkill234total
 nwinsnwinsnwinsnwins
cheese10%10%367%540%
rush250%00%00%250%
aggressive8100%4100%786%1995%
fast expo367%3100%7100%1392%
macro475%367%1292%1984%
defensive1493%11100%2696%5196%
total3284%2291%5593%10990%


#22 juno234total
 nwinsnwinsnwinsnwins
cheese50%1436%3315%5219%
rush30%10%10%50%
aggressive20%10%20%50%
fast expo20%10%1612%1911%
macro10%10%10%30%
defensive1921%425%20%2520%
total3212%2227%5513%10916%

Juno’s cannon contain upset AIUR. Learning didn’t help much, because the problem wasn’t in any of the strategies, it was in AIUR’s poor reactions to cannons appearing in front of its base. It is amusing to watch 2 bots cannon each other when sometimes both get cannons up.

#23 garmbot234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush250%10%00%333%
aggressive1794%17100%367%3795%
fast expo00%10%2383%2479%
macro00%10%10%20%
defensive580%10%2781%3379%
total2584%2277%5578%10279%


#24 myscbot234total
 nwinsnwinsnwinsnwins
cheese10%10%250%425%
rush20%367%250%743%
aggressive333%2100%978%1471%
fast expo10%250%10%425%
macro450%4100%367%1173%
defensive2361%10100%3879%7176%
total3450%2286%5575%11169%


#25 hannesbredberg234total
 nwinsnwinsnwinsnwins
cheese580%3100%367%1182%
rush250%3100%250%771%
aggressive250%250%20%633%
fast expo8100%3100%989%2095%
macro250%4100%1191%1788%
defensive15100%7100%28100%50100%
total3488%2295%5589%11190%


#26 sling234total
 nwinsnwinsnwinsnwins
cheese250%10%333%633%
rush250%00%10%333%
aggressive12100%00%2396%3597%
fast expo10%5100%10%771%
macro367%580%1275%2075%
defensive580%11100%1580%3187%
total2580%2291%5580%10282%

Here is another possible case of insufficient exploration. The 4 zealot drop won 100% of the time on 2 player maps and 96% of the time on 4 player maps, but was never tried on 3 player maps (I guess due to a crash, since AIUR tries to play each strategy once). It’s not a severe problem, though, because 3 player maps did have 2 strategies that scored 100%.

#27 forcebot234total
 nwinsnwinsnwinsnwins
cheese10%10%10%30%
rush00%10%10%20%
aggressive367%20%10%633%
fast expo00%10%10%20%
macro00%978%367%1275%
defensive29100%875%4894%8594%
total3394%2259%5585%11083%


#28 ziabot234total
 nwinsnwinsnwinsnwins
cheese12100%786%3686%5589%
rush10%1100%475%667%
aggressive6100%888%683%2090%
fast expo10%10%20%40%
macro30%10%10%50%
defensive667%475%683%1675%
total2976%2277%5580%10678%

Next: AILien’s learning.

AIIDE 2017 the learning bots

In March 2016 I analyzed which bots learned during the AIIDE 2015 tournament by looking at the data files. Here’s a similar analysis for AIIDE 2017.

I looked at the “write” directory for each bot, to see if it wrote files there, and if so, what the files looked like. Writing data doesn’t mean that the bot actually learned anything—it may not have used the data. Bots not listed in the table did not write anything interesting (maybe log files, nothing more). The table includes 15 bots of the 28 entrants, over half of them.

#botinfo
1ZZZKBotvaried info about each game, including tidbits like the time zone and the processor it was played on
2PurpleWavefor each opponent, a log of games with info including the sequence of strategies followed
5Microwavesame format as UAlbertaBot (Microwave has more strategies)
6CherryPiopening data for each opponent
9Tyrfor each opponent, seems to save info only about the previous game: win or loss, and a flag or two like "FLYERS" or "CANNONS"
11AILien10 values per opponent: scores for zerg unit types, a few odds and ends like macroHeavyness and supplyISawAir
12LetaBotone file which records a log of games, with opponent, race, map, and 3 numbers per game
14UAlbertaBota file for each opponent, giving for each strategy the count of wins and losses; learning was turned on this year
15Aiur91 values per opponent: strategy x map size
17Skyneta file for each opponent, with lines like "build_0_2 14 12"
19MegaBotmany files; the important ones seem to be "MegaBot-vs-[bot name].xml" which give scores for each bot MegaBot can imitate: Skynet, NUSBot, Xelnaga
20Xelnagaa file for each opponent with a single number: -1, 0, 2, or 3
21Overkillmany files with neural network data, reinforcement learning data, and opening learning data for each opponent (more than I thought!)
24Myscbotsame format as UAlbertaBot, but only 1 strategy was played for each opponent; nothing was learned
25HannesBredberg2 numbers per opponent, approximately (not exactly) the win and loss counts

LetaBot seems worth looking into, to see whether its log is learning data and, if so, how it is used. PurpleWave also recorded data essentially as a log, which could be used for a wide range of purposes. And AILien has a unique learning method that I should spend some time on.

UAlbertaBot had learning turned on this year. It has sometimes left learning off because its default strategies were dominant. It’s also notable that Ziabot skipped learning this year. In the past it has learned. Ziabot also finished last.

Next: What AIUR learned.

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.

opponent modeling, scouting, and software development

Opponent modeling is coming along, though never as fast as I would like. Steamhammer now records a pretty informative model. Making best use of the model is not as easy—it has a ton of uses. If it works as well as I hope, Steamhammer will gain scary predictive abilities, even against opponents with multiple strategies.

Opponent modeling depends on good scouting. The more Steamhammer finds out about what the opponent does, the better the model. So today I added a new scouting command, "go scout once around" which sends the worker scout on a single circuit of the enemy base and then returns it home. (Usually. There are some funny cases because the waypoint numbering is not quite clean.) In a lot of openings, I used it to replace "go scout location" which only finds the enemy base and doesn’t look to see what’s there. I’m thinking of also adding "go scout while safe".

The command is a minor addition, but while hooking up the wiring I saw awkwardness in the communication among ProductionManager which executes the commands, GameCommander which initiates scouting, and ScoutManager which does the work. I ended up spending the whole afternoon refactoring it for simplicity and testing to make sure I hadn’t broken anything.

Is that what I should be spending my time on? And yet it makes Steamhammer better.

Steamhammer opponent modeling goals

Next up for Steamhammer is opponent modeling. Some version of it will be ready for AIIDE, deadline 1 September. Opening learning methods that we have seen so far implicitly assume that the bot knows a small number of strategies and that one of them is the best counter for the opponent’s play. I want Steamhammer to be able to cope with opponents that are reactive (Iron), multi-strategy (Zia), or both (Krasi0). My goals for opponent modeling are something like this:

  • Play a wide range of strategies. It has been part of my plan from the first.
  • Learn from the events of the game, not only from the outcome. That way the bot can learn more and faster.
  • Learn from one trial: See an opponent’s strategy in the first game and counter it in the second, or at least make a good try. Fixed-strategy bots that Steamhammer knows a counter for should stand at a disadvantage from the second game on.
  • React by both choosing a counter opening and later choosing a counter unit mix in the middle game.
  • As more games are completed, recognize the range of the opponent’s play.
  • If no one opening strategy counters the full range of the opponent’s play, use game theory to estimate the best mix of openings.

It’s fancier than the strategy learning we’ve seen before, but it doesn’t seem hard to me. It’s straightforward, at least in principle. The key element is a model of game strategy. Already last night I wrote a GameRecord class that keeps a simplified description of what happens. That will be the basis for reading the opponent’s strategy over the game. As soon as we have one game record for an opponent, we can check our openings against it to predict which openings will succeed, and we can also use the record to make middle game decisions about what to produce.

Against opponents with a fixed play style, like XIMP, I expect this to be fast-acting poison. Steamhammer won’t react “Oh, carriers, I’d better make some scourge,” it will prepare ahead of time, “4 carriers will be coming in a minute or so, let me make the right amount of scourge to explode them all.” Against opponents that vary their play, it will take more games to formulate effective venom.

If I have time, I’ll do more. There is no chance that I can get all of these ideas implemented in time for AIIDE, but I’ll make what progress I can.

  • Generalize across opponents, so that an unknown opponent faces play that has proved strong before. If you play a lot of openings, then some of them are weak in most circumstances; so far I have accepted that.
  • Take more information into account, including map and starting positions.
  • Integrate scouting information with the opponent model to get the best possible prediction of what the opponent is aiming for this game.
  • Arrange the known opening lines into a tree, and use the integrated prediction to make decisions at every branch. Opening play will become reactive moment by moment, more like pro play.
  • Use the same mechanism to decide when to break out of the opening book.
  • Record the decisions made just after leaving the opening book as a new opening line to be added to the book and possibly played against other opponents. With breaking out plus adding opening lines, Steamhammer gains the ability to invent its own openings.

Steamhammer wants opening learning 3: lurkers

I rewrote Steamhammer’s lurker micro. Now lurkers fight well enough rely on. They don’t burrow and unburrow too often, unlike most bots’ lurkers. They do tend to be clumsy getting into position in a narrow space, and a side effect of not unburrowing too often is that they may cooperate poorly with other units, but one step at a time. I’m adding lurker support to the strategy boss, because it was past time.

So far I’ve written only 1 lurker opening. I’ll start slow. But as soon as there is a choice of what lair tech to aim for, Steamhammer is faced with the decision. Choosing randomly will lead to mistakes.

Against a terran that goes straight infantry, as many do, lurkers are a strong choice. Against factory units, lurkers are a poor choice (though they can be good for drops and surprise attacks). The majority of terran bots go one way or the other, though some can do either or both. If the opponent’s unit choice can be predicted, then Steamhammer can choose an optimized counter-build from the start. It won’t have to lose time scouting and reacting.

Against protoss, both lurkers and mutalisks counter zealots; any lair tech works. And dragoons are useful against both lurkers and mutalisks; zerg wants zerglings and hydralisks to combat dragoons. So the opponent model tells whether to hurry up and get a lair and how soon to take the second gas. It’s less valuable than against terran, but useful.

Against an opponent that doesn’t bring detection, lurkers are strong against any ground unit mix. The opponent model can remember that too.

Steamhammer wants opening learning 2: zealot rushes

Wuli’s zealot rush beats Steamhammer about 4 games out of 5. UAlbertaBot’s usually wins too. Carsten Nielsen’s wins fairly often, and Lukas Moravec sometimes plays a winning zealot rush. Steamhammer’s weakness is not the initial defense, which holds the zealots for quite a long time. It’s similar to games versus McRave; the weakness is in the transition to lair tech. Steamhammer ends up without enough drones.

Someday the strategy boss will be smart enough to understand the situation and make a safe transition. It will be easier if I improve Steamhammer’s defensive skills, which are simpleminded. But those things take time. In the short run, it’s easier to come up with an opening that beats zealot rushes. Then all Steamhammer needs is to know when to play that opening.

Bots should benefit hugely from opponent modeling, because other bots mostly play in stereotyped ways. Wuli and Carsten Nielsen never deviate from their rush builds. But Lukas Moravec, among others, plays more than 1 build. So to counter zealot rushes, a bot also wants plan recognition. If the scout sees 2 or more gates and a lack of other stuff (no gas, forge, or expansion), then the bot had better stay safe against a hard rush. If it sees a forge and cannons, it had better emphasize drones and hatcheries instead. Opponent modeling and plan recognition can be combined: Here are the plans the enemy has been seen to follow (described in some abstract way, such as the timings at which units appear). Based on scouting information, this past enemy behavior is the closest match, so let’s counter it. Since bots are predictable, simple opponent modeling is likely to give big leverage.

More about opponent modeling: If you know the opponent’s range of openings, then you can figure out how best to open yourself—what to do until you get scouting information and can adapt. Lukas Moravec never tries a fast rush, so you don’t need to stay safe against rushes. If the opponent never plays proxies (like most), then you don’t have to scout for proxies. If the opponent plays a mix of fast and slow openings, then you can try to estimate the best counter-mix with game theory. There are a ton of ways to gain advantage by knowing the opponent’s habits.