archive by month
Skip to content

Steamhammer bugfix version 1.1.1

Steamhammer 1.1.1 is uploaded with a fix to the infinite creep colony bug. I also added a new opening, because it had been nearly a whole day and I have to keep up my self-respect. The 9PoolHatch opening counters zergling rushers more sharply than 9PoolExpo. And I tweaked ZvT to make more drones, because it seemed a little lean.

The bug, by the way, was a hard-to-see typo in a part of the code that has no direct connection with making creep colonies. It took some finding.

I’ll update Steamhammer’s web page tomorrow.

about Steamhammer 1.1

stand by for bug fix version 1.1.1

Steamhammer 1.1 is losing game after game to the infinite creep colony bug. Apparently I ran the wrong test games to see it. There will be a 1.1.1 release to fix the bug.

version 1.1

Steamhammer 1.1 had a shorter development period (compared to the previous version) than 1.0 did, but it includes at least as many changes.

The primary goal of 1.1 is not to play better, it is to raise the ceiling on potential performance by playing more flexibly. Every time you add flexibility you run the risk of playing worse: The bot could misuse its flexibility. Flexibility makes more mistakes possible.

1.1 includes enough strength improvements that I think it probably does play better overall, or at least fixed version 1.1.1 should. Its rating went down at first, partly due to losses to Neo Edmund Zerg (which seems to be by the same author as Newbie Zerg, 5 Rax Newbie, and 5 Pool—what is it with all this coming and going? [it is partly explained in a comment]). Over time, I expect its rating to wobble over a range offset slightly upward from 1.0 (whose final elo of 2163 was above its average).

strategy boss

The headline feature of Steamhammer 1.1 is new macro code in the zerg strategy boss. It looks at the game situation and decides on a unit mix to produce now and a tech goal to work toward. It’s not very extensive; the possible tech goals are hydras, mutas, and ultralisks. Units allowed in unit mixes include those three plus zerglings. A unit mix is specific: It always includes one mineral-heavy unit and optionally includes one gas-heavy unit, and that’s all. The units are combined as resources allow. Some of the unit mixes, like hydra-ultra, are designed for rare circumstances, and I’ll be watching to see whether they ever appear in real games.

The strategy boss has a lot of tiny smartness improvements, mostly to avoid mistakes. Overlord timing is better. It copes more successfully with a wider range of emergencies (oops, lost the main? I’ll call this base my new main). When the enemy army looks big, it may build sunkens. There are no more random unused buildings (buildings may still go unused, but they are always made for a purpose). It no longer goes overboard on scourge (that was a bug). Few situations cause production freezes, though there are still some. It can upgrade in 2 evo chambers at once. It has fine control over the economic growth rate, though it doesn’t make much use of the ability.

All these improvements have surprisingly little effect on skill. They are weaknesses that need to be fixed, but not bottleneck weaknesses that lose many games.

tactical improvements

One big improvement is the worker harass defense as suggested by AIL. Steamhammer beats Stone. This defense skill helped 1.1 to a win over Bereaver in its first SSCAIT game, losing 0 drones where 1.0 often lost 2 or more.

Another big improvement is that Steamhammer is more cautious about the range of enemy units. It understands the range advantage given by a bunker, and assumes that enemy units have their range upgrades. Units less often suicide by dawdling under enemy fire. Steamhammer was able to play longer against Krasi0 before losing due to other weaknesses.

There are a bunch of other improvements, but they’re minor by comparison. Targeting is further improved. Zerglings and scourge are slightly less stupid. Stuff like that.

One improvement behind the scenes is used only by the strategy boss for a minor purpose, but should be valuable in the future: InformationManager tracks when its data about each enemy unit was last updated. You can tell if the data is fresh or stale.

the configuration file

You can specify repeated units in a build order: “4 x drone” gets you 4 drones. Or at the end of a rush build, “50 x zergling”. The openings become shorter and easier to read and edit. 1.1’s configuration file is less than 3/4 the size of 1.0’s, even though the bot plays more openings.

openings

The new strategy boss bring changes to openings. Since the bot can now adapt to the situation, a bunch of openings have been shortened to get out of the opening sooner and start adapting. A few openings have been lengthened because the boss was likely to do the wrong thing.

