archive by month
Skip to content

a latent bug in squad updating

CombatCommander::updateIdleSquad() does this:

    for (const auto unit : _combatUnits)
    {
        // if it hasn't been assigned to a squad yet, put it in the low priority idle squad
        if (_squadData.canAssignUnitToSquad(unit, idleSquad))
        {
            idleSquad.addUnit(unit);
        }
    }

The code is correct as it stands. It cannot assign 1 unit to 2 squads. But I consider it a latent bug: Only the lowest-priority squad can be updated this way. It would be easy and natural to make changes to squad priorities and cause an error, or to copy and paste this code under the assumption that it would be correct in a different case. You would get an irritating bug with no obvious cause. In the spirit of defensive programming, I recommend changing it to this:

    for (const auto unit : _combatUnits)
    {
        // if it hasn't been assigned to a squad yet, put it in the low priority idle squad
        if (_squadData.canAssignUnitToSquad(unit, idleSquad))
        {
            _squadData.assignUnitToSquad(unit, idleSquad);
        }
    }

assignUnitToSquad() ensures that the unit is removed from any previous squad before it is added to the new squad. Any squad other than the lowest-priority squad might steal a unit from another squad, and you can’t steal it without taking it away. Assigning 1 unit to 2 squads would cause problems, and Steamhammer checks later and will throw.

At a minimum, I recommend commenting the code so you don’t copy-paste it heedlessly when adding a new squad type.

Randomhammer-ZurZurZur game with vulture-wraith opening

Randomhammer terran played its first public game with the vulture-wraith opening against zerg ZurZurZur. As I have mentioned, I wrote vulture-wraith originally as an extra test of Steamhammer’s anti-factory zerg opening. I found that the vultures and wraiths bring terror to unprepared zergs, so I made it a permanent part of the terran arsenal.

ZurZurZur, it turned out, was not prepared at all. It could not get a sunken colony up, and it did not get hydralisks into the fight until its natural was destroyed. By then it had lost all overlords so it could not reinforce and the hydras were quickly defeated. I can see how Tscmoo terran does so well with a similar strategy.

ZurZurZur loses its natural

I’m working on zerg stuff now for the upcoming tournaments, but I have a note to revisit this opening. It shows its legacy as a test opening; the execution and followup could be much stronger.

Next: A latent bug.

Steamhammer 1.4.4 change list

Steamhammer bugfix version 1.4.4 is uploaded to SSCAIT. It has only been a week since the last version was uploaded, so the changes are not many. The bug fixes probably mean slightly less inexplicable erratic behavior. Zerg is slightly smarter because of a few small tweaks, but it may not be easy to tell.

After rechecking the CIG publication dates, the next public release may not be until October, after AIIDE submission closes. We’ll see; there is plenty of time before then for me to change my mind.

the Bruce bugs

The bugs found by Bruce Nielsen (Locutus) and related fixes.

BuildingManager::validateWorkersAndBuildings() copied building objects unnecessarily.

BuildingManager::cancelQueuedBuildings() and BuildingManager::cancelBuildingType() unsafely deleted buildings from the _buildings vector while looping through it.

Squad::releaseWorkers() had a similar deletion bug.

MapTools::nextExpansion() had a typo that caused it to do a check with the wrong coordinates.

other code changes

UnitUtil::GetAttackRange() and UnitUtil::GetAttackRangeAssumingUpgrades() mistakenly returned the weapons range of a reaver or a carrier as 8 pixels, rather than 8 tiles (8 * 32 pixels). “We’re perfectly safe at this distance, folks!”

• I am reworking DistanceMap as the foundation of a range of classes that keep a number for each tile of the map. A new base class Grid provides the basic data structure, initialization to a constant value, and lookup. DistanceMap is renamed to GridDistances. I wrote a new class GridAttacks as a start on a kind of influence map; some code is there, but it is not yet tested or used, so exercise care.

• Removed the unused BuildingData class, and the unused method MicroManager::calcCenter().

• Removed more unnecessary includes.

zerg

• If an expansion hatchery fails to build, the building manager turns it into a macro hatchery instead. It prevents Steamhammer from sending drone after drone to expand and losing them all. It’s a low-level trick instead of the high-level tactical safety analysis that should be done, but it’s simple and it works moderately well.

• Versus protoss, count only corsairs, scouts, and carriers as reason to make devourers. Steamhammer played a game in which it made devourers to combat the enemy’s large number of observers. “I know how to beat that plan!”

