archive by month
Skip to content

Skynet - arbiter control

Arbiters are tough to use well. You want to maneuver your arbiter to cloak as many of your units as possible while staying safe. You want to stasis or recall at critical times and places. Occasionally you want to do damage with the pea-shooter. The goals pull in different directions, and you have to decide what’s best.

Here’s how Skynet decides. Skynet has two arbiter skills: It knows how to cloak units efficiently and how to stasis. Skynet does not use recall, and it (understandably) doesn’t put any emphasis on targeting enemies with the puny gun.

Arbiter control is in ArbiterAction::update(), which is like the other micro actions, set up by Behaviour::createDefaultActions(). First it considers stasis:

	if(mUnit->getEnergy() >= 100 && BWAPI::Broodwar->self()->hasResearched(BWAPI::TechTypes::Stasis_Field))
	{
		const int stasisSize = 48;

		bool stasisUrgently = false;
		if(UnitInformation::Instance().getUnitsTargetting(mUnit).size() >= 6 || (UnitInformation::Instance().getUnitsTargetting(mUnit).size() >= 1 && mUnit->totalHitPointFraction() < 0.3))
			stasisUrgently = true;

First, if stasis is possible and the arbiter is in danger of being lost, Skynet sets stasisUrgently to true.

		UnitGroup stasisChoices;
		for each(Unit enemy in UnitTracker::Instance().selectAllEnemy())
		{
			if(!UnitHelper::isArmyUnit(enemy->getType()))
				continue;

			if(enemy->isUnderStorm() || enemy->isStasised())
				continue;

			const int distance = mUnit->getDistance(enemy);
			if(distance > 250 || distance < stasisSize)
				continue;

			stasisChoices.insert(enemy);
		}

Then it comes up with a list of candidate enemies to stasis. Enemies under psionic storm are not candidates—let them suffer!

		if(stasisChoices.size() > 4 || (!stasisChoices.empty() && stasisUrgently))
		{
			UnitGroup stasisTargets = stasisChoices.getBestFittingToCircle(stasisSize);

			if(stasisTargets.size() > 4 || (!stasisTargets.empty() && stasisUrgently))
			{
				const Position &stasisLocation = stasisTargets.getCenter();
				if(mUnit->getDistance(stasisLocation) <= BWAPI::TechTypes::Stasis_Field.getWeapon().maxRange())
				{
					mUnit->useTech(BWAPI::TechTypes::Stasis_Field, stasisLocation);
					LatencyTracker::Instance().placingStasis(mUnit, stasisLocation);
					return true;
				}
				else
				{
					mUnit->move(stasisLocation);
					return true;
				}
			}
		}
	}

And finally Skynet decides whether and where to stasis. If it can stasis at least 5 enemy units, or at least 1 unit and stasisUrgently is set, then it does. UnitGroup::getBestFittingToCircle() finds a circle of the given size which covers as many units in the UnitGroup as possible, and returns the smaller UnitGroup which it covers.

It’s a simple heuristic. An arbiter will happily stasis 5 vultures uselessly when it reaches stasis energy after the army it was covering is destroyed by tanks and vultures. The “right” way to do this would be with a pay-me-now-or-pay-me-later tradeoff that compares the situation now with possible future situations, which would of course be far more complicated. And, since that’s not hard enough yet, it should also consider the effect on the battle: Stasis on a ramp to block reinforcements? Stasis the tanks that are doing the most damage, or the vessel that is detecting the cloaked army? And so on.

Next is maneuvering the arbiter to cloak units:

	UnitGroup unitsToCloak;
	for each(Unit unit in squadUnitGroup)
	{
		if(!UnitHelper::isArmyUnit(unit->getType()))
			continue;

		if(unit->getType() == BWAPI::UnitTypes::Protoss_Arbiter || unit->getType().isBuilding())
			continue;

		if(mUnit->getDistance(unit) > 250)
			continue;

		unitsToCloak.insert(unit);
	}

	if(!unitsToCloak.empty())
	{
		unitsToCloak = unitsToCloak.getBestFittingToCircle(136);
		if(!unitsToCloak.empty())
		{
			Position cloakLocation = unitsToCloak.getCenter();
			if(mUnit->getDistance(cloakLocation) > 110)
			{
				mUnit->move(cloakLocation);
				return true;
			}
		}
	}