The opening selection changed. In earlier versions I rejected overpool openings for ZvP (even though they’re the top ZvP openings in human play) because they require adaptation to play well. Also the ZvZ overpool openings I tried were unconvincing. 1.1 plays 2 different ZvP overpool openings, and 3 ZvZ overpool openings plus an overgas opening.

Openings are still chosen randomly with weights. The number of openings per matchup:

matchup1.01.1
ZvT89
ZvP66
ZvZ711
ZvRandom34

I dropped the ZvP 3HatchHydra opening altogether. I wasn’t satisfied with the version in 0.2 and reworked it for 1.0, but I did a thoughtless job and the 1.0 version is worse. The bad opening is responsible for more than its share of losses. Now all ZvP openings stop at zerglings and let the strategy boss decide whether to go for hydras or mutas next. In my tests it makes mostly good choices.

I also fixed tuning mistakes in the ZvT 2HatchMuta opening; it hits harder. A number of the other openings are not quite optimized and could use tuning up.

Steamhammer 1.1

Steamhammer 1.1 is uploaded. Headline feature: New macro code which knows how to bring new tech online and transition to new unit mixes. “Traditionally” I draw no debug info on the screen in release versions, but for this version I tossed up a tiny bit of info in the upper left for spectators: While in the opening book, the name of the opening; after that, the current unit mix and next tech goal.

Details and stuff tomorrow. I need some rest.

I made strenuous efforts to eliminate all production freezes and critical bugs, but I failed. There turn out to be a tremendous number of ways for production to seize up, and I couldn’t eliminate all of them. There’s also a particularly amusing bug that causes the bot to send all its drones to its main base to build creep colonies. I’ve only seen that bug in 2 test games out of dozens, and it resists finding so far. It will be hilarious to see it happen on the server.

spiral development

When I started Steamhammer, one of my first additions was macro code so the bot could build units appropriate to the openings I had coded in.

Now I have tossed that macro code and completely replaced it. The new macro code is better in many ways: It reacts faster to changes, adapts to a wider range of circumstances, survives more emergencies, knows how to transition to a new unit mix and how to pick a new tech goal, and is generally more capable and less buggy.

And I am planning to toss it and replace it again. Its transitions feel mushy and imprecise, as if it weren’t quite sure what it was aiming for. It’s similar to other bots: It is making decisions based on low-level heuristics rather than on an understanding of the situation.

The central zerg macro skill is knowing when to make drones. Steamhammer 1.1 makes drones because it vaguely believes that more drones are better. Strong players don’t do that. A player at ICCup B level understands that the right drone count depends on what you want them for, and makes drones with specific purposes in mind. Making a purposeless drone means that your army is smaller and you are putting less pressure on the enemy; it is a mistake.

For another example, watch AILien tech up (Steamhammer 1.1’s play is not altogether different). When it has teched up to a new unit, you commonly see a few mutalisks or a few ultralisks join the army. If those are successful, more may join in. Pro games don’t go that way; at some point the observer will center on the zerg rally zone and you’ll see a dozen ultralisks as if out of nowhere. The pro is reacting to the future, not the past. No pro would spend on ultra tech to make a few and see how they turned out.

abstraction saves your bacon

So at some point I will rewrite the macro code again, to give it a deeper understanding of goals and constraints. How can I do it?

I keep seeing people complain, or point out by way of excuse, that the state space is too big for machine learning to work. I don’t accept it.

Take the zerg macro problem. You start out knowing part of the game state and part of its past: Where your units are, where enemy units were last seen, and all the other details. Your problem is to use this information to decide what to make next: a drone, a zergling, a hatchery, a creep colony....

If you frame the problem that way, the state space may truly be intractable. “I have a hydralisk with n hit points at position (x, y)” informs the decision, but not much! If you try to train a neural network (or whatever) with that input and output, you may struggle to make progress. And you absolutely will need a huge quantity of data that will be difficult to gather (even with OpenBW tools, which aim to make the data easier to gather).