• On 2 player maps, increase the chance of playing rush-safe ZvZ openings. Also rejiggered fast pool chances (suggestion by Antiga). A number of other probabilities are adjusted, mostly to make more diverse choices so that learning has data to work with.

new Steamhammer plan: win

In AIST S1, Steamhammer got to play only 5 games and lost 4 of them. It was a sad showing, though the field was strong. CIG is coming soon, and Steamhammer is registered. Then AIIDE is after that. Also, as I have mentioned, Steamhammer is unbalanced in its play skills, much stronger in strategy and macro than in tactics and micro. Looking at results in the past few months, I’ve concluded recently that it is so unbalanced that improving strategy no longer helps at all. When I fix a strategy weakness, even one which demonstrably loses games, the benefit is so small that there is no detectable change in skill, either objectively in elo or subjectively in my judgment. The imbalance is so extreme that nearly all losses, even in games affected by strategy problems, are due to mistakes in tactics and micro.

That means that if I work on the opponent model and opening selection as I planned, I won’t be able to tell if am doing a good job. No matter what I try, it will hardly affect results. It also means Steamhammer is at risk of getting whomped in CIG and AIIDE.

I have made a bunch of bug fixes recently (thanks Bruce!), so here’s my new plan. I’ll release 1.4.4 shortly with source, and to save time I’ll skip releasing the source of 1.4.3. (It takes more effort than you might imagine.) 1.4.4 includes bug fixes (one of which hasn’t been mentioned here), plus a start on an influence map for tactics, plus a zerg strategy fix that may save 1 game in 200 and a zerg tactics change that is more likely to save 1 game in 15 or 20. I think it’s all stuff that people should have.

Then the next public release will be after the CIG deadline next month (CIG releases source itself), and the following one may be after the AIIDE submission deadline in October. I will buckle down and make the changes that improve play the most in that time.

Steamhammer bug fixes

Yesterday was a productive day for fixing bugs, thanks to info from Bruce Nielsen, author of Locutus. See comments to Steamhammer 1.4.3 change list. Because tournaments are coming up, it may be a while before I release another public version of Steamhammer, but bugfixes are important for everyone with a fork, short of the devil.

After fixing these bugs, I can no longer reproduce the “wrong reserves” error messages that the building manager used to kick out several times per game. Other mysterious misbehavior is probably fixed too, though I’m not sure what.

BuildingManager shouldn’t copy buildings

The methods BuildingManager::validateWorkersAndBuildings() and BuildingManager::checkForCompletedBuildings() loop through the vector of Building data structures and delete those that are no longer needed. To avoid deleting from the vector of Building objects while in the midst of looping through it, it stashes the buildings to delete in another vector toRemove and passes toRemove to a deletion method.

    std::vector<Building> toRemove;

Well, it’s copying the building data. I haven’t uncovered any bug that this causes, but it is wasteful at best and at worst an invitation to errors. If code updates the copies, or updates the real buildings before the copies are sent in for deletion, no real building or the wrong real building might be deleted because of how equality is defined in the Building class. undoBuildings() has to undo data structure dependencies and could easily make such a mistake.

There are several steps to the fix, but the idea is to keep a vector of references instead of a vector of copies. To keep references in a Standard Template Library container, we have to uglify the type signature with a wrapper.

    std::vector< std::reference_wrapper<Building> > toRemove;

The rest of the code doesn’t change, only the declaration. Then we also have to update the declarations of the deletion routines that undo data structure dependencies and perform the deletion.

    void undoBuildings(const std::vector< std::reference_wrapper<Building> > & toRemove);
    void removeBuildings(const std::vector< std::reference_wrapper<Building> > & toRemove);

canceling buildings is incorrect

BuildingManager::cancelQueuedBuildings() and BuildingManager::cancelBuildingType() also delete unwanted buildings, and do it while looping through the vector of buildings. It is unsafe and can cause errors. It’s my fault, I wrote these. I rewrote them like this:

// It's an emergency. Cancel all buildings which are not yet started.
void BuildingManager::cancelQueuedBuildings()
{
	std::vector< std::reference_wrapper<Building> > toCancel;

	for (Building & b : _buildings)
	{
		if (b.status == BuildingStatus::Unassigned || b.status == BuildingStatus::Assigned)
		{
			toCancel.push_back(b);
		}
	}

	for (Building & b : toCancel)
	{
		cancelBuilding(b);
	}
}

and

// It's an emergency. Cancel all buildings of a given type.
void BuildingManager::cancelBuildingType(BWAPI::UnitType t)
{
	std::vector< std::reference_wrapper<Building> > toCancel;

	for (Building & b : _buildings)
	{
		if (b.type == t)
		{
			toCancel.push_back(b);
		}
	}

	for (Building & b : toCancel)
	{
		cancelBuilding(b);
	}
}

