archive by month
Skip to content

STARTcraft

Dave Churchill is feeding his students Starcraft as educational food. And he is coming out with new software to make it even tastier.

True to his tradition of naming projects with puns on “Starcraft”, he created a minimal starter bot named STARTcraft. It’s in C++ for Windows; he promises a Linux version later. Its selling point is that it is extremely quick and easy to set up and start coding. Installing Visual Studio 2019 is the slowest part, and after that it should take only minutes to get the bot running: Download the repo, unpack Starcraft from a provided link, open a VS project to code, or run a batch file to launch Starcraft and the bot. A couple of long videos (2 hours each), linked right in front on github, go over Starcraft AI in general, and over STARTcraft and BWAPI programming in particular.

Lately, I have the impression that most new bot authors want to start from scratch and create a bot that is entirely their own. It looks to me like STARTcraft is ideal for that. When you run it, it sends its workers to mine minerals, builds supply when necessary—and that’s all. Well, it also does a little debug drawing on the screen, and some basic map analysis on startup, and keeps track of when each map tile was last seen. There are a small number of utility functions in MapTools and Tools. It is basically just enough to show that the bot is working and give small examples of how to do things. In fact, the few things it does, it does in a simple way rather than an excellent way. If you end up creating a strong bot, you will have rewritten the existing behaviors. It will be all yours.

I’m an old-timer, so I may not be the best at judging what’s good for a beginner. In the past I have recommended starting with an open source bot that is already capable and full of features, but today capable bots are complex and may be intimidating to start with. STARTcraft is simple. With the videos as documentation and the code as examples, and with a working starting point, it is almost as easy as it can be, and yet leaves nearly everything to you. Even if you have unique ideas about architecture, the bot is small and you can refactor it into the shape you like. Recommended.

Steamhammer’s special preparation for AIST S4

AIST S4 starts tomorrow. AIST is a different style of tournament from AIIDE, and I prepared differently for Steamhammer’s specific opponents. From the competitor’s point of view, AIST is a sequence of best-of-3 matches until you lose 2 matches—or win in the best-of-5 final. Steamhammer is unlikely to face the same opponent twice. With only a few games per opponent, Steamhammer’s deep library of builds is irrelevant.

I could have tuned the learning system to work well in short matches, as PurpleWave has done. Instead, I decided it was simpler to disable learning and use Steamhammer’s classic enemy-specific opening selection, which has gathered dust ever since learning was implemented in Steamhammer 1.4. Once I decided, I completed all the prep in a matter of hours. For each of the 5 opponents, I selected between 3 and 5 openings that were different and had high winning rates (except versus Stardust, which Steamhammer almost never defeats, where I picked what I hoped might have a chance). To choose, I looked at saved learning data for Steamhammer and Randomhammer zerg on BASIL, and for Steamhammer on SSCAIT, because each instance came up with different winners thanks to random sampling. Steamhammer will choose randomly from the openings for each opponent, according to probabilities I set, which sometimes vary depending on the map size.

For those who examine the tournament games, these lists may make the builds easier to interpret.

Stardust

  • 9PoolHatchSpeedAllIn (Styx build)
  • 10Hatch
  • 3HatchLingExpo
  • 3HatchHydra
  • 2x10HatchAllIn

BananaBrain

  • 2x10HatchSlow
  • 10Hatch
  • Over10HatchBust (zergling bust)
  • 3HatchMutaPure
  • OverhatchExpoLing

PurpleWave

  • ZvZ_Overgas9Pool (one hatch muta)
  • 11HatchTurtleHydra
  • 9PoolHatchSpeedAllIn (Styx build)

Dragon

  • AntiFact_2Hatch (2 hatch muta specialized to beat factory builds)
  • AntiFactory2 (3 hatch hydra into muta)
  • 2HatchLurker
  • 3HatchLurker

WillyT

  • 9PoolLurker
  • OverpoolLurker
  • 11Gas10PoolLurker
  • 11HatchTurtleLurker
  • ZvT_3HatchMutaExpo

Steamhammer has virtually no chance to beat Stardust. I’m guessing that Steamhammer has maybe a 20%-30% chance against PurpleWave or BananaBrain, and a better than even chance against each of the terrans. Notice the lurker builds, making use of Steamhammer’s stepped-up lurker skills.

Steamhammer’s combat sim smoothing

UAlbertaBot has a simple mechanism for hysteresis in combat: After retreating, it has a time limit before it will attack again. Combat sim results are unstable, and using the raw simulation results can cause vacillation from one frame to the next, which is Not Fun. When I changed Steamhammer’s tactics to cluster units and do combat simulation separately for each cluster, I had to give up UAlbertaBot’s mechanism. The clusters have to be formed anew every frame, so they cannot carry over data from frame to frame. Ever since then, Steamhammer has had jittery tactics, often engaging and disengaging its units at computer speed. It hurt in all matchups, and to me it was most obvious in zergling on zergling micro in ZvZ games. Combat sim smoothing is my first attempt to pull myself back onto solid ground.

It seems to me that Steamhammer’s smoothing is a complicated idea, but the implementation is short and hides its internal details well. It’s a similar idea to one in PurpleWave: Instead of attaching data from previous frames to the cluster, which won’t exist in the next frame, attach it to the individual units. Then the next cluster, freshly created in the next frame, can extract the data from its own units, which are often different from the units in any older cluster.