You need an understanding of the situation, not a list of the details of the situation. I frame the problem as the interaction of goals and constraints. Goals are “I want this current unit mix,” “I want to aim for this set of upgrades,” “I want to add this tech.” Constraints are rooted in the game rules, “it takes x frames to make a y” and “my income is x minerals per second.” Constraints let you calculate or estimate useful information: “How long will it take me to make upgraded ultras?” and “How many ultras will I able to include in my unit mix by then?” and “How many if I expand first to get more gas?”

“What should my current unit mix be?” is a more tractable problem to solve than “what unit should I make next?” and yet in practice it answers the same question. It abstracts away the detail of “what next?” leaving only “what?” The detail of exactly which is next may not matter, and if it does, it can be solved as a separate problem. Also the unit mix question can be answered partly by constraints: Knowing your mineral and gas income and rate of larva production constrains what your unit mix can be, even when it doesn’t constrain what your next unit is. Constraints can be calculated; they should be included in or reflected in the input to the learning algorithm, not treated as something to learn. Don’t do everything bottom up; top down has its place.

Similarly for other goals like “what tech should I seek?”

Abstraction also helps on the input side. I want the input to be not “here is what is known of the game state” directly from InformationManager, but “here is the abstract tactical situation,” the tactical sitrep, maintained by a tactics boss which does not exist yet. I want the strategy boss to realize “it’s too dangerous to expand in this situation, and I have enough drones to support this unit mix, so no more drones for now.” Further in the future, I want it to be able to say “this enemy mineral line has a back door, so morph these nearby hydras to lurkers.” Or something with the same effect.

With an abstract output and a much smaller abstract input, the learning problem should be fully tractable. “The state space is too big” is a flag, and the flag flies over the territory of abstraction. Deep learning copes with large state spaces by learning abstraction using huge amounts of data; I don’t expect to be able to get huge amounts, so I must supply abstraction from outside.

spiral development

Steamhammer’s original macro code was needed so that the bot could play a complete game—otherwise I couldn’t make progress on the basic features. In 1.0 the basic features were finished enough, so next I replaced the macro code to add flexibility; the bot has greater ability to react and adapt. That flexibility is needed for the next step, a smarter tactical boss that keeps track of tactical goals and maintains a picture of the tactical situation. And that tactical picture is the foundation I need to throw away the macro code again and rebuild it.

I’m still concentrating on strategy first. But strategy needs tactical understanding; the levels are not independent.

AILien > XIMP

AILien scored a fine win over XIMP. The game plan to defeat Tomas Vajda’s carrier bot:

  1. Take 7 bases and grow a huge economy.
  2. Prevent XIMP from expanding beyond its natural.
  3. Don’t bother to fight the carriers, just accept losses. With enough units on the map, the carriers chose to destroy defenseless zerg units to protect protoss bases and did not visit a zerg base until too late.
  4. When hive tech is ready, adrenal glands for the zerglings and the ultralisk upgrades, go win. Zerg can send units faster than protoss can kill them.

Ha ha, so simple! AILien made hydralisks, but I didn’t see them fire a shot against carriers or interceptors. That would have been too easy! No, XIMP is not properly put in its place unless zerg handicaps itself with buggy hydras!

I’m too busy to pull images, but watch the replay at the link. Let’s see how long the replay stays online.

scourge

Why do so few zerg bots make scourge?

A pair of scourge costing 25/75 can kill a wraith or corsair costing 150/100, or even a valkyrie costing 250/125, such a favorable ratio that zerg can afford to lose some scourge along the way. If the enemy flees then the scourge may kill nothing, but to send the enemy running is a victory in itself. If you have a spire, then scourge is a cost-effective part of your air defense and reduces the need for hydralisks or spore colonies to protect overlords and mineral lines. Scourge moves fast, good for scouting, and scourge can intercept drops. Scourge is gas heavy, but zerg usually has more bases and therefore more gas. When facing air units, it is almost always a good idea to have some scourge.

Steamhammer is not skilled with scourge. It maneuvers poorly, failing to attack when it could or losing scourge unnecessarily. It attacks poorly, sending too many to one target and not enough to another. And it doesn’t know the first thing about coordinating scourge with other units. But its opponents are also weak at defending against scourge, and overall I believe that Steamhammer’s scourge is more than worth the cost. In tonight’s game versus Tyr by Simon Prins, Steamhammer scourged science vessels one after another, butting heads with the terran army and holding even—until finally one vessel lived long enough to get an irradiate off, creating an opening for terran to break out. Despite some losses, scourge more than earned its pay, and greater scourge skills might have saved the game.

