archive by month
Skip to content

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;

Trackbacks

No Trackbacks

Comments

LetaBot on :

A map where you and your enemy are in the same region would be quite funny, since that usually means a worker rush battle (since you spawn so close to your enemy).

Won't happen in any competitive map though. Even in the small map Blood Bath the regions are separate.

Jay Scott on :

Just so, it will never happen on a competition map. I’m not sure about BWTA, but couldn’t it theoretically be a large map that’s all flat and open? Not that that’s any more interesting!

Ail on :

Those are all really good finds. Gotta make sure to fix them on my end.
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 :

When changing #8, don't forger to change BuildingPlace::buildable, because the check for units on tile in the original version also checks for the builder being already assigned. Before I got rid of the builder check, the buildings were being constructed without checking for their distance.

Jay Scott on :

I knew a bug was in there somewhere. Thanks for finding it! I think there may also be an interaction with whether the building worker was pre-positioned by ProductionManager, so I am going to work carefully through all the paths before I put in a fix.

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.