The only alternative I see is to attach data to locations on the map instead. Both schemes rely on locality, meaning that units move around relatively slowly and affect only their immediate surroundings. Both schemes risk breaking down if units are suddenly transported, recalled by an arbiter or sent through a nydus canal. In those cases you should probably erase their saved data.

Here’s how I chose to do it. A cluster can do at most one combat sim per frame, and no unit participates in more than one cluster. For each cluster that does a combat sim, it sends the cluster’s set of units and the combat sim results (“we’ll win” or “we’ll lose”, nothing more) to Micro::setAttack(). The Micro module already keeps info about each unit’s orders and other info in a map of MicroState objects, so I thought that was the logical place. For smoothing, MicroState stores one floating point number for each unit, which is a smoothed average over time of the unit’s “we’ll win” (represented as 1.0) and “we’ll lose” (represented as 0.0) values. The algorithm is exponential smoothing, which is the next closest thing to trivial.

The cluster, having informed Micro of its latest combat sim result, then calls Micro::getSmoothedAttack() to get the smoothed combat sim result, which is what it actually relies on. Micro simply averages together the smoothed values for the cluster’s units and compares to 0.5. Done.

It’s smoothing only, there is no hysteresis. If the fight is a close call and remains so over time, Steamhammer may still oscillate between engaging and disengaging. But I think it is much rarer than before. I see the smoothing as an important improvement.

Many variations are possible. This is my first cut, and I did no testing of alternatives. Maybe it is better to smooth the actual combat sim scores, rather than only the binary win/lose results. There are ways to add hysteresis to the scheme. I’m sure improvements are waiting to be found. I may look for them.

Next: Steamhammer's special preparation for AIST S4.

Steamhammer’s pathfinding

UAlbertaBot comes with distance maps: Give a destination tile position, get back a grid with the approximate ground distance from each tile on the map to the destination, or -1 if you can’t get there from here. Since forking Steamhammer from UAlbertaBot, I’ve added to and refined the subsystem, and used it for more purposes. For example, Steamhammer uses the ground distance to decide when to transfer workers to a new base so that they arrive just after it finishes. Pathfinding is an obvious use of distance maps, so maybe it’s surprising that I didn’t implement it until now.

the implementation

The low-level implementation is simple. The move command the.micro.Move() accepts an optional distance map. If provided, it calls on a simple algorithm that searches the distance map and finds a shortest-distance path segment 8 to 12 tiles ahead of the moving unit. MoveNear() and MoveSafely() also accept the optional distance map and pass it on to Move(), which does the work.

Plumbing the distance map through the abstraction hierarchy is the hard part. I chose to allow the squad’s order (class SquadOrder) to optionally keep a distance map. Basically, at any given time a squad may either have a distance map to its target, or not. If it has a map, then it is passed along on every movement order for the squad’s units. CombatCommander decides on the squad orders and generally gives a distance map to ground squads, and none to air squads which can fly straight to their destinations. Most squad targets are bases, and it happens that Steamhammer precalculates a distance map for each base. So a squad order to target a base uses the base’s distance map for free, while a squad order to target an arbitrary location calculates a new distance map which belongs directly to the squad order. I’ve been planning that detail of the implementation for years.

Pathfinding is used for squads with ground units, for the scouting worker, and for sending a worker to construct a new base. It is not used for constructing other buildings (which are usually nearby though not always) or for transferring workers between bases.

If the debug flag to draw squad information is turned on, a squad which has a distance map is listed with a “#” symbol before its name. The “#” represents the grid of distances.

results

Pathfinding works well in test games, though not perfectly. The paths are not true shortest paths (see “disadvantages” below), but close enough for now. In test games on Medusa, units of the Ground, Recon, and Watch squads navigate well, no longer getting stuck in the dead end back doors of the bases. On the other hand, I commonly saw a small number of transferred drones stuck there. One oversight is that some units still tend to get stuck behind the neutral buildings on Benzene. Benzene is not in the AIST S4 map pool, so I didn’t notice.

I also need to update the precalculated maps when map obstacles are destroyed. I’m sure I’ll iron out the details in time. It didn’t seem vital for now.

advantages

Once a distance map is calculated, it’s cheap to follow not only a shortest path for one unit, but any shortest path for all units. If you like one shortest path better than another for any reason, you can choose it on the fly.

A distance map need not be calculated from a single destination tile. It can start from any set of destination tiles, so that you can do things like find the shortest path to enter a region, or choose the closest of 2 equal goals. A distance map need not record literal distance; it can pretend that a dangerous tile is larger than others so that you tend to path around it when you can. Distance maps of different kinds have many potential uses.

  • Ground unit pathing through terrain, the implemented feature.
  • Ground or air unit safe paths, to avoid all known risk for scouting, or to discover containments.
  • Ground or air unit least-danger paths, to reach a defended target with minimal damage.
  • Ground or air paths which are out of sight range of known enemies, for sneaking around.
  • Paths to safe firing zones, so we can attack a defended target from outside its defences.
  • Maintaining a contant distance from an objective, such as the convex hull of a group of enemy units, for arcs and surrounds.

