UAlbertaBot fixes #6 through #8
Thanks to AIL I have a 10th fix, so I decided to cover 3 today.
#6 a morphing hive is not a resource depot
WorkerManager::getClosestDepot()
figures out what command center/nexus/hatchery to send a worker to.
if (unit->getType().isResourceDepot() && (unit->isCompleted() || unit->getType() == BWAPI::UnitTypes::Zerg_Lair) && !workerData.depotIsFull(unit))
The unit->isCompleted()
part handles a corner case. If a command center, nexus, or hatchery is uncompleted, it can’t accept resources. If a lair or a hive is still morphing, it can. The bug is that the check omits hive.
if (unit->getType().isResourceDepot() && (unit->isCompleted() || unit->getType() == BWAPI::UnitTypes::Zerg_Lair || unit->getType() == BWAPI::UnitTypes::Zerg_Hive) && !workerData.depotIsFull(unit))
#7 my base is your base
This is the one I wrote up as Steamhammer’s funniest bug. InformationManager::updateBaseLocationInfo()
implements (among other things) a heuristic to guess where the enemy base is: If an enemy building is seen in the same region as a starting location, it assumes that the enemy base is at that starting location. Obviously it can be fooled if the enemy builds in another main base.
if (isEnemyBuildingInRegion(BWTA::getRegion(startLocation->getTilePosition()))) { if (Config::Debug::DrawScoutInfo) { BWAPI::Broodwar->printf("Enemy base found by seeing it"); } baseFound = true; _mainBaseLocations[_enemy] = startLocation; updateOccupiedRegions(BWTA::getRegion(startLocation->getTilePosition()), BWAPI::Broodwar->enemy()); } ...
For example, if the enemy proxies in your base, UAlbertaBot believes that your base is the enemy base, with hilarious results. I fixed it by checking against our own base location. That removes this way for the heuristic to fail, but it adds another one—if you ever play on a map in which your base and the enemy base are in the same region, the bot cannot find the enemy base this way. That’s life when you accept heuristics.
if (isEnemyBuildingInRegion(BWTA::getRegion(startLocation->getTilePosition()))) { updateOccupiedRegions(BWTA::getRegion(startLocation->getTilePosition()), BWAPI::Broodwar->enemy()); // On a competition map, our base and the enemy base will never be in the same region. // If we find an enemy building in our region, it's a proxy. if (startLocation != _mainBaseLocations[_self])) { if (Config::Debug::DrawScoutInfo) { BWAPI::Broodwar->printf("Enemy base found by seeing it"); } baseFound = true; _mainBaseLocations[_enemy] = startLocation; } ...
#8 assigning workers to buildings
BuildingManager::assignWorkersToUnassignedBuildings()
is responsible for choosing what worker builds each building. It calls on WorkerManager
to find a worker which is close to the building’s final position, which is stored in b.finalPosition
. I read this code quite a few times before the bug hit me.
// grab a worker unit from WorkerManager which is closest to this final position BWAPI::Unit workerToAssign = WorkerManager::Instance().getBuilder(b); if (workerToAssign) { //BWAPI::Broodwar->printf("VALID WORKER BEING ASSIGNED: %d", workerToAssign->getID()); // TODO: special case of terran building whose worker died mid construction // send the right click command to the buildingUnit to resume construction // skip the buildingsAssigned step and push it back into buildingsUnderConstruction b.builderUnit = workerToAssign; BWAPI::TilePosition testLocation = getBuildingLocation(b); if (!testLocation.isValid()) { continue; } b.finalPosition = testLocation; // reserve this building's space BuildingPlacer::Instance().reserveTiles(b.finalPosition,b.type.tileWidth(),b.type.tileHeight()); b.status = BuildingStatus::Assigned; }
It is assigning “the closest worker” before the position it is to be close to has been computed. In practice, I found it was assigning the free worker closest to (0,0). If the bot has several bases, it may send a distant worker on a long trek. Since UAlbertaBot normally plays rushes, the buggy behavior is not easy to notice in its play, but Steamhammer likes to take the map.
BWAPI::TilePosition testLocation = getBuildingLocation(b); if (!testLocation.isValid()) { continue; } b.finalPosition = testLocation; // grab the worker unit from WorkerManager which is closest to this final position b.builderUnit = WorkerManager::Instance().getBuilder(b); if (!b.builderUnit) { continue; } // reserve this building's space BuildingPlacer::Instance().reserveTiles(b.finalPosition,b.type.tileWidth(),b.type.tileHeight()); b.status = BuildingStatus::Assigned;
Comments
LetaBot on :
Won't happen in any competitive map though. Even in the small map Blood Bath the regions are separate.
Jay Scott on :
Ail on :
One thing I find super annoying is that when my expansion finishes and I want to build a sunken there, it will still build in the main as the creep is not counted yet in that frame. And if I wait for a third hatch to go up, it is too late. So maybe I need to check for build able creep at the expo.
Lukas Moravec on :
Jay Scott on :