Candidate units to cloak are basically military units in the squadUnitGroup which can be cloaked and are near enough. Skynet never tries to protect probes or observers with the cloaking field (UnitHelper::isArmyUnit() returns false for both), but it will try to cloak dark templar that are already cloaked (a simple omission from the above code and trivial to fix) and units that are already covered by another arbiter (only a little harder to fix). It does the getBestFittingToCircle() thing to find the largest circle of units that it can cloak, and moves toward the circle’s center if it’s not close enough. If it’s already close enough then the micro action does not occur, which I assume frees the arbiter to fire its gun when possible, flee danger within limits, and so on.

Tomorrow: Avoiding splash damage, finishing the series.

Skynet - mine dragging

Spider mines do a ton of splash damage when they explode, and they don’t discriminate. Everything in splash range suffers, enemy or friendly. So it’s popular to intentionally trigger enemy mines while running up to enemy units, dragging the mine into the enemy to cause damage. Mines can sometimes follow a unit for quite a distance before going off. Protoss usually drags mines with zealots, though dark templar are also good. Zerg usually uses speedy zerglings in small groups, because they’re cheap and fast. Terran can sometimes trigger enemy mines with drops, but doesn’t have an appropriate unit to drag mines otherwise.

Skynet knows how to drag mines with a zealot. It uses the same system of micro actions that I touched on yesterday. In Behaviour::createDefaultActions() it records a possible mine drag micro action for every zealot:

	if(unitType == BWAPI::UnitTypes::Protoss_Zealot)
		mMicroActions.push_back(MicroAction(new MineDragAction(mUnit)));

By the way, the micro actions are evaluated in Behaviour::update(), which checks them in order. Each unit can only execute one micro action at a time, and the first action to return true is what happens. Skynet’s only prioritization is to record the most important micro actions first in createDefaultActions(). Mine dragging is recorded near the end, as a low-priority action. Yesterday’s archon-zealot undetected unit attack is higher priority.

Here is the entirety of MineDragAction::update():

bool MineDragAction::update(const Goal &squadGoal, const UnitGroup &squadUnitGroup)
{
	for each(Unit unit in UnitInformation::Instance().getUnitsTargetting(mUnit))
	{
		if(unit->getType() == BWAPI::UnitTypes::Terran_Vulture_Spider_Mine)
		{
			int distance = std::numeric_limits<int>::max();
			Unit closestUnit;

			for each(Unit enemyUnit in UnitTracker::Instance().selectAllEnemy())
			{
				if(enemyUnit->getType().isFlyer() || enemyUnit->isLifted() || enemyUnit->getType().isBuilding() || enemyUnit->getType() == BWAPI::UnitTypes::Terran_Vulture_Spider_Mine)
					continue;

				int thisDistance = mUnit->getDistance(enemyUnit);
				if(thisDistance < distance)
				{
					distance = distance;
					closestUnit = enemyUnit;
				}
			}
			
			if(closestUnit && distance < 32*5)
			{
				mUnit->attack(closestUnit);
				return true;
			}
		}
	}

	return false;
}

This seems to only notice spider mines which have already popped up and are targeting the current zealot. If one is found, then the code checks for suitable enemy units (not stuff in the air, for example) that are close enough. If any is found, then the zealot targets the nearest for attack, end of story.

That’s basic mine dragging. A stronger version would also consider detected or remembered mines: If a mine in the ground is detected or remembered to be near valuable enemies, then check whether it can be activated and dragged into the enemies. You can remember a mine location if you detected it in the past, or if you saw it being laid or moving and reburrowing. That’s more complicated, but human players do it as a matter of course. Humans will even try to drag mines that they expect or hope are there, without being sure.

Tomorrow: Arbiter control.

Skynet - killing undetected units

Archons do splash damage that affects all enemy units, included undetected units. That means that you can use an archon to, for example, kill a burrowed lurker that you can’t detect, by attacking one of your own units. The usual way is to park a sacrificial zealot on top of the lurker and attack the zealot with the archon; the archon will do full damage to the zealot and splash damage to the lurker.