disadvantages

As implemented, the distances mapped are not true unit movement distances, but Manhattan distances. Units moving diagonally, say from bottom right to top left, tend to move not diagonally, but first to the left and then upward. I intend to correct it, but haven’t decided on a plan yet.

Distance maps are a bit bulky and slow, and usually involve calculating a lot of paths that you won’t go anywhere near, so you don’t want to calculate too many maps each frame. It’s better to reuse them as much as possible, which adds complexity. UAlbertaBot comes with a distance map cache, but its replacement policy is “periodically throw out all entries in case some may be unused.” Something a little more sophisticated is called for, especially if you want to store different kinds of distance maps in the same cache.

If you want to map something that changes frequently, like detailed enemy firing zones, you can either recalculate the map frequently—which is fine up to a point—or accept that it will often be a little outdated—also usually fine.

The disadvantages don’t seem severe to me. Distance maps gain their flexibility by doing more calculation than is necessary for any one use case—guess what, work harder, earn more. It does call for a little attention to efficiency.

Next: Combat sim smoothing.

new bot Vyrebot

The latest Steamhammer is uploaded to SSCAIT, descriptions of its new pathfinding and new combat sim smoothing are in the wings, Dave Churchill has been quietly turning out new tools, but it all must wait because we have a new bot! And it’s an unusual one.

Zerg Vyrebot accurately describes itself as “?????????” It was disabled on SSCAIT after 3 games, but remains active on BASIL for now. It follows a unique game plan. Early on, it makes a single sunken colony for defense, placed without regard to covering all buildings or to the enemy’s line of approach. It slowly techs up to lurkers for further defense and expands to its natural. Though slowed by gas shortages as it adds ever more lurkers, plus a spire for scourge in case it needs air defense, it progresses to hive, makes defilers, and researches consume. Then the game is on: It attempts a coordinated lurker-swarm attack! I thought it was an advanced skill, but no, for Vyrebot it’s the first skill that it attempts to master. And, though unpolished and slow-moving, it’s not bad at it, far better than I expect for a brand-new bot with no other significant skills. Dark swarm is difficult to use well, and I’ve seen tscmoo play more weakly with it.

Vyrebot has a public github repo. A comment mentions YouTube videos, but I didn’t find any.

The Vyrebot game that I liked best is Vyrebot-Slater on Heartbreak Ridge, played on BASIL. But of course other games are less impressive. Usually Vyrebot loses, as you should expect for a bot whose best skill doesn’t come into play until the late game. Any opponent that attacks early, or that attacks in the midgame and knows how to defeat lurkers, or that attacks by air before the late spire finishes, is likely to have an easy time of it. If you want to win, then early game skills are more important than late game skills, because you always have to play the early game and only sometimes reach the late game in a survivable position.

Still, I appreciate a creative approach. Go Vyrebot!

Steamhammer 3.4.8 for AIST S4

I have submitted Steamhammer 3.4.8 to AIST S4. Here is the change list, nice and long considering how long there was to work on it. The deadline isn’t for hours yet, so revealing my Dark Secrets early might theoretically come back to bite me, but... eh, it won’t bite hard. I’m still concealing my specific preparation for these opponents.

I tried for important advances that wouldn’t take long. I think I met my promise that Steamhammer will play a more “lucid” game, but let’s see how many fresh bugs I didn’t notice! The biggest improvements are pathfinding for ground units, smoothing of combat sim results, and more supple lurker behavior. My intention has always, ever since I started Steamhammer in December 2016, been to work on mutalisk micro before lurker micro, because it is more important. But I let it pass for too long, and now other bots have strong muta micro. I’ll get back to mutas (they are still more important), but I always want to do something different from everybody else, and now I want to work on lurkers first.

The irradiated squad is a nice improvement, too. Anyway, read on.

map analysis

• Reject starting bases (as identified by BWAPI::Broodwar->getStartLocations()) which have no minerals; they are no longer considered bases, much less possible starting bases. I think these are always observer slots. The AIST S4 version of Aztec has observer slots. Formerly, Steamhammer accepted whatever BWAPI told it and created a Base object for each one, marked as a starting base. The error had surprisingly little effect on play (Steamhammer is not tempted to expand to valueless “bases”), but it did cause sneaky mistakes. One is that a starting base is assigned a natural if conditions are met, with the result that the center bases on Aztec were considered naturals of some of the observer slots, and that information could be used for certain decisions, like where to make a hidden base. I’m glad I found this by looking at the maps, because the mistaken decisions might have been a nightmare to diagnose!

scouting

• The scout worker is released whenever friendly combat units are near. This is the final relaxation of the originally tight conditions for releasing the scout worker early, which I’ve been progressively relaxing.

• If the enemy has no known anti-air units, send an overlord to each base on the map to keep watch. Formerly, the rule was “if the enemy is not known to have tech to make anti-air units”, which was rarely satisfied outside the early game when there weren’t overlords available to distribute.

squads

