archive by month
Skip to content

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.

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",.

Steamhammer status update

Steamhammer 1.1 is in the works. It is currently short 1 major feature, 2 smaller features, and 7 critical bug fixes that I know of, so it is still a ways off. When it does come out, the headline feature will be the ability to transition from one unit mix to another in the middle game. Currently, all transitions are written into the opening book, obviously a severe limitation. The work involves completely rewriting the macro code, and I’m only part way through.

I keep changing my mind about the order of improvements. Here is an outline of what I’m thinking today, so that when I do something completely different, nobody will be surprised.

The basic order of middle game skills should be mutalisk skills, then lurker skills, then drop and runby skills (which are related in a way). For the benefit of people who fork Steamhammer, I want to make sure that all races work and that the bot can play random—it’s theoretically possible today, but I haven’t tested it so expect some bugs. It’s also a priority to upgrade base defense in general and worker defense in particular. In long macro games Steamhammer 1.0 does stuff which is bananasuperbeyond boneheaded, which I’ll have to ameliorate a little at a time. Scouting has got to be worked on and must tie into tactical decisions better. Upgrades need to be more aggressive, and the first hive units should be ultralisks. I really really want to make a start on opponent modeling soon. And I have many other plans that I’ve written down but not put into priority order.

Anyway, here are today’s plans for the headline features of the next few versions. I’m expecting many extra features and fixes in 1.1 and few extras in 1.2 (and beyond that I don’t want to predict).

  • 1.1 - transitions
  • 1.2 - test and fully support random play, like UAlbertaBot
  • 1.3 - mutalisk reign of terror

After starting work on 1.1, I’ve already changed my mind twice about what to do when, and I may rethink again without warning. I shouldn’t try to plan further ahead.

Now I need to put my head back down and return to work.

SSCAIT semifinals and third place playoff, part 2

Today is the second and more interesting part of the SSCAIT 2016 round of 4. Krasi0 fought XIMP, with the winner to move on to the final. The loser went to a 3rd/4th place playoff match against Iron.

Krasi0 vs XIMP

Game 1 was on Empire of the Sun. XIMP is the carrier bot. Terran Krasi0 saw what was coming and chose to attack with tanks and early goliaths before the carriers had all their interceptors. The tanks were somewhat inefficient in sieging down the cannons (most bots are), but they were fast enough. XIMP was in trouble regardless, but losing too many probes sealed it.

Game 2 was on Heartbreak Ridge, a 2-player map. It has many cliffs which favor carriers, but it also has low-ground bases and a short ground distance between the mains, which favors mech attacks. Krasi0 attacked with the same timing as the previous game. Krasi0 again killed the natural, but terran reinforcements chose a path through the center which took them next to the protoss base, distracting XIMP into playing correctly against the reinforcements instead of the main terran force. Krasi0 could have sent fresh units the other way around the center of the map, where carriers could not have engaged them until they arrived. Then the mech army would have stayed compact.

But as it happened, the diversion toward reinforcements and Krasi0’s difficulty in attacking the protoss main through the very narrow entrance meant that the carriers were able to build up interceptor numbers. XIMP defeated the goliaths and started chasing the terrans away. Krasi0 sent goliaths piecemeal toward the front and lost them a few at a time.

While one carrier group fought terran units around the center, another group departed on XIMP’s trademark edge crawl to survey the bases around the map and attack from the rear, a slow but dangerous plan. Both players were in some trouble. XIMP had only its main left and no long future unless it could take and hold another base, while Krasi0 had 2 bases but not enough anti-air to stop the carriers. What would happen?

XIMP’s front carriers played poorly and lost numbers. They could have returned through the center to cover while XIMP retook its natural, or they could have joined the attack on the main where cluttered buildings made goliaths awkward, or they could have circled the terran natural and attacked it from above the cliff, but they insisted on attacking from the front without the advantage of a cliff. With that decision, Krasi0 had the lead. Terran should have sent its ground army, minus air defense, to the protoss base, which would either cause a base race or bring the carriers back home to defend. Instead Krasi0 left tanks and vultures idling around the center of the map. Krasi0 did not use its lead.

Neither side was making the most incisive moves, and the outcome was unforeseeable. Fun game!

Krasi0 got a third base up. XIMP had no money but destroyed the terran main, slowly. Krasi0 did not try to float its factories, but rebuilt from scratch instead. Finally Krasi0 built up enough stuff to start hitting carriers, and that was enough.