When I play Steamhammer versions against each other, the fight often comes down to which side lands more scourge hits. Steamhammer does not know that in a muta-scourge versus muta-scourge battle, you commonly want to hit each enemy muta with 1 scourge to leave it too weak to fight on, rather than 2 scourge to kill it (wasting most of the explosive power of the second scourge). If I taught it that....

A zerg with razor-sharp scourge skills would be a terror of the air.

various Steamhammer developments

SSCAIT now recommends Steamhammer as a starter bot, as Krasi0 pointed out in a comment. Over time I expect this to add cooperation overhead and slow down my development, but also to end up improving my code quality. For now, since there are only a few forks, send bug reports and whatever to me by e-mail or as blog comments. It seems likely that more forks will follow, and I will have to bring up a public issue tracker one way or another. I’ll think of a plan.

Switching my web site to https is also important as people download more often. I have to invest time into that soon.

A minor update to Steamhammer’s web page adds a development roadmap, makes my e-mail address easier to find, and has a few other tweaks.

Steamhammer 1.1 development is running smooth. My change log is similar in size to the 1.0 change log, which took longer. The new zerg macro code is complete and working, though it needs adjustments to do fewer stupid things and more smart things—I won’t let the new release play worse than 1.0. I currently have (... counting ...) 10 critical bugs and a few other high priority issues. I’ve been both finding new bugs and retiring issues at a high rate, so it’s not clear how long it will be before release, but it could be this week.

More documentation is on the menu for the next version, so people can get underway more easily. I’ll make the release then write starter docs shortly after.

thou shalt not divide by zero

Steamhammer is mostly reliable, but does crash occasionally. When SSCAIT catches a crash, it saves a couple of low-level dump files that offer a hope of tracing the cause.

Today I got a game with a division by zero crash, which was infuriating. Division by zero must die! Zero tolerance of zero! I decided to eliminate all bad divisions by inspecting every division “/” and mod “%” in the code, though it was a little tricky when the comment delimiter is “//” and “%” is used in strings.

	// don't even enter the same city as division by zero

I did it, and shored up a few weak spots. No direct division by zero should be possible in Steamhammer. I also added error checks in the hope of catching bad parameters before they are passed to BWAPI, which looks like the cause of other rare crashes that I have never been able to trace.

I tell the story as an example. If you want to write reliable software, you have to be thorough. I fix all reproducible crashes immediately, so that (so far) crashes on the server are unreproducible and difficult to solve, but I don’t let that stop me.

how to beat Stone, according to AIL

I took a little time off from my main goals for Steamhammer 1.1 development to follow AIL’s advice on how to beat Stone. AILien’s implementation is in WorkerManager::handleCombatWorkers(), so I guess AILien’s drones are given combat orders when an enemy is very near. The suggestion was for drones to attack enemies that came within range 50 and were not moving.

My implementation is a little different. I put it in WorkerManager::updateWorkerStatus() and was careful to disrupt mining as little as possible. I tried ranges 48 and 64 and decided that 64 worked better. The else if condition ensures that a worker which has been fighting returns to mining as quickly as possible (idle workers are put back to work within the same frame). A possible disadvantage is that gas miners do not defend themselves (but they never did before, either, and it’s not hard to add).

		// If the worker is busy mining and an enemy comes near, maybe fight it.
		else if (workerData.getWorkerJob(worker) == WorkerData::Minerals)
		{
			BWAPI::Unit target = getClosestEnemyUnit(worker);

			if (target && !target->isMoving() && worker->getDistance(target) <= 64)
			{
				Micro::SmartAttackUnit(worker, target);
			}
			else if (worker->getOrder() != BWAPI::Orders::MoveToMinerals &&
				worker->getOrder() != BWAPI::Orders::WaitForMinerals &&
				worker->getOrder() != BWAPI::Orders::MiningMinerals &&
				worker->getOrder() != BWAPI::Orders::ReturnMinerals)
			{
				workerData.setWorkerJob(worker, WorkerData::Idle, nullptr);
			}
		}