An irradiated squad centralizes the code for managing irradiated units and implements fancier behavior. Every irradiated organic unit (the ones harmed by the irradiate spell) goes into the squad, except for queens which have enough energy to cast and defilers after consume is researched—they will try to cast something before dying. Formerly, an irradiated unit that could burrow would, and that was the extent of the reaction (the successful reaction, anyway; mutas had code to separate out an irradiated muta, but it mysteriously broke). Now, an irradiated unit that is near friendly units will burrow if it can, or try to flee from its friends if it cannot. If it is near enemy units, it will approach to expose them to the radiation, and also attack if it can. If neither, it will run and do what scouting it can. Some of the behavior looks questionable, and I suspect bugs, but it’s good enough for now. The reaction of unirradiated units to nearby irradiated units is unchanged—only workers try to protect themselves, others carry on as usual.

• A newly-created squad did not pass along its order to its own micromanagers until it was updated. It was an oversight inherited from UAlbertaBot. The main effect is that a defense squad would not begin to act until exactly 8 frames after it was created, so defense was always a little bit late.

• The debug display for squads now shows two new pieces of information: A # symbol if the squad is using pathfinding, and the squad’s status string. The status string is sometimes unchanging, sometimes informative. For example, the Scourge squad has status Attack or Stand By, depending on whether enemy flyers are targetable.

• There was a minor bug in deciding which enemy unit last-seen location to visit next after all known enemy buildings are destroyed. The fix has little effect.

other ops and tactics

Smooth attack/retreat combat sim decisions over time. I think this is the single most important change. Micro is less jittery and more decisive. I’m leaving out details for today; it’s worth a separate post.

• I found and fixed several more bugs related to spell units. The most important misbehavior was that Steamhammer treated an enemy comsat scan of a base as an attack that needed to be fended off. Every time terran scanned a base, a couple of mutalisks might peel off the flock and head there to defeat the scan. They never failed!

• I added time hysteresis to the defense squads, on top of their existing range hysteresis. After the enemies have been seen off, the squad waits out a time limit before it is disbanded. I was not convinced that the feature helps play, so I set the time limit to only 1 second.

• When a cluster of units is ordered to retreat, it may decide—depending on a simplified geometry calculation involving a risk radius—to “retreat forward” to join with friends. This helps a small cluster join up with a big cluster that is already in a fight. I decreased the risk radius, except in the case where the enemy is terran and has sieged tanks. It should help small clusters retreat forward more often.

• Earlier, I introduced a bug into FAP by adding an incorrect MAX_DISTANCE constant. I had forgotten that the distances are squared. Fixed.

micro

Pathfinding for ground units for calls to the.micro.Move(), the.micro.MoveNear(), and the.micro.MoveSafely(), (but not the.micro.AttackMove()) at the option of the caller. I’ll post details tomorrow or so. It was easy to guess I would do this now, because Medusa is in the AIST S4 map pool, where Starcraft’s built-in pathfinder likes to pile up units at the blocked back doors of the bases.

• A unit trying to move safely (with the.micro.MoveSafely()) does not try to avoid interceptors, only carriers. Trying to avoid swooping interceptors causes erratic movements, not escape movements.

zerg

Lurker behavior is smarter. When I first implemented lurkers in Steamhammer 1.3 in 2017, I found that if they obeyed the combat sim like other squad units, they were nearly useless; they did not understand when to burrow, or where to burrow, or when to unburrow. The SparCraft combat simulator I had at the time did not support lurkers. Steamhammer could not tell when the enemy had detected them. So I gave lurkers spartan hyper-aggressive tactics: Always attack, burrow at max range versus a target that can shoot back and directly next to a target that cannot, and unburrow when no target is in range. I’ve made only minor refinements since, because it worked surprisingly well, especially against terran bots. But the crudeness shows, and hyper-aggressive is often hyper-stupid. A lone lurker would boldly attack a line of cannons. Steamhammer has lost a lot of lurkers for free.

Today Steamhammer is a capable squad commander, and it can judge pretty faithfully when a lurker remains undetected by the enemy and should remain safely burrowed despite (meaning because of) the large enemy force on hand. Lurkers in a defense squad remain hyper-aggressive, so that they do not hesitate to eliminate enemies from the zerg base. Other lurkers now advance or retreat together with the units in their cluster, with some exceptions for retreat (see below) so that lurkers don’t unburrow too often. Lurker play remains clumsy, but it is far more flexible than before, and better overall.

• A lurker that Steamhammer believes is undetected will not retreat as long as it is in range of a target. It will remain burrowed, or it will burrow then and there instead of retreating. I made this change in an earlier version, but it had no effect until now because lurkers did not retreat.

• Failing the above check, if a burrowed lurker is asked to retreat then there is one more check: Will it survive long enough to unburrow? It calculates an expected survival time in frames and compares it to the time to unburrow plus a safety margin. If it won’t live that long, it doesn’t bother unburrowing; maybe it can get a last shot in. I do the same calculation for sieged tanks, with the unsiege time, so that a tank beset by zealots still has a chance for a last shot at some distant dragoon.