Archon splash affects both ground and air. An archon attacking a zealot will damage a cloaked wraith that is in splash range. An archon attacking a floating ebay should clear spider mines in a small area (I haven’t tested to make sure).

Here’s how Skynet does it. Behaviour::createDefaultActions() recognizes the unit types and records possible micro actions to test and execute later:

	if(unitType == BWAPI::UnitTypes::Protoss_Zealot || unitType == BWAPI::UnitTypes::Protoss_Archon)
	{
		firstTargets.insert(BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode);
		mMicroActions.push_back(MicroAction(new ArconZealotKillUnDetected(mUnit)));
	}

When the micro action comes up for testing, it runs ArconZealotKillUnDetected::update(), which either fails the test and returns false or succeeds, executes the micro, and returns true. Given the original archon or zealot in mUnit, it looks for the closest enemy unit that meets the conditions:

	Unit lurker;
	int lurkerDistance = std::numeric_limits<int>::max();
	for each(Unit unit in UnitTracker::Instance().selectAllEnemy())
	{
		if(unit->exists() && !unit->isDetected() && (unit->isCloaked() || unit->getType().hasPermanentCloak() || (unit->getType() == BWAPI::UnitTypes::Zerg_Lurker && unit->isBurrowed())))
		{
			int thisDistance = mUnit->getDistance(unit);
			if(thisDistance < lurkerDistance)
			{
				lurkerDistance = thisDistance;
				lurker = unit;
			}
		}
	}

	if(!lurker || lurkerDistance > minDistance)
		return false;

The variable is called “lurker” but it looks for any unit that is undetected and is temporarily cloaked (like a wraith or a unit under an arbiter), permanently cloaked (like a dark templar), or is a burrowed lurker. I skipped over the next bit of code: It checks for the other unit (of type typeToFind) and if it’s close enough puts it in other; if it has a zealot it looks for an archon, or if an archon it looks for a zealot. If it finds the needed other unit, it executes the micro:

	if(typeToFind == BWAPI::UnitTypes::Protoss_Archon)
	{
		if(mUnit != UnitTracker::Instance().selectAllUnits(BWAPI::UnitTypes::Protoss_Zealot).getClosestUnit(lurker))
			return false;

		mUnit->move(lurker->getPosition());
		return true;
	}
	else
	{
		if(otherDistance <= 14)
		{
			mUnit->attack(other);
			return true;
		}
		else
		{
			mUnit->move(other->getPosition());
			return true;
		}
	}

If the unit is a zealot (because typeToFind is archon) and it’s not the closest zealot, it bails so that the target doesn’t get swarmed by zealots—one volunteer at a time, please! The zealot moves to the position of the target. If the unit is the archon, it moves toward the zealot (not toward the target; this corrects for miscoordination that could occur if there’s more than one target around) or, if the zealot is within splash range of the target, attacks the zealot.

The bottom line is that Skynet has the basic skill, but its implementation is not sophisticated. It knows all the possible targets, but can only use a zealot as the sacrifice—pros in a pinch will use anything that works as the sacrifice. It doesn’t know that it could use a reaver instead of an archon. It doesn’t consider whether the sacrifice is worth the damage to the target (you’ll hurt your zealot and the cloaked wraith will probably escape). It does no preparation (keep the archon and zealot near each other if they may need to work together) and minimal coordination (if there’s an observer just out of range and approaching, you shouldn’t have bothered; if you have high templar around maybe you should storm it). If the target evades, Skynet may keep microing both units, leaving them out of the fight. But of course most bots don’t have even the basic skill.

Reminder: Other units that do splash can also kill undetected enemies this way. Reavers, sieged tanks, and lurkers are the prime splash-dealing candidates. Corsairs, firebats, valkyries, and infested terrans also have occasional uses. (Mutalisk bounces are technically not splash and do not hit undetected enemies.) And, of course, spell effects like nuke, irradiate, psionic storm, ensnare, and plague affect units whether they are detected or not. Ensnare and plague are particularly useful because they make cloaked units visible.