Both players fought like lions. After mistakes all around, the victory was narrow. Krasi0 won and went to the finals.

Lesson: You never know what little thing will make the difference. A small improvement by XIMP (try to attack from above cliffs, or destroy bases more efficiently) or a minor weakness in Krasi0 (build the 3rd too late, be unable to recover from losing all tech) could have given us a different result.

Iron vs XIMP

Iron is far more aggressive than Krasi0. Iron attacked with infantry and tanks, sieged down the cannons, held off carriers with marines and turrets, finally added valkyries almost as an insult, and won easily. The second game went about the same way. A group of carriers escaped and caused damage, but only until the undefended protoss base died.

It’s not easy to beat XIMP, or more bots would do it, but Iron made it look easy.

the big BOSS bug with zerg

UAlbertaBot includes BOSS, the Build Order Search System, which accepts goals like “I want 12 zerglings and the speed upgrade” and calculates how to achieve them as quickly as possible, including creating the spawning pool and extractor if necessary, adding supply, building the ideal number of workers, and the exact spending sequence for everything. It does make approximations, but the build orders it produces should be close to optimal for the goal you give. UAlbertaBot by default uses BOSS to oversee its production as soon as it gets out of its opening build order.

Unfortunately BOSS also has limitations and bugs. If you give it a complex goal, it can’t run in real time (though you can still use it to create opening builds offline). And for zerg, BOSS has the bug that it will sometimes throw in an extra building that is not implied by your goal, usually an extractor, hydralisk den, or lair. I only wanted zerglings, why are you giving me a hydra den?

Blog reader Micky Holdorf looked into that bug and found the cause. Thanks for the report!

BOSS wants to search efficiently, so given your goal, it calculates the prerequisites so that it doesn’t waste time searching actions that don’t contribute to the goal. It calls this the goalMax, the most of each thing that you might want in order to achieve the goal. For terran and protoss, BOSS recursively calculates all the prerequisites and carefully counts each. Here is the code for zerg, from DFBB_BuildOrderSmartSearch::setPrerequisiteGoalMax():

    else if (getRace() == Races::Zerg)
    {
        _goal.setGoalMax(ActionTypes::GetActionType("Zerg_Spawning_Pool"), 1);
        _goal.setGoalMax(ActionTypes::GetActionType("Zerg_Extractor"), 1);
        _goal.setGoalMax(ActionTypes::GetActionType("Zerg_Lair"), 1);
        _goal.setGoalMax(ActionTypes::GetActionType("Zerg_Spire"), 1);
        _goal.setGoalMax(ActionTypes::GetActionType("Zerg_Hydralisk_Den"), 1);
    }

That’s not a calculation, that’s a stand-in! This part of the code was never written in the first place. I wonder if there was a bug in the recursive calculation for zerg, and Dave Churchill put in a stub until it could be dealt with?

Anyway, the correct fix is to write that code. One workaround is to delete the setGoalMax calls for zerg and always pass in goals that include their own prerequisites; you have to remember that you might need a spawning pool, or notice that you’ve lost yours and explicitly put it in. Micky Holdorf found another workaround with the same effect, which tells BOSS not to worry about prerequisites at all. In DFBB_BuildOrderStackSearch::generateLegalActions() change the line

            if (!goal.getGoal(actionType) && !goal.getGoalMax(actionType))

to this, which tells it that only goals and not prerequisites are legal actions:

            if (!goal.getGoal(actionType))

I don’t have any plans to write a fix for Steamhammer. If somebody sends me one I’ll certainly consider it! But Steamhammer is moving away from BOSS, because BOSS answers the opening question “how do I get this stuff as fast as possible?” and not the middlegame question “how do I spend my resources efficiently?”

Steamhammer at 78%

I looked up Steamhammer’s standing on SSCAIT today and was startled to see it at 78% win rate, equal with Bereaver.

standings of Bereaver and Steamhammer

The winning percentages are volatile and I don’t believe for a minute that Steamhammer is as good as Bereaver. But I have never seen it fluctuate up that high, either. Apparently the 1.0 version is substantially stronger. It is definitely better than 0.2, but how much?

Update: I documented Steamhammer 1.0’s configuration file. Because of the new features I added, somebody already expressed interest in forking from Steamhammer rather than directly from UAlbertaBot. Go for it!