• I fixed a bug in the hidden enemies check; it handles an invalid missing ui.unit correctly. I don’t think it makes a practical difference with the existing codebase. The hidden enemies check prevents unburrowing when the lurker is undetected and, if it unburrows, at risk of dying to a cloaked unit, or a known unit out of sight on high ground. This check often (not always) prevents the cycle: Burrow on low ground just in range of a bunker up the ramp -> bunker stops shooting because the lurker is no longer visible -> the bunker is no longer visible to the lurker -> the lurker unburrows -> the bunker starts shooting again and becomes visible -> the lurker burrows....

• When possible, a lurker, guardian, or devourer will morph out of enemy view. When within enemy static defense range, it will not morph at all—at least theoretically; I’ve seen it happen so I think the check is not accurate. Lurkers should more often surprise the enemy, and cocoons should be shot down less often.

An urgent sunken to stop vultures, or an urgent spore to stop wraiths or corsairs, is made quickly, rather than, oh, whenever the bot happens to get around to it, might be any minute now if we’re still alive then. The slowness was due to a bad design decision I made in reworking the code in the SSCAIT tournament version.

Insert a fresh overlord after a spore colony, not before, when making supply and a spore is next in the queue, and similarly for the evolution chamber prerequisite. Formerly, when corsairs ravaged the overlords, Steamhammer gave priority to getting its supply back into the green. The corsairs had only to camp the zerg base for protoss to win.

• When scourge finds that its target is very close, it attacks even if the combat sim says to retreat. Scourge uses a combat simulation, excluding air units, to avoid ground fire. Often it would be on the verge of shooting down a corsair, but a dragoon was a little too close.... Steamhammer has missed a lot of kills that way.

• The defensive sunken versus cannons is tuned slightly.

openings

• There are no new or changed openings, only a change to how one opening is configured to be used. I saw the OverpoolTurtle build, a bot exploit opening that is objectively horrible, played in one game against a human on SCHNAIL, and that was one time too many. I removed it from the matchup and counter configurations, so it should be played very rarely unless it is found to succeed, and almost never against a human.

configuration

• New flag for Crazyhammer. Setting Config::Strategy::Crazyhammer to true causes Steamhammer to choose its openings purely at random from its large library, ignoring anything it may have learned. It’s used for Crazyhammer on SCHNAIL.

• New debug flag Debug::DrawLurkerTactics draws the name of the current default lurker behavior, either “Aggressive” or “With squad”. It doesn’t convey any information at the moment, since whenever there are lurkers they are “With squad”. I plan for lurker behavior to become more varied and complex, and then the debug flag will be useful.

unused stuff

• The movement sim is included and potentially valuable, but unused. There is code in FAP and in CombatSimulation.

• Fixed a primordial crashing bug in enemy unit clustering, making it also potentially valuable. But it’s turned off for now.

Next: Pathfinding.

movement simulation

Steamhammer is nearly prepared for AIST S4. I made 3 major improvements, plus smaller ones. Not everything I tried succeeded, though. Today I want to write about a new feature I implemented that is definitely valuable, and that Steamhammer will find uses for in the future even though my first use case didn’t work out as I hoped.

Steamhammer inherited from UAlbertaBot a weakness in retreating. It is Steamhammer’s longest-standing serious weakness. The point of retreating is to get away from a fight, so the units get move commands and ignore enemies they meet on the path to their retreat point. Nearly all cases where Steamhammer’s units run into or through or past enemies without fighting are because it is trying to retreat them. There are spectacular failure cases, like running ultralisks through a field of cannons and losing them for nothing. My past attempts to fix it have been half-hearted, since I want to delay coding a full tactical analysis until later. I thought I’d try something more courageous.

So I implemented retreat simulation in FAP. Moving is a lot easier than fighting, and it didn’t take much code. Pass in a target position, and our simulated units move to the position while the enemy’s keep chasing and shooting as usual, with no change to their side of the simulation. We’ll say the retreat “succeeded” if the fleeing units’ losses were lower than some percentage.

My idea was: If the regular combat sim said to retreat, then I ran a retreat sim in the same frame. If the retreat failed, then don’t run away, keep fighting regardless. The retreat sim runs fast because it is simpler, and also I cut its duration to only 2 seconds. I didn’t meet any problems with the frame time limit. In principle, the retreat sim can conclude “the cannon will kill us even if we run away, so keep hitting it,” and notice when the retreat path is blocked, and distinguish a faster force that can escape from a slower force that the enemy will chase down (yes the speedlings will kill your marines for free if they run, better to make a last stand).

In practice, the retreat sim might be worth it on average, but I was not convinced. The sim results were as noisy as ever, and there were times when it hurt. Also, in some situations it’s conceptually the wrong thing to do. Having the “retreating” ultralisks fight cannons is better than running past the cannons, but better yet is to understand the situation as an attack rather than a retreat, and to stand aside until you can win. And that seems to call for the tactical analysis that I was saving for later.

I called the internal routine in FAP not retreatsim() but rather movesim(), because it can answer many questions about when movement is safe.

  • Will this runby succeed in running by?
  • How far can the transport go before it must unload?
  • How many scourge will survive to hit their target?
  • Will this plague/broodling/etc. succeed, or will the caster be killed first?
  • If I mind control that unit, will it live to join my army?

And with a little more elaboration, questions like

  • If it lives to cast, will the defiler/queen/etc. also escape safely?
  • Is there time to unburrow/unsiege, or should the immobile units remain as they are?
  • Can the mutalisks pick off another marine and still get away without losses?

