archive by month
Skip to content

close game Steamhammer-LetaBot

I don’t want to write up any games that might be from the SSCAIT elimination phase, since it wouldn’t be polite to scoop the official announcement. But there are some good ones. In particular, CherryPi is starting to show its full opponent modeling skills, which are more sophisticated than we could see in the round robin phase with only 2 games against each opponent.

So here is a close game from the round robin, Steamhammer versus LetaBot by Martin Rooijackers. Steamhammer randomly chose a 1-hatchery lurker rush, a risky opening which often beats LetaBot quickly. This time, Steamhammer got distracted chasing the scouting SCV and put on no pressure with its early zerglings, allowing LetaBot to bunker safely at the front of its natural instead of in its main. Often LetaBot overreacts to the threat of the early zerglings, which is actually slight, and leaves itself weak to the lurkers. Here it defended nicely.

marines and blood

Well, not quite nicely. A bunch of marines stood in front of the defenses and were slaughtered by lurkers outside detection range of the turret. But Steamhammer is no smarter. As soon as the way to the bunkers was clear, zerg became overaggressive with the lurkers and lost them quickly. One lurker stayed outside bunker range and drained terran minerals into repair for a while, but as soon as marine range research finished in the academy, it died too. Steamhammer had rushed to lurkers with a weak economy (see the worker counts in the picture), so after an even-ish combat outcome, terran was ahead.

Followup lurkers behaved the same, killing infantry that placed itself needlessly in danger, then placing themselves needlessly in danger and dying. LetaBot expanded much later than it should have, letting zerg catch up in economy. But Steamhammer had been frittering its army away while LetaBot continued to build up despite losses, so terran was still ahead.

Finally the terran push came. The 4 sunkens and small zerg force can only delay the inevitable; the natural will definitely be lost. Zerg has 4 bases and an adequate economy, so it can attack the terran ball from all sides, but chances to save the game seen small.

the natural about to come under siege

The rear-placed sunkens were highly effective, because LetaBot assaulted them without regard to losses. Marines funneled between the buildings, suffering both sunken hits and splash damage from tank fire. Scattered zerglings ran in from every direction, but SCVs kept the tanks repaired. Steamhammer kept sending more drones to mine the natural gas, while LetaBot had expanded and added to its SCV count, so zerg was falling behind in economy again.

A small number of terran reinforcements were intercepted by zerglings taking a strange path across the map. The terran attack started to become disorganized, with some units running into the zerg main before the natural was reduced. In the picture, the spread-out tanks are under attack from both ends, and in the minimap is an engagement between zerglings and reinforcing marines, preventing the marines from joining up promptly.

terran is disorganized

The zerg army remained tiny, but it was LetaBot’s turn to rush in pell-mell without organizing its forces. Without marines supporting the tanks, and with adrenal gland research finished to increase the zergling attack rate, the small number of zerglings finished them off easily.

With the slow-to-replace tank mass destroyed, Steamhammer had enough forces to stop any followup attacks. The turnabout was sudden. Zerglings broke into the terran natural while the double bunkers were unoccupied, and simultaneously 1 lurker and a handful of zerglings erased the terran third. There was a little more drama with a last-ditch battlecruiser as Steamhammer was slow to deliver the finishing blow, but in the end an unnecessarily massive zerg force battered down the undefended terran buildings.

It was another narrow comeback. Those make fun games.

Next: Analysis of solid versus daring bots.

Steamhammer opponent model status

The opponent model can (sometimes) select openings in the development Steamhammer. That’s the key feature I wanted, so I should be able to release Steamhammer 1.4 shortly after SSCAIT ends, as I hoped. After some thought, I found a simple way to avoid the big refactoring I had expected.

It’s rather nice to see Steamhammer lose the first game, or sometimes the first several games, then realize what’s going on and abruptly counter the opponent’s play. It tries to predict the opponent’s plan. So far the prediction is made in an extremely simpleminded way, but I may improve it before release. Of course it is easier to predict an opponent that plays a fixed strategy than a learning bot, so I expect the skill to be less effective against the top bots. Predicting the opponent’s plan lets Steamhammer choose more extreme openings that are risky against an unknown opponent. Today I added a ZvZ_12Hatch opening, which is unsafe against many bots but is a strong counter to the favorite strategies of AILien and a few other zergs.

I expect to remove most of the enemy-specific strategies that are configured for various opponents. There are way too many of them. Steamhammer will finally have the ability to adjust its play when an opponent starts playing differently. Some enemy-specific configuration will remain for this version because the opponent model is not smart enough yet. I think I will let Steamhammer learn from scratch rather than feeding it artificial “learned” data to replace the manual configuration. That will depress its results against the rushbots until it learns about them, but it will be a good test of how well the opponent model works. Steamhammer will be smarter, but at first it will look stupider.

As always, my list of bugs to fix and improvements to make is longer than I can possibly work through. CherryPi has a whole team that still can’t fix all its bugs. I should be able to make some progress before the end of the tournament, though.

One of Brood War’s permanent irritations is that updates to the game engine tend to break saved replays from previous versions. There is no backward compatibility. I have decided to add a version number to Steamhammer’s saved opponent data files. I hope that I’ll be able to release future versions with different file formats that can still make use of the learning data from older versions. It would be no fun to have to relearn the opponents from scratch because an improved version came out.

aside

Speaking of SSCAIT, mixed in the usual random game list since the round robin phase ended are games that look like they’re from the round of 16. It worked the same last year. I watched some of these games from the replay page, but Steamhammer’s apparent round of 16 games are omitted from the page, no doubt due to some glitch. I save all of Steamhammer’s replays. Tournament replays are especially interesting, so it will be a shame if some of them aren’t available.

