archive by month
Skip to content

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.

Trackbacks

No Trackbacks

Comments

Dan on :

A solid batch of changes! Good luck, Jay.

Bruce on :

This inspired me to look into the "reserves wrong" bug again, it also eluded me the first time I tried to figure it out. But I've had a bit more luck this time!

In some cases where a queued building was cancelled because of the buildingTimedOut check, a different queued building would get removed from the _buildings vector. I am not entirely sure why, but I refactored it to avoid the temporary toRemove vector and that seems to have helped, so perhaps it has something to do with the building object being copied when pushed into that vector. In any case, I haven't seen the message in the test games I've run since.

While debugging this, I also noticed that occasionally a nexus would fall to the buildingTimedOut check simply because it was on the other side of the map and the worker took a long time to get there. In many cases this would result in the probe not having the resources to build it when it arrived, as they were no longer reserved, so the probe would stay idle there indefinitely. I don't think this is caused by any of my changes, so you may want to look into it as well.

I'm still doing some final testing, but should be pushing the changes to Github tonight or tomorrow.

Jay Scott on :

The purpose of toRemove is to avoid changing the vector of buildings while iterating through it. Did you make sure that your change is safe against that? Hmm, actually, now that I look at it again I see that there are other cases in BuildingManager which may be unsafe due to that error.... Maybe I will change them to use iterators. Or something.

Jay Scott on :

Why did Dave Churchill decide to copy building objects into that vector? For some reason I never wondered about it before. If done that way, it should be a vector of references.

Jay Scott on :

... which is illegal due to the design of std::vector. Sigh. Surely there must be a correct way to do this without dropping to low level.

Jay Scott on :

OK, std::reference_wrapper works and only makes the type moderately ugly. I used the same method to fix cancelQueuedBuildings() and cancelBuildingType(), which erroneously modified the vector of buildings while iterating over it. Theoretically, erase-remove would be a better way to do the final deletion, but it seems awkward in this case and the code is good enough.

Jay Scott on :

Thanks for the report! I fixed 2 bugs and made the code better, even if I didn’t follow your idea as written.

Jay Scott on :

Maybe BuildingTimedOut() should start with a prediction of how long the building should take to start. Multiply by a safety factor, and nearby buildings will time out faster and release the reserved resources sooner, while distant buildings will not time out unnecessarily. But I’m also thinking of switching the whole bot to a more comprehensive progress-tracking design. Hmm. Anyway, the issue is on my list, thanks again.

Jay Scott on :

I examined the whole codebase for similar bugs and found 2 more. BuildingData::removeBuildings() was wrong, but it was also unused so I removed it. Squad::releaseWorkers() was another case. In practice this also is not used, but it should be available so I fixed it.

Bruce on :

I just pushed my changes.

I chose to fix the remove buildings thing by using an iterator in validateWorkersAndBuildings and erasing them directly in there, which I found simpler. I'm not really a C++ guy so I can't speak much for the reference wrapper stuff, but it sounds reasonable :)

I agree that the timeout detection should make a prediction when the building is queued and work from there, but I didn't want to go all the way for now, so I chose to start tracking the frame when the building order was first issued and use that for timeouts. It's probably too conservative but seems to be an improvement.

There is also an unrelated bug fix that may interest you:

https://github.com/bmnielsen/Locutus/commit/08921979834590cddd958ba7157635ac466f5884

This caused the bot to stop expanding if anything was built inside (0,0) to (3,2). I noticed it on Python where there is a geyser there, so as soon as the top-left base was taken it wouldn't take more.

Jay Scott on :

If I were a C++ guy myself, I would have already known the story about putting references in STL containers!

Add Comment

E-Mail addresses will not be displayed and will only be used for E-Mail notifications.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Submitted comments will be subject to moderation before being displayed.