I think it’s a safe bet that Steamhammer will be answering some of these questions before AIIDE this year.

new bot Bobot

We got another new bot! Newcomers have been thin on the ground of late, we must treasure them. Terran Bobot’s description says “little bot I made to have fun and also to go into machine learning in the future. By william sokol”.

“Bobot” seems to be a popular name that has been used by unrelated projects. I found a repo of a BWAPI bot named BOBot by Antohi Mihai Razvan, which appears to be a different bot, and much older (it uses the old problematic BWTA map analysis library, while our Bobot uses BWEM).

Bobot has lost nearly all games so far, which is not uncommon for a brand-new bot made from scratch by an inexperienced author. It did defeat Marine Hell a couple times on BASIL, winning on points when the games timed out. Every bot author who sticks with it soon starts to win games. Bobot seems to have a basic idea of how to build a base and produce units: It makes barracks, academy, natural base, and 2 factories, then produces marines and tanks. I haven’t seen it get any medics or academy upgrades; the academy is apparently for scan and nothing else. It does not research siege mode for the tanks. Bobot doesn’t scout for the enemy, and apparently doesn’t actively attempt to attack enemy bases. Its combat skills appear to be limited to rallying units in front of its base and then, when enemies appear, attack-moving in their direction. In the best case, its units may be lured to an enemy base and destroy it.

Overall, Bobot looks like it is very early in development. This may be a test upload to make sure it runs, before the author gets down to serious work.

One of Bobot’s better games is Bobot vs GarmBot by Aurelien Lermant on Destination. Bobot lost by crashing, but its units were lured into destroying a number of zerg bases first.

Be sure to have fun, William Sokol! I look forward to seeing what machine learning ideas you try.

another Steamhammer-Krasi0P proxy game

Back in last July and August, I wrote up a couple of Steamhammer-Krasi0P proxy games, proxy vs proxy just to show off the new skill, and proxy vs turtle, which was a festival of absurdity. A few days ago, Steamhammer played another proxy hatchery against Krasi0P, this time one that looked almost tailor-made for the opponent. In reality the build was tailor-made to defeat TyrProtoss’s cannon-and-build-up-at-home strategy, although Steamhammer has never tried it against TyrProtoss. It was sheer coincidence that it was also suitable to counter Krasi0P’s cannon-contain strategy (not its cannon-at-home strategy this game). Though I should add, I keep specialized builds around in part to discover coincidences like this.

both proxies are up

The game is already 6 minutes old. Krasi0P, seeing the proxy, built only one containing cannon, which you may be able to make out as a purple dot on the minimap. That may be why protoss has extra minerals. Steamhammer, when it saw the cannon in turn, decided not to take its natural but to send a drone to the upper left, where it took a “hidden” base. Steamhammer does not want to be stuck inside a containment, it wants to be on both sides, and here it forgot that it already was on both sides. Protoss chose to tech up and defend itself with more cannons before moving out with its first dragoon, and zerg is just starting its first sunken with good timing. The timing is an accident, since the build was made for a different opponent playing differently!

hitting that pesky cannon

Steamhammer made lurkers and, before anything else, sent them to clean up the isolated containing cannon. It would have been more efficient to send the hydras before morphing them into lurkers....

moving in for the kill

Steamhammer added more sunkens and a spore to prepare for TyrProtoss’s move out with a large army including an observer. Krasi0P did not have a large army or an observer, and soon fell to three hatcheries worth of zerg onslaught (on the minimap you can see units approaching from both other bases). Krasi0P seemed to try to adapt by cutting its containing cannons down to one, but did not understand the situation and reacted slowly. Steamhammer didn’t understand either, and only chose an appropriate plan by chance, but did at least execute its build efficiently.

They aren’t visible, but in each picture, outside the frame, there are drones mining at the proxy. Even after making 4 sunkens and a spore, 3 drones remained to mine there. Another funny game between these two.

the kitchen sink

A SCHNAIL game. This human player was able to cope with every aggressive trick Steamhammer tried, and it tried several, but not able to keep up macro at the same time.

finishing moves

Zerglings and ultralisks are under the dark swarms, invulnerably shredding everything. Of the ensnared marines to the left of the swarm, the majority are also plagued. Only the most recent spell shows.

When its economy is strong enough, Steamhammer switches quickly from midgame play whose goal is to hold on and not fall behind, into the endgame with power moves. As a human player, how does it feel to face so-so play for most of the game, holding after no worse than a bit of a scramble, and then be abruptly overwhelmed by a prodigious army that seems to have all the tech and be able to do everything simultaneously?

In the following game between the same players, the human was the aggressive one, moving out and repeatedly destroying zerg expansions. Steamhammer never built up its endgame army. But with the terran army away from home, mass zerglings overran the terran bases. Keeping up with AI macro is mechanically demanding for the human player. I expect it’s good practice, though.

AIST S4 prospects

Here are the participants in AIST S4 sorted by BASIL elo, with the latest BASIL update as a guide to gauge how much the bot may yet be updated by the submission deadline of 15 March. If it has been a long time since the last update, the author may have taken the opportunity to make big changes, so the elo is a less reliable guide.