I also had Steamhammer build a sunken when it could. That freed up zerglings from chasing SCVs so that they could pay a visit to Stone’s base. Games became short and decisive. In a test match, Steamhammer beat Stone 15-0. The closest it came to a loss was getting down to 3 drones before the sunken came on line; most games were one-sided.

Steamhammer 1.1 will take that much longer to come out, but it will be that much stronger. The bot should lose fewer drones to harassment by the enemy scout, which will help in many matchups.

Update: The code has a bug! To fix it, change this condition

			else if (worker->getOrder() != BWAPI::Orders::MoveToMinerals &&
				worker->getOrder() != BWAPI::Orders::WaitForMinerals &&
				worker->getOrder() != BWAPI::Orders::MiningMinerals &&
				worker->getOrder() != BWAPI::Orders::ReturnMinerals)

to this, adding one more check:

			else if (worker->getOrder() != BWAPI::Orders::MoveToMinerals &&
				worker->getOrder() != BWAPI::Orders::WaitForMinerals &&
				worker->getOrder() != BWAPI::Orders::MiningMinerals &&
				worker->getOrder() != BWAPI::Orders::ReturnMinerals &&
				worker->getOrder() != BWAPI::Orders::ResetCollision)

Drones (and I presume other workers) go into ResetCollision mode for just one moment as they finish mining minerals. The effects of the bug are subtle but far-reaching. Drones going toward the minerals have the mineral-mining job. Having finished mining, they hit ResetCollision and, since they are carrying minerals, end up with the return-cargo job until they return their minerals to the hatchery, when they go idle again and are reassigned to gather minerals. (Returning cargo is not part of UAlbertaBot. I added it to reduce unnecessary loss of resources.) Mining appears to proceed normally, but behind the scenes drones are switching jobs unpredictably. You can no longer accurately count your mineral drones. Other tasks looking for mineral drones do not accept return cargo drones, which can cause buildings to be constructed slightly later. The effects are small and not immediately noticeable, but harmful.

Sneaky sneaky one!

Tscmoo zerg is back!

Tscmoo zerg had been disappointing recently, but today it was updated and went on a rampage, scoring 4-2 against Killerbot, Bereaver (2 games), LetaBot, Krasi0, and Iron.

Tscmoo played similarly against all opponents, no matter the race. It opened 9 pool, safe against cheese and intimidating some enemies into overcommitting to defense. The rest is adaptive, but when the coast was clear, it followed up with two more hatcheries before getting gas and a hydralisk den—no matter the opponent. It dedicated itself to the long game by starting carapace upgrade before hydralisk speed or hydralisk range, an unusual and risky decision. And it took a distant “hidden” third base before spreading to the rest of the map, adding to the risk (Bereaver took advantage in one game).

Hydralisks were not always the right choice, but Tscmoo made them work with good surrounds, careful micro versus shuttle-reaver and spider mines, and distraction plays like 4-hydra drops. It made fewer mistakes.

Will Tscmoo zerg will fall back to another strategy if the hydras don’t work against some opponents? I guess we’ll see.

Steamhammer’s 2 strategies vs XIMP

Steamhammer 1.0 got to try both of its prepared strategies versus XIMP in close succession. Did somebody schedule the games?

In the first game (Brood War replay file), Steamhammer went mutalisks. XIMP counters with corsairs, so I was taking a risk. The strategy is inspired by Sijia Xu's Overkill, which plays a 2-hatch muta opening that smashes XIMP before protoss reacts.

Instead of a quick air attack like Overkill, Steamhammer went three hatch before pool and prepared a heavy blow. The build produces a large first wave. XIMP saw the lack of ground pressure and built fewer cannons than usual, only 5. The mutas started in on the cannons, but had only killed 2 before corsairs chased them away. With the slower build, XIMP was able to react correctly.

Steamhammer strikes the cannons

When Steamhammer has a spire and scouts air tech, it makes 1 pair of scourge as insurance. When it verifies enemy air units, it aims for exactly the right number of scourge to clear the enemy air force. The game became a muta-scourge versus corsair seesaw. XIMP saw air units and felt the need to keep investing in corsairs, so it could not make carriers. Steamhammer pruned back the corsair numbers with scourge, but was short of gas. Neither side could keep a large force in the air.