Near the end of this 2008 game, Stork fires on his own carriers with corsairs to hold off cloaked wraiths.

Tomorrow: Mine dragging.

Skynet skills

I meant to write another post or two about how particular bots keep their squads in formation, but after grepping source code for every keyword I could think of, I didn’t turn any up. We already know that OpprimoBot keeps its squads compact with a flocking algorithm, so my search was definitely not comprehensive... but I still gave up.

Instead I’ll write a short series about interesting skills coded into Skynet. Of bots whose source I have, the three candidates for Bot With the Most Hand-Coded Skills are ICEbot, Skynet, and Tscmoo, and Skynet is the easiest to read.

As an appetizer, here is a comment from TaskManagerClass::getPriorityList, which sorts tasks related to build order, research and production into priority order:

	// If I am vulnerable to counter attack / have no map control, place defense higher
	// If I am not behind on army size but its not safe to attack, tech
	// If I am not behind on army size but its safe to attack, produce
	// If I'm behind on army supply, produce

The ideas in the comment are not implemented. Skynet is already one of the cleverest bots in adapting its build to the game situation. Imagine how much cleverer it could be if Andrew Smith had had enough time to implement these ideas.

Tomorrow: Killing undetected lurkers.

Zia’s evolution

Zia has followed a wandering course. The early versions opened 12-hatch and went mass mutalisks. Then it switched to a 9-pool opening. Then a fast rush opening. Now it is back to an economic opening, this time with mass lurkers and lings.

I suspect that Zia is collecting strategies. Some future version may have all the strategies available as choices. If it selects well, it will become a force to reckon with.

Tscmoo terran apparent neural network output

I was watching the new Tscmoo terran with its reputed neural networks.

screenshot showing what looks like neural network output

Hmm, what are those red and blue dots?

detail of apparent neural network output

I read that as the output of the neural network. The dot diagram is incomprehensible unless we know about the network layout. The text is the interpretation; it looks like strategy instructions or hints to the rest of the program. I timed a couple of updates and found them 15 seconds apart, which fits with strategy information.

I can’t tell what the details mean. How can the army composition be tank-vulture if you open with two starports (see those wraiths on the screen)? Is that a prediction for the opponent, maybe? What does “support_wraiths” mean, since I didn’t notice the wraiths seeming to support or be supported by anything?

crazy new Tscmoo protoss strategy

Whoa, did y’all see that? Tscmoo protoss has a hilarious new strategy: Cannon contain into mass dark archons with mind control!

The cannon contain may win a lot of games against unprepared bots, but the dark archons—I’ve never seen that many at once.... This is even wilder than Tscmoo terran’s nuke strategy.

dark archons and zealots

Update: An even funnier picture: Mass dark archons chasing after a floating engineering bay.

dark archons chase an ebay

humans don’t understand bots

Igor Dimitrijevic’s comment on yesterday’s post reminded me: It’s difficult to understand much of a bot’s behavior by watching it.

Krasi0 is a good example. In the last several months I’ve watched the old veteran bot grow much stronger, returning to the top ranks. I can describe in general terms some things Krasi0 has improved at: It is more aggressive, it is better at protecting its workers from danger, it is smarter about where it sieges its tanks. (It also solved crashing bugs.) But I feel sure that the points that I’ve noticed are only the tip of the iceberg. There must be not only details but whole classes of behaviors that I did not pick up on at all—otherwise it could not have improved so much.

I guess humans don’t have the perceptual bandwidth to take it all in, at least not without the experience or prior knowledge to know what to look for. Starcraft play is too complicated for us to follow! I’m sure I could understand more if I studied replays closely.

I’ll take it as a reminder not to be too glib in drawing conclusions.

Speaking of glib conclusions about bot behavior, MaasCraft looks more interesting when it plays against more interesting opponents. I concluded earlier from watching 2014 replays that it mostly moved its army forward and back along the path between bases. Well, that’s what its opponents were doing too, so it may not have had much choice. Today’s bots try more complicated maneuvers, and today’s MaasCraft reacts with its own more complicated maneuvers. I’ve seen it (seemingly intentionally) split its army to trap stray units, for example. It reacts sensibly to multi-prong attacks.