boteloupdate
Stardust307229 Jan
BananaBrain29401 Mar
PurpleWave287714 Jan
Steamhammer27281 Feb
Dragon26871 Oct
WillyT256526 Feb

Elo is pretty good, but even the best forecast can’t predict the results of a knockout tournament with random seeding. If we take elo as perfect, then any bot in the bottom half might get a tough pairing in the first round and fall to the loser’s bracket, then face another tough pairing. In reality elo is not perfect and even a top player might be knocked out early with bad luck in the BO3 matches. Also, 6 players do not make an exact power of 2 bracket, so I expect that some bots will get a bye the first round and the remainder will have to play an extra match, facing a longer road. The bracket works out if 2 bots get a bye the first round, so that 4 play in the first round and 2 drop to the loser’s bracket. Then round 1 and round 2 of the winner’s bracket both have 4 players.

Nevertheless, Stardust is clearly the favorite to win. We probably have a good handle on BananaBrain’s strength, since it was updated only a few days ago. PurpleWave I hope will have fixed the issues that harmed its performance in SSCAIT—unfinished improvements, we’re told—so it may be able to pass BananaBrain and meet Stardust in the final. Dragon may or may not get a big update, since it has not been reuploaded since October. If it does, it might suddenly become a contender.

Steamhammer, I don’t mind saying, has important updates and should play a more lucid game than the current BASIL version. On the other hand, I have stayed true to my intention of treating the tournament season as over—although it’s not—meaning that I disfavor safe improvements and favor work on basic infrastructure and new features that may need tuning. I don’t have time or resources to test intensively, so I accept risks of bugs and unexpected consequences and poor tuning. Either way, success or failure, Steamhammer may cross the plans of any bots that try to prepare specifically against it. I think my odds are good of, at last, avoiding the bottom spot in AIST. And I hope to beat one of the protosses.

low level bugs

In its first draft, a new feature in Steamhammer caused a crashing bug. After several runs to collect information, it looked like a bad pointer or a memory corruption bug. Thank you C++, we enjoy unsafe languages above all others. With more poking, I identified a memory safety issue and realized that it would take some rejiggering to fix. It took me several hours to do minor rewrites up and down long call chains and verify that I hadn’t missed any links. Finally the code was tight, everything was double-checked, it compiled correctly, and I ran the first test—and it failed with the exact same symptoms. It happens to everyone, right?

Well, the show must go on. I configured tests to all run on one map for efficient debugging, and brought out the loupe for a close look. It took me three days, I wrote self-test code to catch errors as early as possible and extract their secrets, I had to learn (what seems to me) Lovecraftian C++ arcana that I would prefer not to know the existence of much less to understand, but I solved it, and kept the code nice and efficient too, and my new feature came to work perfectly on the test map in a wide variety of situations. That took a lot longer than writing the first draft. OK, now to test more widely. On the very next map—the self-test immediately caught errors. It happens to everyone, right? Right? Right? Cue “They’re Coming to Take Me Away, Ha Ha!”

This bug turned out to be trivial, though.

I once worked with an intern who wrote the worst code I have ever seen. Not only was it tangled and barely decipherable, it was ingeniously self-modifying, and not with address variables but with hardcoded address constants so that it depended on running at a fixed address... even though the software was planned to be burned into ROM. Unbelievable but true. My first step in fixing it was to throw it out. I guess I should be proud of myself as a stable and careful programmer, since I still have some hair and I rarely lose days of time to low-level bugs.

But I have to say, if I wanted to have fun in an unsafe low-level language, I would use a fun unsafe low-level language, like FORTH.

tuning against humans

Check out this event in the game webgames vs Steamhammer on Sylphid from SCHNAIL.

mutas over the barracks

Human players of terran and protoss normally group their production buildings. It’s pretty much a requirement for efficient macro. It also introduces a vulnerability: If the opponent maintains control over the area of your production and you don’t already have enough army to force them away, then you are in deep trouble. Any new units you produce will die, so you cannot regain control of your production. Here, terran has built turrets to protect its main and natural mineral lines, but none next to the barracks, an oversight that invites attack.

In the picture, I judge that Steamhammer does not hold enough control over the terran barracks to win, even though the selected barracks is in the red and in danger of burning down. It did hold that much control earlier and had strong winning chances, but frittered it all away: Mutas attacked the barracks, then were distracted away, then attacked the barracks again, then were distracted, and so on, losing mutalisks and allowing the marine count to build up. Bots rarely group their production buildings, so I never taught Steamhammer about the leverage of controlling the enemy production. In the picture, the mutas are about to be distracted away for the last time. Terran repaired the burning barracks, had unhampered production, and won easily with their huge economic advantage.

Distractibility is an issue in itself. I need to improve tactical target selection so that it doesn’t change its mind so frivolously. The skill of controlling the enemy production is a harder question for me, from a development point of view. The skill would be just as valuable in bot-vs-bot games when it applied, but it would not apply often. I wouldn’t be tuning specifically against human play, but it leans that way.

There are specific anti-human skills that don’t help against bot opponents. Humans often try to overwhelm the opponent’s multitasking with threats and feints and multi-point attacks. Multi-point attacks in themselves call for multitasking ability. Bots can do the same much better. I don’t have any plans to tune to exploit human weakness in multitasking, though I suppose I may come around to it in the distant future.