Steamhammer 1.4 plans

My hope for Steamhammer 1.4 is to release it shortly after SSCAIT ends. Whether it is then or later, 1.4 will come out when it is “done enough.” I have finally decided what “done enough” means.

The headline feature is of course the opponent model. I turned off the extensive game record for this version, and kept only the brief summary of each game against each opponent, largely a list of numbers like when air units came out. The game summaries are already so rich with information that it’s difficult to exploit it all.

The opponent model will initially support 3 features: 1. The plan recognizer to figure out what the enemy is doing. This is in the tournament version and is being improved. 2. Specific strategy reactions to recorded information about this opponent. 3. The largest addition over the tournament version will be opening selection based on the opponent model. My first cut at this will be simple, but it will be able to hard-counter some opponents in the second game played while still choosing openings randomly. The opponent model will allow Steamhammer to play specialized openings that aren’t safe against the average opponent.

Choosing openings according to the opponent model is turning out harder than I expected. The decisions are not hard; I wrote a very simple first try. Refactoring so that the right information is available at the right time for each decision, that’s the tricky part. The codebase remains on a different model. I’ll have to take care to avoid bugs.

I don’t want to ignore terran and protoss. They have been falling behind in skills. Well, they’re going to keep falling behind, but more slowly. All the opponent model features are supported to some extent by every race. I have added new strategy adaptations and emergency reactions so that terran and protoss should be a little less fragile. Today I am trying to get shield batteries working and fix protoss emergency zealot production.

As usual, bug fixes are making a big contribution. The Steamhammer development version is noticeably stronger than the tournament version, to my eyes. The BuildingManager fix helps all races with macro, and the chooseTechTarget() fix (briefly mentioned here) helps zerg avoid strategy blunders.

Steamhammer 1.4 will be the first of the 1.4.x series. I want to make more progress before I move on to the next major feature.

epic game Steamhammer-WOPR

Today’s game is Steamhammer vs. WOPR by Soeren Klett on Andromeda. The game is not only epically long, it is epically clumsy on both sides.

WOPR plays a mech strategy that is slow with air defense. When Steamhammer opens with mutalisks, it flicks WOPR aside. This game, Steamhammer randomly chose to open with a low-economy lurker rush, and momentum went the opposite direction. WOPR had just enough scans to stop the initial lurker attack (a cleverer or more persistent attack might have succeeded). Then zerg strangely decided that its next tech choice should be hydralisks. Steamhammer had seen enough tanks that it should have known it was making a bad decision, so I think the error was not in analyzing the unit mix, but in choosing the tech target based on the analysis. Yesterday I rewrote StrategyBossZerg::chooseTechTarget() in a way that fixes 2 subtle bugs and improves the intended behavior, and I hope that will solve the problem. Anyway, WOPR eventually went on campaign and wiped out the zerg natural and main and the in-base mineral only, while cementing its huge lead by expanding across half the map. All that was left was to clean up a few underpopulated zerg expansions.

In the picture, the zerg economy remains weak and the army is hopelessly outmatched because it has suffered continual losses due to poor unit mix and poor tactics. In a human game, zerg could give up. 2 new terran expansions are visible on the minimap. Also notice that there are 3 evo chambers even though Steamhammer only knows how to upgrade with 2—it’s another bug that I fixed recently.

Steamhammer is about to get crushed

Like many old-school bots, WOPR believes it is finished when it has destroyed the enemy main. It does not seek out remaining bases, but sends its army toward (0,0) in the upper left. But WOPR does move its army to react to enemy units that it sees, and it continues to take bases and defend itself. Terran was maxed and zerg was extremely weak, with 8 surviving drones and the only tech a spire built at the last minute before the hive was destroyed. In the minimap, red is everywhere and blue survives in a corner. (Light blue on the minimap represents neutral buildings.)

Steamhammer has been crushed

Steamhammer knows how to recover. It slowly rebuilt its economy and tech and made mostly air units, especially scourge since it had seen battlecruisers. When the few zerg units showed themselves, an overwhelmingly strong column of terran units appeared and chased them away. Even constrained to ignore the zerg bases, terran had a big advantage. If zerg built up fully and also maxed its army, terran still had more bases and had been banking resources for a long time. WOPR only had to hold firm until the time limit was reached, and it would be awarded victory on points.

terran chases zerg off

After the above picture, the battle started to get interesting. The scourge scored more victories than losses against the battlecruisers and cloaked wraiths. The first ultralisks were annihilated by tanks with +3 attack, but zerg was starting to do some damage. Of course WOPR had a huge reserve of minerals and gas, and it instantly replaced lost units. But then zerglings and one ultralisk found their way into the terran 9 o’clock base and destroyed it before it had quite mined out. Terran forces had been distracted by air activity at the center base, and most of the terran army remained guarding the unimportant upper left. It looked as though zerg had a chance after all; WOPR’s tactical reactions were not accurate enough. Notice the zerg supply continuing to increase. WOPR replaced many of its lost workers with combat units, which was correct play because terran had a big bank and didn’t need the income.

zerg destroys a terran base