MaasCraft is still scoring poorly, but now its tactical search is showing sparks of promise—I suspect due to changes in its opponents, not itself. As a reminder, LetaBot has a search descended from the same code, turned off in some versions but likely to be turned on in final versions.

Zia and mutalisk micro

Zia’s mutalisk cloud is scary when it gets big. Eventually the mutas not only one-shot the units that they target, but their bounces instantly kill nearby units. The mutalisks sweep a path of destruction. But think about it—is that efficient? If mutalisk bounces at 1/3 power kill instantly, then the main attack must usually be gross overkill. Most of the firepower is wasted.

The idea of individual mutalisk control, as introduced by the Berkeley Overmind and copied by other zergs since, is to waste no firepower. Each flier independently dances in and out for safety and ideally attacks at near its maximum rate. But watch how Tscmoo zerg implements this: Its mutalisk cloud is also scary when it gets large, but usually not as scary as it could be, because it spreads out too much. Sometimes half the mutas are posing for pictures with the ground army while half are on the job. And the attackers often pick some targets over here, some over there, and don’t kill either as fast as they should. Tscmoo doesn’t focus its fire enough; it’s the opposite mistake from Zia.

Causing damage does not win games. Maximizing your damage output is not the winning move. You want to balance between killing the most important enemies and staying alive.

Try to imagine PerfectBot’s muta micro. Even PerfectBot can’t truly play perfectly, because calculating optimal micro is infeasible. But surely PerfectBot focuses fire efficiently, switching mutas fluidly between targets, taking into account importance and time to kill based on distance and damage rate and expected losses, to reduce overkill to near zero and spend less time flying between targets and strike a good balance between killing the most important stuff fast and staying alive. “This takes 5 more shots to kill, 12 are shooting, might lose 1, so switch 6 to new targets.” Zia and Tscmoo zerg are no competition for Jaedong, but I think Jaedong would boggle at PerfectBot’s mutalisks.

How close can we get to PerfectBot micro today? 1. Given a set of targets in priority order, calculating how to focus them down efficiently with minimal waste seems intricate but ultimately not that hard. 2. Folding in a desire to also minimize losses makes optimal decisions computationally infeasible. Even approximations seem tough. 3. Prioritizing the targets depends on the total game situation and will have to be done heuristically. For now I guess we’ll have to settle for a simplified algorithm.

Watching Zia last week, I thought it picked targets usually one at a time (simple 3) and once the target was chosen ignored damage taken while chasing it down (very simple 2), so the intricate-but-not-hard efficient killing calculation by itself should be a big improvement. Zia-this-week has been updated and has fancier micro than Zia-last-week, so I’m already behind the times! I got the impression that Zia-this-week is better about picking targets and switching targets and avoiding damage, but that it still wastes shots with too much overkill.

new zerg bot Zia

Speaking of zerg being easiest, I’m pleased with the aggressive new zerg bot Zia (games cast by Nepeta). Zia’s description says it’s “just for fun.”

Zia goes for middle game play with mutalisks and zerglings, complete with upgrades that eventually reach 2-2. (One game in the video also has a hydralisk switch which did not seem advisable.) Other mutalisk bots follow the lead of the Berkeley Overmind and spread out their mutas for (what is advertised as) maximum overall damage rate. Zia doesn’t seem to care about bot fashion. Zia concentrates its mutas into a bunch more like a human player, which may be less efficient in creating damage from an operations research point of view but is aggressive and can be more efficient at actually killing targets—and that’s how you win games.

Attack-oriented opponents, even strong ones like Iron and tscmoo terran, seemed unable to defend against so many mutas flying so aggressively and backed by lings. Defense-oriented bots like LetaBot (which holds its base with active defense and saves up its strength for mighty strikes) didn’t have much trouble, though. I also saw Krasi0, a hardened veteran that learned mutalisk defense facing the Berkeley Overmind, adapt effortlessly and win.