follow-the-leader doesn’t always work

Steamhammer divides its units into squads (like many bots), and gives each combat squad orders to try to reach a target position, such as an enemy base. The system is inherited from UAlbertaBot. A squad may have units all over the map, such as some at the front line and some just spawned, and units in different places face different situations. So Steamhammer divides a squad into clusters of units and tries to make appropriate decisions for each cluster, depending on whether enemy units are near and so on. The clustering has been in place since fall 2018; see Steamhammer 2.0 squad unit clustering for the description, which is still up to date in everything it says, though some details have changed behind the scenes.

I want Steamhammer’s clusters to join up when they can, so that the army fights together. So it uses a follow the leader rule: The frontmost cluster, the one closest to the target position, is the leader. Other clusters, if they are not distracted by enemies nearby, try to join up with nearby clusters that are more forward, and ultimately with the leader. In other words, clusters don’t independently seek out the target position; their first priority is to merge into a big scary army that will win. Compare Steamhammer’s tactical habits with, say, Dragon, which feels freer to scatter its units across the map.

Thinking about nydus canals, I noticed that following the leader doesn’t work in all cases.

sketch of situation with nydus canals and enemy attack

The enemy is attacking a base with a nydus. A squad is ordered to defend. Cluster 1 is closest, because it can jump through the nearby nydus to reach the base. Cluster 2 does not want to follow the leader; it wants to travel by ground, not by nydus, because that way is closer for it. Cluster 2, to follow its defense order, should attack (or at least threaten) the enemy from the rear.

Nydus canal is not the only way to break follow-the-leader. Imagine a target position with two separated entrances, like an edge base on Fighting Spirit. If two clusters find themselves at different entrances and want to attack, each should probably attack its own entrance.

I don’t know how I’ll end up fixing this. There is time to think about it. A related and more immediate issue is how to attack the same enemy position from two directions at once, which Steamhammer currently can’t do except by accident. I’ll solve that by analyzing which clusters are attacking the same enemy units and doing a joint combat sim, so that all attack together or all retreat.

AIST S4 prep

Today I received my acknowledgement that Steamhammer is registered for AIST S4. It will play, if the creek don’t rise.

I haven’t been posting about Steamhammer progress because I’m preparing (ssh! it’s a secret!) Secret Improvements. When the list of participants is announced, I expect I’ll make special arrangements for some of them. Steamhammer should be a moving target so that the same doesn’t happen to it.

The improvements I’ve made already are significant, and I’m finishing up another good one today. I’m hoping that, unlike the other two times Steamhammer participated (S1 and S2), it won’t be knocked out at the first opportunity and end up in last place, or tied for it, with 2 match losses. (In S3, the rival Microwave played instead and got the same result.) I don’t want to get used to being pushed over like a cardboard cutout.

I’m also hoping that by submission time at the ominous Ides of March, Steamhammer will have collected enough opening data that I can decide what to do about it next. I’m still forecasting that I’ll need a second collection phase to fill in gaps. Plus I’ll need a long-term plan to keep the data updated as Steamhammer’s skills progress.

practice games on SCHNAIL

Recently a not particularly skilled protoss played more than 20 practice games versus Steamhammer on SCHNAIL, winning 3 and losing the rest. I went through the games in sequence. Steamhammer put its variety of builds to good use, sometimes 4 pooling, sometimes busting with one or another kind of all-in, sometimes defending then countering, sometimes building up then rolling everything down with hive tech. I was mostly pleased, though tactical clumsiness was an issue. In the most spectacular game, protoss aggressively attacked, destroyed and held the zerg natural, built a gateway and nexus there and started mining the zerg’s minerals, and found and cleared the only other zerg expansion. Steamhammer held its main with sunkens and units, used an escaped drone to lay down another expansion that protoss did not find, teched to lurkers, pushed down its ramp to free its natural, defeated the dragoon army, and won with the one big counter. That must have been frustrating for the human player, who apparently did not have the multitasking ability to both micro the dragoons and get observers in time (a difficult skill to learn, if you ask me, because multitasking is not natural for humans). Yet game after game followed.

Looking at it from the human player’s viewpoint, protoss started with a not very convincing gateway and forge build with no cannons. After Steamhammer easily busted it a few times, protoss started experimenting with other plans. Some were defensive, like holding with cannons and trying to expand by shuttle. The more successful were aggressive, like the game above. Over the long sequence, I thought I could see the human player’s skills slowly sharpening, with more consistent macro and more appropriate strategies. Stronger players quickly notice Steamhammer’s weaknesses and exploit them, but this was a player who hadn’t reached that level of knowledge yet. It takes experience to build skills, you have to work through all the stages.

SCHNAIL is great for this, I conclude. If you can find a bot at your strength level or above, but not hopelessly above, and with some variety in its play, you may have a good practice partner. You can polish your basics and learn what kinds of plans work and try out new ideas. If you find yourself winning most games, then I guess either you’ve improved that much or else you’ve learned to exploit the bot’s weaknesses. I’d say if you want to keep improving, it has become time to look for your next opponent.