Steamhammer attacked the upper left natural and didn’t quite finish it off. Then the ray of hope started to dim. Steamhammer made too many guardians and was in danger of losing them all to the battlecruisers. Then hope brightened: WOPR played poorly and Steamhammer had the sense to follow up with hydralisks and scourge, and the battlecruisers exploded. WOPR targeted hydralisks though battlecruisers can one-shot the more dangerous scourge. Zerg destroyed the upper left natural. Then hope dimmed again: Steamhammer decided that the next target had to be, not an undefended base, but the upper left main where the giant terran army was hanging out. In the picture, the zerg army could not even get close before it was forced back by terran reinforcements appearing to its rear. Still, notice that zerg has retaken its main and mineral only and is now taking the formerly terran upper left natural. WOPR retook 9 o’clock and finished mining it out, but was otherwise not as economically active as it should have been. The zerg economy was in high gear and zerg was ahead in minerals but hurting for gas.

zerg chooses a hard nut to crack

Both sides showed messy tactics and micro. Steamhammer made a few devourers for help against the battlecruisers and wraiths, but used them poorly. Zerg lost workers after taking the indefensible center base (which it had earlier kicked out of terran hands). Steamhammer also made many indecisive movements and often chose the wrong targets. WOPR played worse, though. It kept the goliaths in the corner surrounded by siege tanks, so the massive air defense was out of range of attacking guardians. Guardians slowly ate through the rows of tanks and finally killed the command center. Hydralisks, despite some confused behavior, kept terran reinforcements at bay though they couldn’t approach the mass of tanks.

WOPR plays worse and zerg starts to win

Having killed the upper left command center, Steamhammer felt no need to engage the remaining terran army but set about the task of clearing the other terran bases. At least that part worked well. WOPR was less efficient and burned through its entire bank of minerals. Steamhammer won without ever maxing its army.

It was 46 minutes and full of events, many of which I didn’t mention. As one example, you can see on the minimap in the last couple pictures that Steamhammer built 2 macro hatcheries in a strange position outside its original base, where WOPR had taken the natural. I’m not sure what caused that.

By the way, this is the kind of game where either player would have benefited from being able to take island bases. If Steamhammer knew how to take islands, it could have made 2 more bases and would have recovered faster. If WOPR took islands and turreted them up, they could have been defended indefinitely by air units. To destroy a strongly defended island base you need a coordinated large-scale attack which no bot has the skills to pull off.

luck

In AIIDE this year, Steamhammer scored much lower in the first 5 round robins (55%) than over the entire tournament (64% over 110 rounds). The difference amounts to finishing #10 instead of #13. It could be due to other bots getting confused by Steamhammer’s random openings and mislearning, but I think it is more likely to be statistical noise. Steamhammer happened to be unlucky early on, and the bad luck washed away over the long tournament. Data is cleansing.

SSCAIT has only 2 round robins. The 5 rounds of Steamhammer’s bad luck comprised 135 games compared to the 154 games each bot plays in the 2 rounds of SSCAIT, similar numbers. Some bots will be lucky and place a little higher than they would have in a very long tournament, and some bots will be unlucky. SSCAIT has a different purpose than AIIDE, so I think that’s OK. But it is a point to remember.