I like Zia, but by now you must realize that I’m not psychologically capable of finishing a post without asking for more. Mutalisk targeting looks a little haphazard—too much zipping about, hitting the wrong enemies, and taking incidental damage. When you concentrate your force, it becomes much more important to concentrate it in the right place! One thing the Berkeley Overmind did right was to fly around the perimeter of the defended area looking for weak points. Also the lings are not attacking as much as they should, as if they were sometimes ordered to move while next to a target. Would that be fun stuff to improve?

Update: Zia starts +1 attack for its mutalisks before spending on the first mutalisk. That’s dedication!

CerkoBot and the Augean Stables

I like CerkoBot by Tomas Cere. Its play is complicated and interesting and entertaining. One bit I especially like is its way of defending its choke: It will chase harassing enemies away, but only up to a limit—it maintains a frontier beyond which it doesn’t stray while defending. Though it’s old, it still puts up a stiff fight against many new bots.

But being old, it has a lot of weaknesses. It’s hardly fair to criticize a bot which hasn’t been updated since 2012, but I’ll catalog the big weaknesses anyway. I must be cruel at heart (but there’s a method to my meanness).

• Its early build order is not safe and it often loses to rushes.

• Reavers are its backbone, but it has poor reaver targeting and doesn’t understand how to position other units to avoid blocking reaver shots. A reaver that is in range of an enemy building will never move forward to target units, no matter how urgent. (Luckily for CerkoBot, reavers are so strong that they often turn the tide anyway.)

• It sucks at keeping its army together. When fast-moving units encounter opposition, they often have to retreat because the reavers have been left behind. That’s only right, but instead of gathering the army at an intermediate point, CerkoBot retreats the distant reavers too, staying scattered. Learn from vector units, they have both scatter and gather!

• It mispositions its observers so that it sometimes has no detection at critical points. It also bunches them together, often losing them all at once.

• In the late game it builds arbiters, but it can’t use them efficiently. It loses them too easily, doesn’t maneuver well to cloak more units, and never uses stasis or recall.

• It can’t cope with a spider mine blocking an expansion spot. I think that’s the main reason that it loses to IceBot.

• It dies against mutalisks.

• Like many bots, it hasn’t bothered to read the victory conditions and wants to fight the enemy’s army instead of the enemy’s bases. (Except the reavers, which shoot buildings over workers.) It will ignore an undefended expansion to lose its army against an entrenched terran formation elsewhere.

If CerkoBot were ever to become a contender again, as it was in its heyday, it would have to fix a bunch of these weaknesses. Eh, probably more. Why not clean the Augean stables instead? That’s why we need starter bots on the one hand (Peneus), and search and learning tech on the other (Alpheus).

GarmBot update

GarmBot was reuploaded twice recently on SSCAIT. It looks as though it is under development again. Woohoo! Its description has added the message “Now with some lurkers. Big update coming to upgrade to BWAPI 4.1.2.” And I did see it build lurkers, which could spell trouble for some opponents (ones that GarmBot didn’t already overwhelm, I mean!).

GarmBot’s strategy is unsound, of course, but it’s also unique and delightful. It has a lot of room to improve without varying from its extreme macro, every-unit-for-itself game plan, so I’m interested to see how far Aurelien Lermant will go. Will some units get better micro or better decision-making? Will it make cleverer choices about which units to make where, such as not hatching drones to die instantly at a base that’s under attack? I would be interested to see whether it helps at all to build defensive units at a base that’s under light harassment and to waste as few resources as possible at a base that’s under massive attack—improvement or “so what?”

Not to put impossible expectations on anybody, but I can hope.

Update: The current GarmBot seems cleverer in some ways. For example, it is smarter with overlords. It’s also less aggressive about expanding and less successful overall. That’s OK—when adding ideas to a performance program it is normal to make the program worse at first, even when the ideas are good. The rest of the program may be optimized for the old behavior, and then you need a lot of updates.

Iron vs Letabot

Games of Iron against different versons of LetaBot have been entertaining me. Iron has learned the same trick as Tscmoo of killing tanks by laying mines next to them. LetaBot often suffers unnecessary mine hits when it sieges up just as a mine triggers. On the other hand, LetaBot also has micro skillz and likes to kill mines after they trigger—I’ve seen it kill a mine with two fast-reacting SCVs!