XIMP took a third, and Steamhammer took bases down the left side of the map, struggling to get more gas. But the new gas came online slowly, and zerg resorted to making zerglings—which is what it needed all along! The lings tore down the protoss natural, and then XIMP could not keep up. It was an entertaining game.

Steamhammer hits XIMP’s natural

In the second game (Brood War replay file), Steamhammer went hydralisks, the safe and natural choice. Hydras attacked toward the protoss natural and danced with the cannons and carriers for a while, killing some cannons and then most interceptors. When enough hydras had accumulated, they cleared the cannons. At the same time, scourge arrived and exploded carriers. The game looked over.

Steamhammer fights cannons and carriers

But no, no easy win. Steamhammer hit a bug which made it unable to produce any more hydras. Scourge were able to hold off carriers for a while, but without ground attack there was no hope. Solving all the similar bugs is a major goal of version 1.1.

Steamhammer beats Iron

Today Steamhammer 1.0 got to try, for the first time, its planned strategy versus Iron (Brood War replay file). It worked as intended, and Steamhammer won quickly. In the screenshot, Iron placed both its marines on the ramp, which made it easy. In my tests, the ramp still breaks and the bunker falls if Iron gets marines into the bunker.

Steamhammer breaks Iron’s ramp

I’m sure Igor Dimitrijevic will update Iron before long, but for the moment Iron loses to a standard 9 pool speedling opening—an opening that Steamhammer also plays 10% of the time versus other terrans, with fair success. Iron’s zergling defenses at that timing are not enough; the lings overwhelm the blocking SCVs and the bunker. Try the same against LetaBot and you will die!

By the way, getting zergling speed is critical versus terran. Zergling speed gives tactical and micro advantages against all races, but bots play poorly and don’t gain full value from the advantages versus protoss or zerg. But terrans have ranged units, and the gain from spending less time under fire before reaching the target is giant. It takes many more marines to overpower fast zerglings; marines should be unable to move out onto the map at all until academy tech. My suggestion: By the time you have 6 or more zerglings versus terran, you should at least be collecting gas to start Metabolic Boost. I think the main exceptions are unusual low-economy builds and zerglings for immediate emergency defense.

recreating the Newbie Zergrush strategies

Newbie Zergrush has vanished like the morning dew. If you want to test against its nonstandard strategies and missed the boat, I reconstructed two of them. You can configure Steamhammer 1.0 to play these openings. The experience is not 100% authentic, but it’s close. I made these by watching the build orders, not by copying the original configuration, so there may be slight differences. Also there should be code differences, so the way the openings play out should be a little different. It’s still a good test of whether your bot survives these aggressive strategies.

Newbie Zergrush played 7 pool 12 hatch versus zerg and 8 hatch 7 pool versus terran and, at least at first, protoss. It also had a mass sunken build which I did not try to recreate.

    "7Pool12Hatch"  : { "Race" : "Zerg", "OpeningBuildOrder" : ["drone", "drone", "drone", "spawning pool", "go scout location", "drone", "overlord", "zergling", "zergling", "zergling", "zergling", "zergling", "hatchery @ min only", "zergling", "zergling", "zergling"]},
    "8Hatch7Pool"   : { "Race" : "Zerg", "OpeningBuildOrder" : ["drone", "drone", "drone", "drone", "hatchery @ min only", "spawning pool", "go scout location", "drone", "overlord", "zergling", "zergling", "zergling"]},

I cheated and cut these down to keep the post short. At the end of each build order, use the power of copy-paste to extend the line of , "zergling" out a long distance. That’s because the strategy manager doesn’t know these openings, so it will start an unhelpful default strategy as soon as the opening book ends.

Get the Steamhammer 1.0 binary release from from my page or from SSCAIT. I wrote documentation for the config file. Add the two opening book lines above (with the extra zerglings) to the “Strategy” subsection (be careful about commas, JSON files are unforgiving). Then configure Steamhammer to play the opening against your bot. For example, if you’re testing a terran bot, one way is to rip out the original ZvT setting and replace it with "ZvT": "8Hatch7Pool",.