It’s difficult to judge by intuition whether a bot is getting lucky or unlucky. The majority of Steamhammer’s losses so far are “unlucky” losses against opponents that Steamhammer usually defeats. That is exactly what we should expect. The bots in the highest places (Steamhammer is currently #6 out of 78) don’t have many opportunities to lose to stronger opponents. Look at the crosstable and you’ll see that all the top bots have the majority of their losses on the right-hand side, against weaker opponents. No player is perfectly solid, we all lose occasionally due to our own mistakes; it’s a hard game.

That said, I have a clear idea of which games I see as unlucky results. For example, Steamhammer lost 0-2 to Flash this tournament, while in my test at home, Steamhammer beat Flash at a ratio of 4:1. In its losses, Steamhammer happens to randomly choose openings that don’t work against this opponent, or gets into less common situations where weaknesses pop up. Steamhammer’s first game against Flash is its worst game of the tournament so far; Steamhammer barely seems to be in the game at all, but simply falls down when poked. I should repeat that an unlucky result against one opponent doesn’t mean that Steamhammer’s overall result is unlucky. With 2 games against each opponent, lucky and unlucky results against given opponents are virtually inevitable. And it’s hard to judge by intuition whether the good and bad luck balance out.

Steamhammer does have one clear lucky win, Steamhammer > Microwave. Microwave learned that 5 pool on average beats Steamhammer’s ZvZ opening mix, and played it this game too. Steamhammer got lucky and randomly chose 9 pool speed, which counters 5 pool, and won after a long game. Steamhammer maintained its lead the whole game, but Microwave defended stubbornly and had to be ground down (it’s a good game if you like that kind of thing). How will the second Steamhammer-Microwave game go? I can’t predict! Microwave will have an edge if it keeps its opening, but after losing it may switch.

For the rest of the tournament, I predict 2 losses to Iron and 1 more loss to McRave. TyrProtoss is also likely to take its game, and if CherryPi adapts against Steamhammer in the same way it has adapted against other zergs that defeated it, then CherryPi will have an edge in its remaining game—and those are all the likely losses. If Steamhammer wins any of those 5 games, they will be lucky wins. I can’t predict the Microwave or Tscmoo games. Any other losses will be unlucky losses. It seems plain that the majority of Steamhammer’s losses for the rest of the tournament will be unlucky losses, losses against opponents that Steamhammer usually beats, and that is how it should be. Frequent unlikely chances outweigh scarce likely chances.

Next: An epic game.

Steamhammer reserved resource bug

Today I fixed the Steamhammer bug that sometimes had the building manager reserve minerals and gas and not release them. When I put in debugging code, the error turned out to be much more common than I suspected. In the first test game after fixing the bug, late game macro went from sometimes spotty to smooth and strong, and Steamhammer scored a win over McRave from a situation where it never had before. The bug deserved earlier attention than it got.

The error was that one caller of undoBuildings() skipped out on its responsibility to release resources when buildings were canceled or unable to be started. (undoBuildings() is a Steamhammer addition, so the bug does not affect UAlbertaBot or early Steamhammer forks, at least not in the same form.) I fixed it by moving the responsibility of releasing resources from the caller into undoBuildings() itself, adding these lines:

		// If the building is not yet under construction, release its resources.
		if (b.status == BuildingStatus::Unassigned || b.status == BuildingStatus::Assigned)
		{
			_reservedMinerals -= b.type.mineralPrice();
			_reservedGas -= b.type.gasPrice();
		}

Of course those callers that release resources have to be changed so they don’t. Not all of them are supposed to—sometimes the building is under construction and the resources are already released.

Don’t trust this bug fix too much. There could be more bugs. I left the debugging code in for now, so if there are more building resource issues, or mistakes in the fix, then I should find them before the 1.4 release.

Steamhammer is getting auto gas stealing

I’ve just finished implementing AutoGasSteal, which means that Steamhammer can make its own decisions about when to steal gas. I also added random gas stealing, just because it’s easy. As I wrote in November, my main purpose is to stop the gas steal code from bitrotting like it did before; regular gas stealing in regular games will be my test suite. I doubt that stealing gas will make much difference in Steamhammer’s strength, but it’s easy to imagine that a few opponents may find themselves confounded.

There are now 4 ways to order a gas steal:

  1. Write it into the build order with "go steal gas". If Steamhammer hasn’t started scouting yet, this also sends out the worker scout to find the enemy base.
  2. Write your own C++ code to order a gas steal (it amounts to setting a flag). For example, StrategyManager could decide based on scouting information.
  3. New: Configure a value for RandomGasStealRate, a probability from 0 to 1. Steamhammer will randomly choose to steal gas in that proportion of games.
  4. New: Configure AutoGasSteal as true. Steamhammer will decide based on the opponent model whether to steal gas.

If any of these methods orders a gas steal, Steamhammer will go steal the opponent’s gas if it can. For now, the scouting worker that will steal gas heads out at the usual time (whenever "go scout" or one of its variations comes up in the build order). I added some data so that later Steamhammer can decide based on experience when to send the gas steal worker. I would like the bot to decide gas steal timing and scout timing on its own. I’m not sure whether I’ll get to that in this version.

For the first pass, I implemented auto gas stealing in a simple way, with the UCB1 algorithm that many bots use for opening learning. It keeps data for each opponent to remember how often it has tried stealing gas and how often it has won with and without, and chooses one or the other based on the UCB values. A small amount of fictitious data whispers the lie in its ear that it has tried stealing gas a couple times and lost. That makes it reluctant to pay the cost of stealing gas unless it sees a benefit. In a later version I may switch it to a contextual bandit algorithm so it can take more information into account—the opponent model has plenty more information available.

It works for all races. Just turn it on in the configuration file. I did find one new bug in the gas steal, a rare bug when emergency reactions interfere with starting the refinery building, but that will be fixed before long.

Now I need to go run a lot of tests!

Steamhammer scouting bugs

When I rewrote Steamhammer’s scouting, I tested it widely and thoroughly. I’m disappointed to find out that it still hides bugs, though the bugs only show up in a few games here and there. I’ve seen a couple games where the scouting overlord stops in an empty base, when it should continue to seek the enemy. Plus there is one other bug.

Watch the game Steamhammer-Bryan Weber on the 4 player map Roadrunner, which shows a hard-to-notice but potentially fatal scouting blunder. Bryan Weber played 5 pool while Steamhammer made a hatchery first, which could lead to a build order loss. Bryan Weber’s overlord scouted the correct base first, while Steamhammer’s went in the wrong direction. The situation is bad so far, but Steamhammer’s scouting drone has a chance to make up for it. Watch it leave the base at 2:10 in the game, quickly spot the enemy overlord, and immediately turn around and go home. It looks like the smart inference paid off! The top base was known empty, and Steamhammer calculated that only 1 of the remaining bases was a possible origin for the overlord. The enemy base has been located, so the scout drone doesn’t need to look for it but can get back to mining.

The scout overlord of course wants to see what is in the enemy base. Check its path: At the same moment that the drone turned around, the overlord changed direction from the actual enemy base toward the empty bottom base. Steamhammer drew the wrong conclusion from the enemy overlord’s position. Somehow the “can this overlord have gotten from A to B by this time?” calculation ruled out the actual base A that the overlord came from to reach point B. A minute later, Steamhammer’s zerglings also aimed for the empty base, leaving its own base defenseless.

While this nonsense was going on, Bryan Weber skipped out on a chance to win over breakfast, then a chance to win over lunch, then a chance to win over dinner. It wasn’t hungry at all and chose to wait around instead. Steamhammer won the game despite a build order disadvantage and a scouting bug because the opponent played worse.

Why are there so many bugs? The overlord position inference worked (or correctly drew no inference) in every test I threw at it.

entertaining Steamhammer game

MadMix protoss lived up to its name and went to 4 bases before its first gateway against Steamhammer. You can’t beat Steamhammer that way, but you may make a very entertaining game!

Actually it would have been a short and boring game if Steamhammer opened with one of its mass zergling openings, as it often does. But Steamhammer went macro style, with 12 hatch 13 pool (squeezing out extra drones early) aiming for hydralisks. Also Steamhammer didn’t understand that the enemy had gone overboard and should be drowned for it (it’s an easy adaptation to code but not needed because Steamhammer wins those games anyway). Zerg thought, logically enough, “That’s a lot of bases, I’d better make a ton of drones to keep up!”

Steamhammer wants more drones

Steamhammer still made enough units to keep the pressure on and repeatedly kill probes. It didn’t take many; 4 bases before gateway is amazingly reckless. Despite concentrating on drone production, zerg was never in trouble.

protoss barely has an army

Finally Steamhammer reached its goal of 75 drones and army production turned on full. The fury of the swarm swept away the fragments of the protoss bases.

fury of the swarm

Against TyrProtoss, Steamhammer played the same hydralisk opening. Despite playing on its worst map and messing up its macro, Steamhammer had (as I expected) an almost adequate army and held a chance to stop the big timing attack. Instead (as I also expected) it decided to move its forces through the enemy army without fighting and lose everything for nothing—the weakness is related to paths on the map. Steamhammer still makes many fatal blunders in big army fights.

Against ZurZurZur, Steamhammer got in trouble but managed to slowly claw ahead with better decisions. This could be an instructive ZvZ to study, with mistakes all around and all through.

new Steamhammer versus old Iron

I ran a 15 game match of this year’s tournament Steamhammer versus last year’s tournament Iron. This is a true year-over-year comparison. By comparing the 2017 SSCAIT Steamhammer versus the 2016 SSCAIT Iron, we can get a measure of how much Steamhammer has improved.

Over the course of 2017, Iron has consistently stayed well ahead of Steamhammer, fixing weaknesses before Steamhammer was able attack them properly. I think Iron has even pulled ahead a little, which is impressive because Iron is stronger and therefore harder to improve. Last year’s Iron versus last year’s Steamhammer goes to Iron with a few losses, this year’s Iron versus this year’s Steamhammer goes to Iron with maybe a couple losses, maybe none.

It turns out that Steamhammer has more than caught up with last year’s Iron. The score was 10-5, new Steamhammer over old Iron. Steamhammer won both with mutalisks (which Iron at the time thought it was ready for) and with lurkers (which old Iron was not prepared for). 2016 Iron was already tough, so it’s a good result. The result table here is from Steamhammer’s point of view.

mapopeningw/lnotes
BenzeneOverhatchLateGas0A mass zergling opening with slow tech. It can’t touch Iron’s vulture build.
Destination4PoolSoft0Iron has zergling rushes down cold.
Heartbreak Ridge9PoolSpeed1Steamhammer struggled with the map, as it often does, but eventually won with lurkers.
Moon GlaiveZvT_2HatchMuta1Smash.
Tau CrossOverhatchLateGas0Bad luck for this inappropriate opening to come up a second time. Steamhammer plays it in 1% of games.
AndromedaZvT_3HatchMutaExpo1A bit of a struggle, but the mutalisks prevailed.
Circuit Breaker9PoolSpeed1Iron defended too cautiously, pulling excess SCVs, letting Steamhammer draw ahead in macro. Zerg won with lurkers.
Electric CircuitZvT_3HatchMutaExpo1Smash.
Empire of the SunOverpoolSpeed0Steamhammer got lurker tech but decided to make no lurkers. It went hydra and lost after the tank numbers built up.
Fighting SpiritZvT_3HatchMutaExpo1Smash.
Icarus2HatchLurker1Iron was not ready. Spider mines and wraiths eventually cleared the lurkers, but the command center was already lost. Mutalisks finished the game.
JadeZvT_3HatchMutaExpo1The most hard-fought game of the match. Iron held on with good turret repair and its vulture containment, but Steamhammer slid drones past the contain to take the map and eventually win with hive tech.
La ManchaZvT_2HatchMuta1Another hard fight going to hive tech.
Python3HatchLurker1Smash.
RoadrunnerZvT_13Pool0One of Steamhammer’s classic openings, but objectively weaker. The initial pressure looked good but was never enough to tip Iron down the slope.

The games were varied, especially considering that Iron plays a single strategy, and both sides got to show their strengths. Some were one-sided smashes for one player or the other.

Steamhammer was able to prevent too much damage from Iron’s standard 3 vulture runby with zerglings. Iron’s vulture micro at the time was not slick enough to keep the vultures safe from speedlings in the zerg base. Or to say it differently, Steamhammer’s micro has improved enough to nab the vultures when they get stuck on a building or take a wrong turn.

In a number of games, Steamhammer won by getting ahead of Iron in workers. When mutalisks fly in, Iron has turrets ready and works hard to keep them repaired. Steamhammer likes to pick off the repairing SCVs. It’s costly in mutalisks and reduces the immediate pressure, but zerg gets ahead in macro and can win later in the game.

Iron’s forward turrets protecting its vulture contain sometimes confused mutalisks and distracted them from what they should be doing, but I don’t think the effect was decisive in any game. Loss of drones that tried to exit through the vulture contain was a problem, though.

It was striking that old Iron did not scout the map in the middle game. If Steamhammer squeezed a drone past the vulture containment and the game continued long enough, zerg could set up additional bases that remained safe throughout. It happened in both of the hive tech games.

What do the last 2 days’ results mean? In the SSCAIT 2016 round robin phase, Steamhammer 0.2 finished tied for places 16-17, and the latest Steamhammer is stronger than its much-improved successor Steamhammer 1.0. Iron finished 5th with 89% win rate, and the latest Steamhammer is stronger. Steamhammer improved tremendously in 2017, so much that it would have likely have finished in the top 4 last year if we sent it backward in time. Where did I leave that time machine?

We’ll see how it does this year. The tide has risen all around. Still, 2018 had better watch out!

new Steamhammer versus old Steamhammer

To commemorate a year’s progress, I ran a 15 game match of the latest tournament version Steamhammer 1.4a3 versus Steamhammer 1.0, which was released on 28 January 2017. A true year-over-year comparison would have been this year’s tournament Steamhammer versus last year’s tournament version, Steamhammer 0.2, but that would have been a boring blowout. Steamhammer 1.0 is good enough to put up a fight.

The score was 11-4 in favor of the latest Steamhammer. The results below are from the point of view of the new version.

mapw/lreason for loss
Benzene0abdicating the fight for air superiority
Destination1
Heartbreak Ridge1
Moon Glaive1
Tau Cross1
Andromeda0gross tactical blunders
Circuit Breaker0build order
Electric Circuit1
Empire of the Sun1
Fighting Spirit1
Icarus1
Jade1
La Mancha0stupid drone chasing
Python1
Roadrunner1

It’s interesting that there is only one build order loss. In a couple other games where new Steamhammer randomly chose an inferior build order, it managed to hold on and turn the game around with stronger play. New Steamhammer has a wider range of better openings and was more likely to get a build order win. Also interesting is that every loss was for a different reason. It means there is no One Big Flaw to fix. I have handled the worst of them; the rest is a rabble of many lesser weaknesses. It is a sign of growing maturity.

Tomorrow: A different year-over-year match.

Steamhammer 1.4a3 with last-minute bug fixes

New tournament version with last-minute fixes! I set to work on version 1.4 for release around the end of the tournament, and thought, “Well, what could be a better start than a bug fix?” I ended up fixing 2 bugs that could help in the tournament and have low risk of causing other problems. I changed gears, and now 1.4a3 is uploaded.

• Why does Steamhammer sometimes make a hydralisk den versus zerg, when it is explicitly coded to never make hydralisks in ZvZ? Once I realized that it only happens when the opponent went random and rolled zerg, I was able to trace the bug. It turned out that the actions are out of order: Steamhammer first makes its strategy decisions, then checks to see if the enemy race is still Unknown. So in the strategy boss’s first production round after it finds out the enemy’s race (which is usually the first round out of book), it thinks it still doesn’t know. “Hmm, better be ready for anything.” Facepalm!

• In a test game after fixing the above bug, Steamhammer skipped lair tech (in ZvZ!) and rushed to hive for ultralisks. I’ve seen that misbehavior before. It hurts badly when it happens, and the fix turned out to be trivial. A condition was miswritten with && instead of ||.

There must be dozens of play bugs that are hard to trace or even to notice because their effects are rare or subtle....

Steamhammer 1.4a2, tournament version

I’ve uploaded the SSCAIT 2017 tournament version of Steamhammer, which is version 1.4a2. The reason for the strange version number: I promised that release version 1.4 would have a full-up opponent model, and even if I break every other promise, I can keep that one!

Randomhammer is disabled for the tournament (it took me a minute to figure out how to create an empty zip file, since zip refuses to do it directly...). For Steamhammer, I found a number of low-cost low-risk improvements that are enough to make a difference. The new version already has a win against McRave, for the first time in a long time, so I can’t have messed up too much.

• Fixed a crashing bug in assigning units to the Recon squad after losses in the main army require the Recon squad to be downsized. The bug only affected terran, and was related to medics.

• Ranged units target arbiters with a higher priority. By the way, in the strange world according to Steamhammer, scourge are ranged units.

• Fixed a bug in recognizing worker rushes. On one map, enemy workers busy mining could be recognized as “rushing”. Still a strange world.

• A few changes to zerg to be more careful in collecting gas. Turning gas off because there’s a gas excess compared to minerals now happens using the current minerals, not current minerals minus minerals reserved for buildings, so it is more conservative. If there are idle drones, always turn gas on (might as well put them to work). If short of gas, Steamhammer adds extractors faster—it always has, but now it is a little more eager.

• Account for gas usage in planned spending. Together with the gas changes above, this improves the midgame unit mix and army size. Watchers may not notice, but to me the difference is visible.

• Be more thorough in using all larvas. Steamhammer has long had a fallback rule to make zerglings with any spare larvas. I loosened the conditions, and had it also make drones if the army is “big enough” according to an arbitrary criterion. The rule usually fires later in the game, when there are plenty of hatcheries and Steamhammer wants to make more gas units than it has gas for. The result is a bigger army, and again, I can see the difference.

• Tweaks to make guardians less often. I think Steamhammer still makes guardians too often, but it should be closer to right.

Randomhammer crash!

Randomhammer crashed in a game! It’s the first crash since May, except for the untraceable crash. Now I know exactly what to do next.

The crash was in an interesting TvP game against cannon-bot Jakub Trancik. It’s a shame not to see how it continued. Randomhammer went with marines and Jakub Trancik decided to build its proxy cannons below the ramp, so Randomhammer (seeing the pylon with its scout) didn’t pull SCVs but waited for combat units. The first marines were not quite able to stop the cannons, and retreated out of sight up the ramp. Terran and protoss have very few strategic reactions (unlike zerg), but there is one when the bot is making infantry and meets dragoons, tanks, lurkers, or static defense: It adds tanks. It was the correct move; the unit control is clumsy and loses stuff unnecessarily, but the tanks were sieging down cannons and I was starting to think that the play was good enough.

Then CRASH! The crash is in CombatCommander::updateReconSquad() which decides what units are in the Recon squad. It is complicated for terran because there may be medics in the squad, depending on size and composition, so it’s no big surprise that there’s a bug.

To work!

Steamhammer tournament plans

Today I went for a walk in the snowy woods, setting the first footprints on some of the paths. It’s kind of how I feel about working on Steamhammer: At some points I may leave the first prints, but the path was already there. And I feel as though I still have a lot of paths to walk before I can reach wilderness.

For the tournament, I have changed my plans. Trying to hit a deadline makes my code worse and messes with my motivation. Some people thrive on deadlines; I go wrong. So instead of turning on the next opponent model feature as I had been thinking, I’ll do whatever smaller fixes and features seem likely to help. I’ll have to comment out some stuff I’ve just done, but whatever. The tournament version will be 1.4a2 (anyone who’s not confused by the version numbers needs to get back to their real work), and hopefully not long after I’ll put out release version 1.4 with opening selection controlled by the opponent model. I expect that later 1.4.x releases will add more opponent model features, because the opponent model can do many things.

The switch is partly inspired by watching Steamhammer’s 2 recent losses to XIMP by Tomas Vajda. The loss on Heartbreak Ridge I can swallow, because Steamhammer has always played particularly poorly on that map. The loss on Icarus seems to show that Steamhammer is playing worse against the carriers (though it was a long, hard-fought game and many carriers blew up). Apparently delaying the hive, though often good, in this case left Steamhammer unable to batter down bases with ultralisks, because it used all its gas making scourge. Looking closely, I see that it was short of gas in part because it turned off gas for a full minute before seeing the carriers, seemingly due to a bug. Outside of emergencies, zerg should turn off gas only if gas piles up to at least 3 times the mineral count, and here it turned off gas with 1700 minerals and 300 gas. That should be possible only if the building manager reserved a huge pile of minerals to make buildings, and didn’t reserve any gas... it seems barely possible. This is something that can be fixed by a deadline.

Steamhammer 1.4a1 - the rest of the changes

Yesterday I wrote up Steamhammer’s scouting improvements. Today is the rest of the change list.

stuff affecting squads and combat

• Added a parameter setting to each squad: For combat simulation, include either all known enemy units in the area (including remembered units that are out of sight), or include only visible enemy units plus known static defense. For most squads, it is useful to include all known enemy units, so that you don’t attack a superior force when you momentarily can’t see all of it. It’s especially useful for a squad of zerglings, which have a short sight range. The Recon squad is set to fear visible units only, because its purpose is to see what is there.

• The combat simulation radius is also a parameter setting for each squad. The primary combat squads have a large radius so they can try to understand the ultimate outcome of a battle (though it’s questionable whether this is a good idea). The Recon squad has a small radius, so that it does not fear distant units that it is out of range of. That lets it come closer and see more.

• If we can see the last known location of a remembered unit (its lastPosition) and the unit is not there, we flag it as goneFromLastPosition. We keep the lastPosition and know when it was set, so we can narrow down where the unit is now (which is not done yet, but someday). This is a feature from the when the enemy unit is out of sight post. Units which are goneFromLastPosition are skipped in combat simulation, which makes Steamhammer more aggressive. Of course the unit might still be nearby, so Steamhammer is taking a risk, but it seems better than fearing ghosts.

• Squads no longer single-mindedly attack the enemy main. The combat commander prefers to issue orders to attack the enemy base with the least static defense. It’s still not very smart, but the tactical play is much more interesting and varied. There are other tweaks.

• A bug in tactics calculation prevented the combat commander from issuing orders to a squad to attack enemy units, which it will do if there are no enemy bases the squad can attack. Fixed.

• Valkyries, corsairs, and devourers now play much better. Since they are unable to attack bases, the bug fix above to send them to attack units was critical. A new unit micro controller, MicroAirToAir, helps the flying squad make better use of these units. Steamhammer no longer sucks with devourers and protoss corsair openings are stronger.

• Ranged units like to hit air targets that have acid spores on them. Get full value from those devourers.

• Queens, defilers, and dark archons are recognized as combat units and assigned to squads. Steamhammer can’t use any of these spellcasters (yet), but there’s no reason to ignore them.

• Due to mis-nested conditions, if you asked a squad whether it had any air or ground units, flying detectors were counted as ground units. Fixed.

UnitUtil::GetWeapon() makes the simplifying assumption that a bunker has marines in it. It used to ignore bunkers. Someday Steamhammer will attempt to keep track of whether a bunker is loaded, but not yet.

InformationManager::getNearbyForce() includes enemy medics. They were mistakenly excluded by a weapons range check. It was a serious bug.

InformationManager::getNearbyForce() checks enemy attack ranges more accurately. It makes little difference in practice.

InformationManager::getNearbyForce() no longer returns detectors. It is used to feed the combat simulator, and FAP does not support detectors, so it was no help.

• In FAP, added N00byEdge’s September patches: A scarab is a suicide unit, medics heal less.

other

• Recognize some enemy opening plans. This is part of the opponent model. For now, only zerg takes advantage. The plans Steamhammer tries to recognize are named Proxy, WorkerRush, FastRush, HeavyRush, and SafeExpand (which means natural with cannons or bunker). It doesn’t always recognize them successfully, but when it does I find that it is rarely wrong.

• Added the macro act command "go nonadaptive" for zerg, affecting drone production. Zerg normally adjusts its drone production depending on the situation, even if it is playing a book opening. For example, if the book says “make zerglings” but the opponent has cannons, Steamhammer may decide to make a drone instead (it has even more freedom out of book, of course). The "go nonadaptive" command means to turn off this adjustment process for the entire game and trust that the opening book has the situation covered. The new command is used in openings which counter forge fast expand. This version doesn’t play any of those openings, though!

• Steamhammer’s event-driven base tracking system sometimes gets out of sync with reality. Past versions made corrections in some cases. Now all incorrect stored base information is corrected as soon as possible.

MapInfo::getNextExpansion() skips bases without enough resources to be worth it. It should fix occasional cases where Steamhammer re-expanded to a base that was mined out.

MacroAct::mineralPrice() and gasPrice() are now correct for upgrades beyond level 1. See upgrade prices in Steamhammer.

• The configuration file has new flags Config::IO::ReadOpponentModel and WriteOpponentModel. In this version the flags don’t do anything (the file I/O is turned off in code), but in the release version 1.4 they will work. Separate control of reading and writing the files is handy for debugging.

terran and protoss

• Terran and protoss turn off gas collection when there is too much gas, as zerg has always done. “Too much gas” is defined conservatively as 1. over 400 gas, and 2. over 4 times as much gas as minerals, and 3. more gas than is needed to fill all the orders in the production queue. I think the biggest benefit is that the bot can survive after many workers have been killed, when collecting gas causes mineral mining to slow or stop. Gas production starts again when the bot has a use for the gas.

• Adjusted the terran ratio of marines:medics to 5:1. It used to be 6:1, not enough medics. Some people prefer 4:1.

• Added a terran siege-expand opening. Steamhammer doesn’t adapt well enough to play the opening safely in many cases, but I think it should be adequate for TvT.

zerg

• In case of a recognized Proxy, WorkerRush, or FastRush enemy opening plan, possibly break out of the opening and react appropriately. If the opening is already doing something that looks sensible, let it. I did not follow IMP’s advice to not distinguish between plans with the same reaction, because I expect that the reaction will not always be the same (especially for forks).

• In case of a recognized HeavyRush, maybe build a sunken. It is a wimpy reaction, but may help sometimes. This Steamhammer version does not react to SafeExpand, but I hope that the tournament version will.

• Recognize corsairs, scouts, and carriers as countering guardians. What a blunder! That is why Steamhammer kept making guardians against XIMP.

• Recognize goliaths as countering zerglings. Properly, only enough goliaths counter zerglings; their long range makes them deadly in groups. Steamhammer is too primitive to understand that, so far.

• Also what-counters-what tweaks in ZvZ.

• In ZvT, prefer to get lurkers after mutalisks in the standard way. Steamhammer classically rushed to hive, crazy zerg style. I’m not sure whether this adjustment works as intended.

• Always get overlord speed before hive, except versus zerg. This is a concession to the BWAPI 4.1.2 bug that you can’t get overlord speed after hive.

• Prefer to make unit types that already have their upgrades. It’s not a strong preference, though.

• A new “do I have enough units to get away with this?” check to decide when it is safe to tech. For example, if the enemy has air and we don’t, delay less important spending.

• Added a bunch of openings to counter protoss forge expand. This version doesn’t play them, though. I hope the tournament version will.

• Added an overgas 9 pool opening for ZvZ, mostly because it’s fun but also because it’s a good opening.

• Added an OverpoolHydra opening similar to ZZZKBot’s 1 hatch hydra build that it used to defeat Iron in AIIDE 2017. It is a weak build, and I don’t know if it has any other use than to defeat that version of Iron.

• Lurkers can change targets away from a building. See fixing a lurker micro bug.

• Lurkers are little more eager to target a protoss observatory or robotics facility.

• At some point a bug crept in that made it impossible to research lurker aspect if you had a hive. Fixed.

• In ZvZ, get queen’s nest and hive only once you have at least 12 mutalisks. Steamhammer was losing games by teching at an insane rate.

• Other minor strategy changes to mildly encourage making more combat units.

• Never have more than 9 devourers at a time. See too many devourers.

• A new feature is supposed to prevent zerg from stopping gas collection if that will cause drones to go idle. If you have excess drones, you might as well collect gas whether you need it or not. Unfortunately, the feature doesn’t seem to be working, though I can’t find a bug.

code changes with no effect on play

• Renamed the MainAttack squad to Ground. MainAttack used to be a good name, because all the squad wanted to do was attack the enemy main, but now it has wider ambitions. The 3 combat squads are now named Ground, Flying, and Recon.

• The capitalization of MapGrid::getUnits() was nonstandard. Fixed.

• Removed the unused MapTools::_units instance variable.

• Renamed RegroupRadius in the config file, also known as Config::Micro::CombatRegroupRadius in the code, to CombatSimRadius, since that is what it is. A unit that can fire into this radius around the combat simulation center point is included in the combat simulation. Also it doesn’t hurt for the names to be consistent.

• Rewrote UnitData::updateUnit() to be slightly simpler.

• There was still a redundant mention of SparCraft in the VS settings. As far as I can see it caused no harm, but it’s gone now.

• Map-related data structures use short ints to push less other stuff out of cache. I cleaned up the typing of short ints so that the compiler understands that the type conversions are safe, though they were already.

• In FAP, I initialize the closestDist local variables in 3 routines, solely to avoid compiler warnings when warnings are set to a high level. The code was correct, but the compiler is not smart enough to tell.

• All the debug configuration “Draw...” options can be set manually during the game using /set. It can save time in debugging. I had added some options without adding corresponding commands.

• I renamed all the Micro::SmartX routines to Micro:X to reduce noise in the code text. It doesn’t need to brag. For example, Micro::SmartMove is now Micro::Move.