The games go back and forth, but Iron has the upper hand for now. Iron has become strong against other bots. LetaBot could improve by sieging more cautiously, which is part of the terran technique of inching units forward to force a minefield. Or it could scan for mines. Scanning intelligently for mines is not that easy because the bot has to keep track of where mines are likely or possible, which seems like a lot of coding work to support a single skill.

in other news

Rob Bogie has uploaded MaasCraft (originally written by Dennis Soemers) to SSCAIT, and it plays like the replays I watched last month. MaasCraft is scoring poorly—the opposition is a lot stronger than in 2014. He also re-uploaded it, which sounds like an update. Is MaasCraft going to see major updates under its new ownership? I haven’t yet noticed any changes to its play.

PS Making slow progress on the website improvements. I chose a hard road for myself.

tanked and spanked

Here Tscmoo has discovered that Tyr built near the edge of its low-ground main base and is vulnerable to attack from outside. Blasting down buildings with no losses helped Tscmoo in its easy win.

Tscmoo’s tank fires into Tyr’s base from outside

Did Tscmoo’s tank just happen to wander into a good position, or did Tscmoo analyze the situation to find the opportunity?

Noticing when you can shoot across a barrier is a great skill. I’ve seen other bots miss chances for free kills with tanks and even with dragoons. And it’s not hard—all you have to do is figure out what’s in range from where, and recognize simple cases where it gives you an advantage. Human players are always on the lookout for chances like that.

hitting the wall

In a game last month, Tyr by Simon Prins was winning until this happened.

screenshot of game in which Tyr blocked itself into its own base

Pro tip:Don’t do that. The Great Wall doesn’t keep barbarians out, it keeps you in. The protoss bot by Roman Danielis dropped a dark templar inside the base from a shuttle and cleaned house. (You thought that a dark templar carried a warp blade? This one had a mop.)

I shouldn’t pick on Tyr—I think Tyr is above average in placing its depots in tight clumps along the edge of its base. It hit a bug this time, and I think the bug has been fixed. Building placement is hard, and it’s especially hard if you are terran, because terran bases need a lot of room for production buildings and supply depots. Some bots space all buildings apart from each other so that there’s a gap between any two and blocking is impossible. Terran bots that do that end up running out of room and having to build depots and factories outside the base in vulnerable open areas. Here ICEbot spaced out the buildings in its main and its construction had to spill into the map.

screenshot of terran buildings spilling out of the base

The fancy solution is do a path analysis in deciding on building placement. Does it block units off from anywhere they need to get to? Can fresh units get to the front without threading a maze? The analysis will be trivial sometimes, but not when the base starts filling up. PerfectBot will analyze both its own and its opponent’s building placement, for both attack and defense. “Oh, the opponent built an obstacle course. The enemy army is here so I’ll drop there.

Knowing when the opponent has built a wall is a valuable skill—notice how well LetaBot’s ramp wall-in works against many opponents. If you don’t want to do real pathfinding around buildings, you could try to recognize a wall when your units fail to make progress toward their goal. I’m not sure it’s easier and I’m pretty sure it works less well, but monitoring progress toward goals is a good idea regardless. Units that find themselves behind a wall may want to destroy a building in the wall, without caring whether the building is enemy or friendly. Tyr could have won if it had opened its own wall.

Most bots probably rely on heuristics to place buildings so that they rarely cause problems. Nothing wrong with that; it should work well enough and it will be easy, and there’s a lot to do so ease counts. But I hope a few authors will go for more ambitious solutions.

MaasCraft’s play

Does MaasCraft’s search make its play more interesting? I grabbed its replays from Starcraft AI Data Archive and watched some.

Short answer: No. It mostly moves its army forward and back along a path between its base and the enemy’s (exactly what its author Dennis Soemers hoped search would avoid) and rarely pulls any interesting maneuvers. It bears out what the author said about the appropriateness of the algorithm to the strategy.

I did notice one advantage MaasCraft has over many bots: It doesn’t chase after fast units that it can’t catch. I suppose the search sees that they get away.