archive by month
Skip to content

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.

Trackbacks

No Trackbacks

Comments

Tully Elliston on :

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

If you are storing values on a per unit basis, in addition to the 1 and 0 MicroState boolean, why not add an integer that acts like a fear meter between frames.

Eg. The integer starts at a fixed value, say 10. When the unit is outside of combat, the value returns to 10.

Every frame that the MicroState boolean is 0, the integer decreases by 1.

Every frame that the MicroState boolean is 1, the integer increases by 1 (to a maximum of 10).

Then, base your attack or retreat decision on the integer being 0 rather than the MicroState boolean being 0 - units will need to consistently detect defeat in the combat sim for multiple frames before they bail, reducing jitter.

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.