releasing workers from a squad

I realized I was not sensitive enough to this category of bug, and surveyed the codebase to see whether there were any more. Almost all risky loops were correct, whether written by Dave Churchill or by me, but I found 2 more. One was inconsequential because it was in the BuildingData class, which is unused (so I deleted it). The other was in Squad::releaseWorkers() and should be fixed.

// Remove all workers from the squad, releasing them back to WorkerManager.
void Squad::releaseWorkers()
{
	for (auto it = _units.begin(); it != _units.end(); )
	{
		if (_combatSquad && (*it)->getType().isWorker())
		{
			WorkerManager::Instance().finishedWithWorker(*it);
			it = _units.erase(it);
		}
		else
		{
			++it;
		}
	}
}

finding the next expansion to take

Bruce also pointed out an unrelated typo in MapTools::nextExpansion(). It's a serious bug. Here is the Locutus commit fixing it.

Change this:

        for (int x = 0; x < player->getRace().getCenter().tileWidth(); ++x)
        {
			for (int y = 0; y < player->getRace().getCenter().tileHeight(); ++y)
            {
				if (BuildingPlacer::Instance().isReserved(x,y))
				{
					// This happens if we were already planning to expand here. Try somewhere else.
					buildingInTheWay = true;
					break;
				}

To this:

        for (int x = 0; x < player->getRace().getCenter().tileWidth(); ++x)
        {
			for (int y = 0; y < player->getRace().getCenter().tileHeight(); ++y)
            {
				if (BuildingPlacer::Instance().isReserved(tile.x + x, tile.y + y))
				{
					// This happens if we were already planning to expand here. Try somewhere else.
					buildingInTheWay = true;
					break;
				}

x and y were treated as absolute map coordinates instead of relative to the building location, so the check gave completely wrong results. The next check, immediately below, is correct; the bug is a typo. But the social status of the bug does not matter. What matters is that it broke stuff.

Proxy updated; Locutus vs. Krasi0

updated Proxy

New bot Proxy is—I can almost add “of course”—updated already. The obvious change is a zergling build if the opponent is zerg; the hydralisk build was too slow for ZvZ. The zergling build is successful, and Proxy’s elo has climbed over 2000. It is now an average bot, maybe better than average. That is excellent for a newcomer.

Locutus and minimum tank range

When Locutus runs dragoons by Krasi0’s bunker, Krasi0 (in games I’ve seen by the current version) has 1 siege tank available as its main interior defense. Terran sieges the tank and Locutus loses its dragoons, doing less damage than it should.

dragoons keeping their distance

In a broad sense, this is why I haven’t implemented runby in Steamhammer. Running by fixed defenses is easy, but playing well after you have run by is not so easy. The runby units become desperadoes, expecting to die and seeking to deal as much destruction and distraction as possible until then. Steamhammer’s units normally retreat from too much danger; desperadoes may be able to retreat, but can’t count on it. They have to make different decisions about what to shoot at, when to run away, and where to run to.

In this case, the important thing to shoot at is the tank, which limits the dragoons’ freedom too much. The dragoons should rush inside the sieged tank’s minimum range. They’ll win the fight and live to cause more trouble. Dragoons that wander too far off or take potshots at SCVs are not contributing as much as they could.

I think it’s complicated to handle all the defenses terran might try. I guess it’s a matter of taking one step at a time.

the ordeal of Sparkle

Mark Twain advised writers that every time you want to write “very” you should put “damn” instead: “Supporting Sparkle is damn hard.” Your editor, he said, would remove the word “damn” and your writing would be as it should. Well, I don’t have an editor. Supporting Sparkle is damn hard, and my writing is as it should be.

When I first looked at the AIST competition, I posted “I will have to spend time surviving tricky map features and won’t have much for thriving with the important play features like mineral-walking drones into the third world, or linking up island bases with nydus canals.” I was right. Steamhammer can survive on Sparkle and Third World, but it doesn’t have the skills to play the maps as they were meant to be played. I did not have the time to implement them. That’s the theme of this post: Not enough time.

the Require: feature

Since Sparkle requires specialized openings, I wanted to implement at least Require: { “map” : “Sparkle” } for openings in the configuration file. See yesterday for the many ways in which my plan was flawed. I invested too much time before I realized that it would not pay off, and I had to put in a hack and move on to other stuff. It was not fun. I’ll rethink this completely for the next version.

the opponent model

You can’t get 5 pooled on an island. You don’t have to do anything special if the enemy makes a fast factory—you expect that on an island map. The plans that Steamhammer knows how to recognize don’t matter on island maps, and I had neither time nor test opponents to work on recognizing island plans, much less to counter the plans. It made more sense to short-circuit the plan recognizer, or you might say, to correctly recognize that none of the known plans mattered.

Still, Steamhammer does know 3 Sparkle openings, and it would have been nice to rely on the opponent model’s opening selection to pick the ones that worked better. But that was problematic too, because of how the opponent model tries to generalize across maps. Most maps you can generalize across: If this opening worked well against this opponent on Circuit Breaker, it has a good chance to work on Roadrunner too. But island maps, and concept maps in general, call for specialized choices; you can’t generalize from a concept map to any other map, or vice versa. I’ll remember that when I work on opening statistics, one of the next planned features. I learned that Steamhammer should be able to measure how much a map is like other maps, and generalize appropriately.

island transport

When I surveyed the AIST S1 map pool, I thought: Hmm, Sparkle is an island map and Third World is a kind of semi-island map where workers can pass between the map halves by mineral walking. The important skills are mineral walking, drop, and placing nydus canals. What combination of skills should I aim for first? Drop is the most flexible and the most useful overall, but it will take a ton of work to implement in Steamhammer because the existing infrastructure is weak. Also drop is slow and awkward for moving units around. Mineral walking drones is comparatively easy, and it is useful on a modest variety of maps. Also mineral walking doesn’t require research; when you can use it at all, you can use it from the start of the game. Nydus canals are the best way to move units between islands in the late game, they’re useful on almost all maps, and they’re related to pathfinding which I want to work on, so now is a good time to work on nydus. Plus Sparkle provides neutral zerg buildings on islands, so zerg can create nydus connections without needing drop. Mineral walking and nydus were my choice; drop is not required for zerg to play either map.

As it turned out, I didn’t have time to implement either feature. Steamhammer is restricted to its starting island and cannot move land units to another island. Zerg will win with air power, or it will not win. On Third World, Steamhammer is restricted to the starting half of the map, which has only 5 bases including the enemy main and natural. It’s disappointing.

island strategy

Ideally, I wanted to provide an air strategy with an optional switch into a late game ground strategy of securing islands with hydralisks by nydus. As the deadline approached, I foresaw that I would not have time for nydus, so I spent a few days getting the air strategy tuned up. I optimized the 3 Sparkle openings (1 hatchery muta, 2 hatchery muta, and 3 hatchery muta) until improvements became small. Once I realized that I could not finish the Require: feature and that the opponent model had to be more or less turned off, I simply set each opening to be played 1/3 of the time. It’s as good a guess as I can make.

At first, the mutalisks were uselessly weak. To get anywhere, I had to make changes to the strategy boss to give it some understanding of island maps. First, correct the unit mix so Steamhammer sticks with air and doesn’t decide to switch to lurkers or ultralisks, which without drop or nydus have no offensive value. Even after that, Steamhammer often lost against the weak built-in AI. I continued: Reduce ground upgrades and eliminate unnecessary research to save gas for mutalisks, and get air armor upgrades. Now Steamhammer can defeat weak or unprepared opponents. Improving mutalisk and scourge micro would help more, but that would have taken way too long.

In Candide’s best of all possible worlds, meaning several versions from now, Steamhammer should support drop and have the option of following a ground strategy on an island map. Ideally.

ground units

Steamhammer still tends to make many zerglings with its leftover minerals, which it accumulates because it has too many drones for what it is doing, which is because it doesn’t understand that it is stuck on one island. Well, its economy control skills are lacking overall; it only wants to make drones up to a limit depending on the number of mineral patches and geysers it has. The zerglings are at least useful for keeping an eye on the island and stopping drop attempts.

At first, the zerglings tried to reach the enemy base, piling up at the island edge. I expected that; it is part of what MapPartitions is for. I updated the tactical analyzer (primitive as it is) to only send ground squads to places they can reach. Now the zerglings understand that they can’t get to any enemy base, so they “explore the map,” running back and forth between corners of the starting island to keep an eye on things. Incursions should be spotted right away.

There is a bug with assigning air units to a ground squad. Despite a fix in the previous version, it still happens sometimes; it must be a second bug. I’ve seen guardians assigned to the ground squad when they should not be, which on Sparkle causes this weird behavior: The squad is sent as usual to explore the map, but because it includes air units it knows it can explore the entire map. The zerglings again gather at the edge of the island, while the guardians slowly drift from empty point to empty point. The bug doesn’t cause much trouble on land maps, but on islands it’s serious.

Steamhammer still makes defensive sunkens as usual when it sees a scary enemy ground army. It doesn’t know whether the army might appear at its base.

At some point in the 1.5.x series I will make squads more dynamic. I plan to make it illegal for a squad to have ground units in more than one map partition; if a ground unit moves from one partition to another, it will be put into a different squad which belongs to that partition. Ground units in transports will belong to a different squad yet. Squads won’t have to worry about their units being stuck in separate places, and should have an easier time making decisions.

island scouting

Steamhammer’s original Recon squad is a small ground squad that scours the map for enemy bases, and when it finds one, may attack if the base is poorly defended. On Sparkle, Steamhammer only found a new enemy island base when it happened to stumble across it while doing something else. I changed the squad assignment so that on an island map, the Recon squad consists of 1 overlord. Before overlord speed finishes, the overlord moves so slowly that its recon target often times out before it gets there, switching the overlord to a new destination, but the overlord still crisscrosses the map and sees stuff that wouldn’t otherwise be seen. Once speed is done, the overlord scouts nicely. Steamhammer has no overlord survival skills, so the overlord will happily fly over turrets and generally has no fear. I’m hoping that the scouting info makes up for the vulnerability. But in test games, Steamhammer loses overlords at a high rate.

destructible blocking buildings

The low ground natural base on each starting island has a psi disruptor building on top of the geyser. Every race can build a refinery building under the psi disruptor, but none can mine the gas until the psi disruptor is destroyed. I found by experience that building an extractor before destroying the psi disruptor was a poor idea. First, the extractor apparently sticks out beyond the edges of the psi disruptor at some points, so that zerglings attacking the psi disruptor have to choose their target carefully and it takes longer to finish off the neutral building. Second, when the psi disruptor is destroyed, usually—though not always—the extractor is destroyed at the same time. I’m not sure how that happens, but it explains why I’ve always seen pros destroy the building first and take the gas after.

Yesterday I described Steamhammer’s new multi-part system to destroy the psi disruptor, and how the openings are adjusted to get the right number of zerglings at the right time to take the gas when needed. It’s complicated and it is needed only on occasional concept maps like Sparkle.

the geyser with a crystal on it

The high ground natural has a geyser with a big crystal on it. Only zerg can mine from that geyser. Steamhammer does not believe that it can take this gas, and never tries. I have a theory about why, but I haven’t looked into it; I chose to deal with the destructible buildings first, and ran out of time. It may have been the wrong decision. Maybe I should have skipped the destructible psi disruptor and figured out how to take the crystal gas.

As zerg, Steamhammer should be able to mine 3 geysers on its starting island. It is only smart enough to get 2.

taking the high ground natural

BWTA believes that the high ground natural base does not exist; it does not locate a base there for any race. Maybe it understands that the neutral zerg lair spreads creep which blocks the base placement. If so, it is a mistake: Zerg can build on the creep and mine the gas, and terran and protoss can either destroy the neutral lair and wait for the creep to dissipate, or (since they can’t mine the gas) locate a base on the opposite side of the minerals. Pros commonly take the opposite side of the minerals. To figure all this out needs a sophisticated understanding of the situation.

Steamhammer’s map analysis doesn’t understand the base either. It doesn’t realize that the neutral lair spreads creep, and tells all races they can build a base in the normal spot. Or, in some starting positions, near the normal spot; it sometimes displaces the base (maybe there’s an off-by-one bug). Since Steamhammer will be playing zerg in AIST and no other map has the weird features, it was good enough and I left it alone.

Steamhammer recognizes the neutral lair as a “blocker” that should be destroyed, but does not destroy it. I think the bug happens because of a use of BWTA that I didn’t remove. It does no harm in practice, so I left this one alone too.

I decided to do all this stuff and I’m not convinced that it was worth it. I could have concentrated on Third World instead. I would now have the mineral walking feature at a minimum, and it could share some pathfinding code with nydus canals. On the downside, I would have done less to improve Steamhammer’s play in AIST, because moving Sparkle from can’t-win to can-win is a bigger jump than moving Third World from can-win to plays-nicely. On the upside, I would have had a feature that is more widely useful. I feel some regret. I could have chosen to play somewhat better on a variety of maps, and instead chose to play substantially better on one map, Sparkle, that may never matter again. And that substantially better play is still painfully weak.

Steamhammer 1.4.3 uploaded to SSCAIT

Steamhammer 1.4.3 is uploaded. I cleared its learning data, so it is starting from zero and has to figure out on its own how each opponent plays. This is the AIST version. As I have mentioned, the changes affect play on SSCAIT so little that it will be hard to notice any difference.

Note: Because this is the same file I submitted to AIST, debug drawing is turned off. The stream will not show Steamhammer’s familiar game info and production displays.

Steamhammer 1.4.3 change list

AIST S1 submission is closed, so here is the change list for the new Steamhammer 1.4.3 which I submitted. I’ll upload this to SSCAIT tomorrow when I have energy again; I had an exhausting weekend. The SSCAIT upload will be the same file I submitted to AIST, and this would also be a good opportunity to clear Steamhammer’s learned data and start the blank slate test that I mentioned in this post.

Most of the work for this version was preparation to compete in AIST with its demanding maps. Play on other maps is barely different. This post is about what I did, and tomorrow I’ll go into the overall plan and what I didn’t do.

map support

• MapPartitions provides information about walkability and ground connectivity per walk tile. It is used in various places, mainly for figuring out what places a worker or a ground squad can reach, as is needed to support island (Sparkle) and semi-island (Third World) maps. It also helps on land maps that have islands or plateaus, which are common. squad.mapPartition() tells which map partition a squad is on (a squad might have units in more than one partition—it picks any one unit arbitrarily and goes with that unit’s partition). On the map Third World, the narrow ramps from the main bases seem impassible to Steamhammer at build tile resolution (32x32 pixels), but MapPartitions at walk tile resolution (8x8 pixels) understands that units can pass through. It fixes the inability to expand on Third World.

• A bug in map analysis caused a crash on Transistor. Fixed.

• On the map Third World, BWTA doesn’t provide a region for the enemy base. Steamhammer uses BWTA regions for directing the worker scout to and around the perimeter of the enemy base. The missing region caused an endless stream of exceptions and left the scouting worker idle. At some point I will have to provide another way to direct the worker scout (Locutus shows one idea), but for now I worked around it by releasing the scout if there is no region when there should be. Third World is a 2 player map, so it is relatively less harmful to skip worker scouting.

• I wrote 3 openings for Sparkle, a map which requires specialized openings—seriously specialized openings for this map only. Zerg has to make the right number of zerglings at the right time, as dictated by the map design, to get the next gas geyser its strategy calls for.

• I wanted to provide the Require: feature to configure openings appropriate to given map features, but my design was crummy. First, as described it cannot work, it’s not correct for all cases; second, because of how opening selection is implemented, it seems to require a substantial rewrite; and third, it interacts with opponent model opening selection in ways that I didn’t account for. Major ouch. For now, I hardcoded “if the map is Sparkle, select from the Sparkle strategy mix” of the 3 Sparkle openings. Hold your nose, it’s a disgusting hack.

• Sparkle places a psi disruptor on top of the geyser at each starting island’s low ground natural base. You have to destroy the psi disruptor to mine the gas. To support Sparkle (and a small number of other maps, such as Arkanoid), I assembled a complex feature that destroys neutral buildings which obstruct the operation of bases. Actually I kept it simple: The feature destroys neutral buildings which are very close to bases; good enough. Each base, during the bot’s initialization when its data structure is created, finds any neutral buildings that it thinks are in the way and remembers them as blockers. The tactical analyzer, choosing targets for squads to attack, looks to see if any of our bases have blockers, and may assign a squad to destroy the blockers. That involved adding a new squad order DestroyNeutrals and new code in the squad to implement the order (this part at least can be reused when I get around to destroying neutral buildings that block paths). When a blocker is destroyed, InformationManager (responsible for unit tracking) notices and informs Bases, which keeps a reverse index for efficiency and tells the affected base that its blocker is gone. Whew. Not simple, but necessary to play an acceptable game on Sparkle. The feature usually works but fails occasionally, apparently because InformationManager prematurely decides that a blocker has been destroyed; I’m not sure how that happens.

• Island support: Steamhammer normally sends an overlord scout, unless the opponent is known to be terran. It has zero overlord survival skills, so it doesn’t send an overlord to look at a terran base. On island maps, it can’t scout by ground, so I changed it to send the overlord anyway (and lose it to marines) if Steamhammer starts on an island.

• Island support: The Recon squad was designed to perform reconnaissance in force with a small ground squad. On an island map, instead it assigns 1 overlord to be the Recon squad. Obviously that’s no longer reconnaissance in force. It works adequately, but the lone overlord is vulnerable and easily lost.

• Island support: Zerg strategy is adapted to islands. Mainly, zerg goes air only until nydus canals are established. Since zerg doesn’t know how to establish nydus canals, that’s the entire game. It does make plenty of zerglings with its excess minerals, which scout around the island they are on and pounce on any drops.

• Island support: Zerg delays ground upgrades to save gas for its air strategy. It turned out to be a necessary adaptation. Steamhammer is normally aggressive about getting evo chamber upgrades, and it ate up so much gas that the mutalisk fleet was stunted.

• Zerg gets air upgrades when appropriate. Well, it gets air armor up to +2 before greater spire. Steamhammer hasn’t had that feature since early days; I finally restored it. This isn’t technically an island support feature, since it could happen in any game, but it is especially important with the island air strategy.

• Island support: The plan recognizer and plan predictor do not try to recognize or predict island plans; the enemy plan is always Unknown. For one thing, I would need to add new and different island plans; the existing plans don’t fit. For another, Steamhammer doesn’t understand that island and non-island maps are fundamentally different, and that what it learns about an opponent on an island map is probably wrong on a land map, and vice versa. To handle this right, the map adaptation in the opponent model needs more smarts. Anyway, the upshot is that the opponent model is effectively turned off on an island map.

•  I removed more uses of BWTA from various places in the code. There are still a lot more to go; I mainly removed the uses that got in the way for AIST.

buildings

• Fixed a nonfatal exception: A fresh Building object was initialized wrong in one case, so that canceling an expansion which could not be placed might throw. An expansion can almost always be placed (that is, can almost always have its final location set), so it was rare.

• Only print the “reserves wrong” debug message if the DrawBuildingInfo debug option is turned on. The “reserves wrong” message was supposed to shame me into fixing the underlying bugs, but it failed; the bugs are still there.

• In removing BWTA from the code which chooses where to place the next expansion, I removed a feature: Steamhammer will now happily expand to a base in a region where the enemy has buildings. I didn’t want to do extra work to keep using the BWTA region when I intend to drop BWTA. I may have to add the feature back if it causes problems.

tactics

• Squad status strings are a little more informative. Turn on DrawSquadInfo to see them. I should rewrite the squad display, because it’s tough to understand and messages from different squads can overlap on the screen.

• Tactical targeting: A lifted building will be assigned to a squad that can attack air. Clever, no? Oops, but a squad with zerglings and scourge qualifies... well, one step at a time.

• Micr0 targeting: A higher priority for air units to hit tanks. Picking off tanks can be critical, but Steamhammer didn’t think so.

• Micr0 targeting: Scourge are slightly more eager to hit carriers, as opposed to other targets.

code changes

• Changed instances of BWAPI::Position(0,0) to BWAPI::Positions::Origin.

• In the configuration file, I moved the debug log location from the Debug section to the IO section. Keeping the file stuff together seemed more important than keeping the debug stuff together, since the other debug options are unrelated to the debug log.

• Added the missing manual commands /set drawscoutinfo (forgotten long ago) and /set drawqueuefixinfo (forgotten recently).

• Formerly, if you asked for the center point of a squad with no units, you got (0,0) and (if it was turned on) a message on the screen. Not so useful. Now you get the location of the starting base—the spot where future squad members are most likely to appear—and no message.

openings

• The Sparkle openings are Sparkle 1HatchMuta, Sparkle 2HatchMuta, and Sparkle 3HatchMuta. Simple. The 1 hatchery opening starts with 9 gas 9 pool to get mutalisks as fast as possible, and the others start with 12 hatchery. The followups are different from the land-based parent openings, though: They have few zerglings at specific timings, more drones, and earlier hatcheries and second gas. Details matter—it’s not so simple after all.

• New anti-factory variations to offer choices in defeating terran vulture-first play: AntiFact_13Pool and AntiFact_2Hatch.

zerg fixes

• Fixed a problem that could make drones way over the limit. It was rare—except on island maps. I had been puzzling over this one for months.

• Fixed a typo in calculating gas reserves after ordering a carapace upgrade. It was a bug, but the effect is barely noticeable even if you know what to look for—it might delay a few gas units from this production round to the next round seconds later.

Next: The ordeal of supporting Sparkle.

new bot Proxy

Zerg bot Proxy (which is not a proxy bot but a dll) is a nice start on a new bot. It plays only one strategy and has no outstanding skills, but its strategy is dangerous against many opponents. Most impressively, it holds a score of 2-0 against Tscmoo terran. It is currently rated not far below 1900, a decent performance for a new bot apparently made from scratch. (It does say that it uses BWEM and FAP.)

Proxy describes its strategy as “eco cheese into mass hydra.” “Eco cheese” means make lots of drones, which says that Proxy is vulnerable to almost any early attack. A lot of bots play rushes, so Proxy will never be a top scorer with this strategy. Once the mass hydras arrive, though, with that big economy behind them they seem endless and relentless. It mixes in zerglings as needed to balance minerals and gas; that may be its greatest skill. This game against Marine Hell is boring to watch, with no variety, but it shows what I mean about relentlessness. Proxy slowly battered its way up a ramp by sheer persistence.

Like any new bot, Proxy doesn’t have many skills yet. It takes 2 bases and never expands again. It cannot replace lost buildings. It is poor at scouting. It often makes weak tactical decisions. These things need time to implement. Considering how little it knows, I think it is performing well.

Good start!

Steamhammer and AIST S1

Steamhammer is registered for AIST S1. It is almost done; I am working on final testing and fixes. After submission closes, I’ll release the new version as 1.4.3 and upload it on SSCAIT. Features that I formerly planned for 1.4.3 will be pushed to 1.4.4, and so on down the line. Expect the usual change list et cetera, plus discussions of the map features and how I chose to cope with them.

On SSCAIT, it will probably be hard to notice a difference from version 1.4.2, because most of the changes are to support the difficult maps of AIST. There are about 7 bug fixes and improvements that affect play on all maps, but in such subtle ways that neither stream viewers nor Steamhammer’s elo should see a change. Even so, there are a lot of changes, including entirely new features like MapPartitions. Judging the change bars by eye, I’d say I touched about 15% of the lines of StrategyBossZerg. There are also changes to tactics, micro, map exploration, the Recon squad, and scouting.

Steamhammer is not remotely ready for the tournament. The impossible is impossible. I took shortcuts to get as far as I did. But it can play games on the AIST maps without looking like a moth flying into a windowpane (“I see the light, it’s that way.” Wham! “Oh... I see the light again, it’s that way.” Wham!), and it can defeat some opponents. It’s a huge step up from crashing, or throwing endless exceptions, or playing a mass zergling opening on an island map. And I’ve made more progress toward removing BWTA, though it’s haphazard—I removed the parts that were in the way.

Next: The new bot Proxy.

confused game Steamhammer-Tyr

Steamhammer and Tyr terran by Simon Prins played a hilarious game on Moon Glaive. I think that Steamhammer decided on a 3 hatchery before spawning pool build (it’s not entirely clear), but the zerg build became confused because Tyr’s scouting SCV delayed the natural hatchery, and because Steamhammer has an issue on this map where it sometimes sets its bases down unexpectedly far away. The build as executed was hatchery-pool-hatchery, with the third hatchery in Tyr’s natural because zerg hadn’t located the enemy yet. Something similar happened almost exactly a year ago in a game versus Krasi0.

Tyr reacted differently than Krasi0: Tyr ignored the zerg base in its natural!

Tyr is unworried by the zerg base

The marines fired at the zerglings, but the hatchery and drones were in plain sight, and they just didn’t care. Their job, I gather, was to guard the natural against zerg attack, and the functioning zerg base was not an attack. They took their orders literally! Steamhammer was also unworried, and mined minerals contentedly.

Tyr was bright enough to figure out that it had to expand somewhere else, and planted a command center at what would normally be its 3rd base position. The zerg play was disorganized, but the terran play was more disorganized, and Steamhammer won effortlessly.

Arkanoid and BWTA

Regularly scheduled real world events are taking up some of my days.

I wanted to run a test on the map Arkanoid. It’s a concept map from 2006 with many destructible neutral buildings in blocking positions. Some of Steamhammer’s new code for the AIST maps should kick in, so I thought it would be a good test.

But the map crashed BWTA. I have to finish removing BWTA before I can test my own map code on Arkanoid. There is a disadvantage after all to my step by step replacement procedure.

how to specify island openings

By the way, I decided to implement this opening requires that map feature without defaults, like this: If the opening specifies a feature, like "island": "yes", then the feature value is required. If it doesn’t specify, then there is no default value; the map may have the feature or not, the opening doesn’t care. So far it’s as described before, and now I add one more idea: Island openings are playable on non-island maps. They are unlikely to be a good idea, but it will happen against some opponents (I proved it by test). Simply give them a low probability of being played. On an island map, everything else is disallowed, and you get an island opening. On a non-island map, you usually get a usual opening, but occasionally Steamhammer will try out an island opening to see if the opponent is weak to it.