Steamhammer and opponent-specific strategies

Some successful bots play in a way that seems to me so unlike sound strategy that I feel morally obligated to beat them—or at least to try. Soundness must be upheld! I do it at the expense of teaching Steamhammer to play well in the general case, but it still feels worthwhile. It’s a quirk of mine, I guess.

As Steamhammer becomes more reactive I’ll remove some of these handmade counters, and when opening learning is in place I’ll drop all of them. But for now I think this is the way.

versus XIMP

The carriers must be crushed! For Steamhammer 0.2 I came up with a three hatch before pool build that puts a lot of hydralisks on the field, in the hope that they would tear down the cannon wall and win, or failing that, would hold off the carriers and bleed XIMP’s money away. I wrote up the first game. It didn’t quite work out; Steamhammer tore down the cannon wall, but with its other weaknesses did not have enough left over to win convincingly.

In later games, I found that Steamhammer 0.2 wins occasionally, but usually loses. It depends partly on the angle at which the hydras approach the cannons. Steamhammer doesn’t know the first thing about good formation, and when the hydras approach around a corner or otherwise in poor formation, they can’t take the cannons. The bot needs to know how to form up.

And it needs other improvements. It can eventually deal with either the cannons or the carriers, but when it tries to deal with both it loses. The tactical decision making is not there. Plus Steamhammer 0.2 has weak macro and doesn’t make as much stuff as it should.

Steamhammer 1.0 has adequate macro. It sometimes lets its minerals run up, but adds enough hatcheries to recover. Its hydra build is improved. The tactical decisions and formation are not improved, so I’m not sure how games will go.

For fun, I added a second three hatch before pool opening that goes mutas. We’ll see whether Steamhammer’s air power can prevail with adequate muta micro and disorganized scourge usage. XIMP goes corsair when it meets air, so I timed the build to produce the first 9 mutas simultaneously and hit hard before XIMP can react.

Versus XIMP, Steamhammer chooses hydra and muta each 50% of the time. Steamhammer already played 1 game versus XIMP with the air build, but it was on Benzene and Steamhammer’s third hatchery drone got lost due to the back door block—the same problem that Bereaver had against XIMP on Heartbreak Ridge, with the same sad result.

versus Iron

The vultures must be repelled! They don’t make sense as a mainstay against zerg. I tried an opening tailored and timed to beat the vultures without having to equal Iron’s superb micro. When I ran my specialized strat against an early December version of Iron, Steamhammer won 13-2. Iron is updated frequently, so as the tournament deadline approached I tested again against the 14 December version of Iron, and Steamhammer scored 8-7; that’s why I said that it had about equal chances. I messed up and my strat didn’t run during the tournament. But later I tried it against the tournament version of Iron, only a few days newer, and Iron won overwhelmingly! All respect to Igor Dimitrijevic, Iron improves at an incredible rate.

For Steamhammer 1.0 I put in a cheesy exploit strat, not a sound counter strategy. Against the tournament Iron it wins 13-2, losing only on 2-player maps. Iron improves so fast that I’m not sure it will work against the January Iron—and if it does, probably not for long! I have 2 different sound counter strategies in hand, but Steamhammer needs new skills before it will be able to pull them off. Eventually I hope to force Iron to play soundly, and then its amazing ability will be hard to touch.

versus Jakub Trancik

The cannon proxy scares me. If you see the cannons in time, they are no trouble. But what if you don’t see them? Steamhammer is none too sharp at scouting for surprises. In practice Steamhammer 0.2’s 9 pool is successful in stopping the cannons when no bug intervenes.

For Jakub Trancik I went with Steamhammer 1.0’s new “hidden base” skill in a 9 pool expo opening. This is Steamhammer’s only use of a hidden base in a far corner of the map. If the cannons are seen, lings will tear them down. If they are not seen, the zerg main will probably fall sooner or later, but the hidden base will live on. Steamhammer 1.0 is not fully robust, but it can often recover from losing its tech.

Steamhammer 1.0 also plays the 9 pool expo 20% of the time versus random opponents, because why not?

versus GarmBot

Against Aurelien Lermant’s GarmBot, Steamhammer plays its 13 pool opening—an opening that is specialized for play versus terran. By sheer coincidence it is also a strong counter to zerg GarmBot!