text: #include "CombatCommander.h"
  #include "UnitUtil.h"
  #include "SquadData.h"
  using namespace UAlbertaBot;
  
  const size_t IdlePriority = 0;
  const size_t AttackPriority = 1;
  const size_t BaitPriority = 1;
  const size_t BaseDefensePriority = 2;
  const size_t ScoutDefensePriority = 3;
  const size_t DropPriority = 4;
  const size_t DropAttackPriority = 5;
  
  CombatCommander::CombatCommander() 
      : _initialized(false)
  {
  
  }
  
  void CombatCommander::initializeSquads()
  {
  	if (Config::Strategy::PreventBaiting) {
  		// the neutral zone attack squad will handle enemy units that try to bait our combat
  		// units to follow them around the map
  		SquadOrder neutralZoneAttackOrder(SquadOrderTypes::Attack, getMainAttackLocation(), 800, "Attack Enemy in Neutral Zone");
  		_squadData.addSquad("NeutralZoneAttack", Squad("NeutralZoneAttack", neutralZoneAttackOrder, AttackPriority));
  	}
  
  	if (Config::Strategy::BaitEnemy) {
  		// change the position of the bait squad, although this will do for now as we want to keep the enemy closer to their base
  		SquadOrder baitSquadOrder(SquadOrderTypes::Attack, getMainAttackLocation(), 800, "Bait Enemy in Neutral Zone");
  		_squadData.addSquad("Bait", Squad("Bait", baitSquadOrder, BaitPriority));
  	}
  
      SquadOrder idleOrder(SquadOrderTypes::Idle, BWAPI::Position(BWAPI::Broodwar->self()->getStartLocation()), 100, "Chill Out");
  	_squadData.addSquad("Idle", Squad("Idle", idleOrder, IdlePriority));
  
      // the main attack squad that will pressure the enemy's closest base location
      SquadOrder mainAttackOrder(SquadOrderTypes::Attack, getMainAttackLocation(), 800, "Attack Enemy Base");
  	_squadData.addSquad("MainAttack", Squad("MainAttack", mainAttackOrder, AttackPriority));
  
      BWAPI::Position ourBasePosition = BWAPI::Position(BWAPI::Broodwar->self()->getStartLocation());
  
      // the scout defense squad will handle chasing the enemy worker scout
      SquadOrder enemyScoutDefense(SquadOrderTypes::Defend, ourBasePosition, 900, "Get the scout");
      _squadData.addSquad("ScoutDefense", Squad("ScoutDefense", enemyScoutDefense, ScoutDefensePriority));
  
      // add a drop squad if we are using a drop strategy
  	if (Config::Strategy::StrategyName == "Protoss_Drop" || Config::Strategy::StrategyName == "Protoss_DTDrop")
      {
          SquadOrder zealotDrop(SquadOrderTypes::Drop, ourBasePosition, 900, "Wait for transport");
          _squadData.addSquad("Drop", Squad("Drop", zealotDrop, DropPriority));
  
  		// Drop attack squad attempts to attack enemy workers
  		// units only added to it when Drop squad units reach destination
  		SquadOrder dropAttack(SquadOrderTypes::DropAttack, getMainAttackLocation(), 800, "Drop Attack");
  		_squadData.addSquad("DropAttack", Squad("DropAttack", dropAttack, DropAttackPriority));
      }
  
      _initialized = true;
  }
  
  bool CombatCommander::isSquadUpdateFrame()
  {
  	return BWAPI::Broodwar->getFrameCount() % 10 == 0;
  }
  
  void CombatCommander::update(const BWAPI::Unitset & combatUnits)
  {
      if (!Config::Modules::UsingCombatCommander)
      {
          return;
      }
  
      if (!_initialized)
      {
          initializeSquads();
      }
  
  	_combatUnits = combatUnits;
  
  	if (isSquadUpdateFrame())
  	{
          updateIdleSquad();
          updateDropSquads();
          updateScoutDefenseSquad();
  		updateDefenseSquads();
  		updateAttackSquads();
  		if (Config::Strategy::PreventBaiting) {
  			updateNeutralZoneAttackSquads();
  		}
  		if (Config::Strategy::BaitEnemy) {
  			updateBaitSquads();
  		}
  		updateDropAttackSquads();
  	}
  
  	_squadData.update();
  }
  
  void CombatCommander::updateIdleSquad()
  {
      Squad & idleSquad = _squadData.getSquad("Idle");
      for (auto & unit : _combatUnits)
      {
          // if it hasn't been assigned to a squad yet, put it in the low priority idle squad
          if (_squadData.canAssignUnitToSquad(unit, idleSquad))
          {
              idleSquad.addUnit(unit);
          }
      }
  }
  
  void CombatCommander::updateNeutralZoneAttackSquads()
  {
  	Squad & neutralZoneAttackSquad = _squadData.getSquad("NeutralZoneAttack");
  	SquadOrder neutralZoneAttackSquadOrder(SquadOrderTypes::Attack, getMainAttackLocation(), 800, "Attack Enemy Base");
  	neutralZoneAttackSquad.setSquadOrder(neutralZoneAttackSquadOrder);
  }
  
  void CombatCommander::updateBaitSquads()
  {
  	Squad & baitSquad = _squadData.getSquad("Bait");
  	SquadOrder baitSquadOrder(SquadOrderTypes::Attack, getMainAttackLocation(), 800, "Bait Enemy in Neutral Zone");
  	baitSquad.setSquadOrder(baitSquadOrder);
  }
  
  void CombatCommander::updateAttackSquads()
  {
      Squad & mainAttackSquad = _squadData.getSquad("MainAttack");
  	SquadData squadData = SquadData();
      for (auto & unit : _combatUnits)
      {
          if (unit->getType() == BWAPI::UnitTypes::Zerg_Scourge && UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk) < 30)
          {
              continue;
          }
  
          // get every unit of a lower priority and put it into the attack squad
          if (!unit->getType().isWorker() && (unit->getType() != BWAPI::UnitTypes::Zerg_Overlord) && _squadData.canAssignUnitToSquad(unit, mainAttackSquad))
          {
              _squadData.assignUnitToSquad(unit, mainAttackSquad);
          }
      }
  
      SquadOrder mainAttackOrder(SquadOrderTypes::Attack, getMainAttackLocation(), 800, "Attack Enemy Base");
      mainAttackSquad.setSquadOrder(mainAttackOrder);
  }
  
  void CombatCommander::updateDropAttackSquads()
  {
  	if (Config::Strategy::StrategyName != "Protoss_Drop" && Config::Strategy::StrategyName != "Protoss_DTDrop")
  	{
  		return;
  	}
  
  	Squad & dropAttackSquad = _squadData.getSquad("DropAttack");
  	SquadOrder dropAttackOrder(SquadOrderTypes::DropAttack, getMainAttackLocation(), 800, "Drop Attack");
  	dropAttackSquad.setSquadOrder(dropAttackOrder);
  }
  
  void CombatCommander::dropSquadReassign(BWAPI::Unitset units)
  {
  	// If close to enemy base, attack enemy base, don't need to be part of drop squad anymore
  	BWAPI::Position enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy())->getPosition();
  	Squad &dropAttackSquad = _squadData.getSquad("DropAttack");
  
  	for (auto & unit : units)
  	{
  		if ((!unit->isFlying()) && enemyBaseLocation && (unit->getDistance(enemyBaseLocation) < 500))
  		{
  			_squadData.assignUnitToSquad(unit, dropAttackSquad);
  		}
  	}
  }
  
  void CombatCommander::updateDropSquads()
  {
  	if (Config::Strategy::StrategyName != "Protoss_Drop" && Config::Strategy::StrategyName != "Protoss_DTDrop")
      {
          return;
      }
  
      Squad & dropSquad = _squadData.getSquad("Drop");
  
      // figure out how many units the drop squad needs
      int transportSpotsRemaining = 8;
      auto & dropUnits = dropSquad.getUnits();
  
  	bool dropSquadHasTransport = false;
  
  	// If units in dropsquad are near enemy base, reassign them to DropAttack squad
  	dropSquadReassign(dropUnits);
  
      for (auto & unit : dropUnits)
      {
  		if (unit->isFlying() && unit->getType().spaceProvided() > 0)
  		{
  			dropSquadHasTransport = true;
  		}
  		else
  		{
  			transportSpotsRemaining -= unit->getType().spaceRequired();
  		}
      }
  
      // if there are still units to be added to the drop squad, do it
      if (transportSpotsRemaining > 0 || !dropSquadHasTransport)
      {
          // take our first amount of combat units that fill up a transport and add them to the drop squad
          for (auto & unit : _combatUnits)
          {
              // if this is a transport unit and we don't have one in the squad yet, add it
              if (!dropSquadHasTransport && (unit->getType().spaceProvided() > 0 && unit->isFlying()))
              {
                  _squadData.assignUnitToSquad(unit, dropSquad);
                  dropSquadHasTransport = true;
                  continue;
              }
  
              if (unit->getType().spaceRequired() > transportSpotsRemaining)
              {
                  continue;
              }
  
  			BWAPI::Position ourBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->self())->getPosition();
              // get every unit of a lower priority at our base and put it into the drop squad
  			if (!unit->getType().isWorker() && _squadData.canAssignUnitToSquad(unit, dropSquad)
  				&& unit->getDistance(ourBaseLocation) < 500)
  			{
  				// if Dark Templar drop, only load Dark Templars
  				if (Config::Strategy::StrategyName == "Protoss_DTDrop" && unit->getType().getName() != "Protoss_Dark_Templar")
  				{	continue;	}
  
  				_squadData.assignUnitToSquad(unit, dropSquad);
  				transportSpotsRemaining -= unit->getType().spaceRequired();
              }
          }
      }
      // otherwise the drop squad is full, so execute the order
      else
      {
  		BWAPI::Unit transport = dropSquad.getTransportShip();
  		BWAPI::Position enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy())->getPosition();
  		BWAPI::Position ourBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->self())->getPosition();
  
  		// if transport exists and is close to base
  		if (transport && transport->getDistance(ourBaseLocation) < 500)
  		{
  			// If enemy base is far away, get in transport
  			if (enemyBaseLocation && (dropSquad.calcCenter().getDistance(enemyBaseLocation) > 800))
  			{
  				for (auto & unit : dropSquad.getUnits())
  				{
  					if (!unit->isFlying())
  					{
  						// issue order to get in transport
  						Micro::SmartRightClick(unit, transport);
  					}
  				}
  			}
  		}
      }
  
  }
  
  void CombatCommander::updateScoutDefenseSquad() 
  {
      if (_combatUnits.empty()) 
      { 
          return; 
      }
  
      // if the current squad has units in it then we can ignore this
      Squad & scoutDefenseSquad = _squadData.getSquad("ScoutDefense");
    
      // get the region that our base is located in
      BWTA::Region * myRegion = BWTA::getRegion(BWAPI::Broodwar->self()->getStartLocation());
      if (!myRegion && myRegion->getCenter().isValid())
      {
          return;
      }
  
      // get all of the enemy units in this region
  	BWAPI::Unitset enemyUnitsInRegion;
      for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
      {
          if (BWTA::getRegion(BWAPI::TilePosition(unit->getPosition())) == myRegion)
          {
              enemyUnitsInRegion.insert(unit);
          }
      }
  
      // if there's an enemy worker in our region then assign someone to chase him
      bool assignScoutDefender = enemyUnitsInRegion.size() == 1 && (*enemyUnitsInRegion.begin())->getType().isWorker();
  
      // if our current squad is empty and we should assign a worker, do it
      if (scoutDefenseSquad.isEmpty() && assignScoutDefender)
      {
          // the enemy worker that is attacking us
          BWAPI::Unit enemyWorker = *enemyUnitsInRegion.begin();
  
          // get our worker unit that is mining that is closest to it
          BWAPI::Unit workerDefender = findClosestWorkerToTarget(_combatUnits, enemyWorker);
  
  		if (enemyWorker && workerDefender)
  		{
  			// grab it from the worker manager and put it in the squad
              if (_squadData.canAssignUnitToSquad(workerDefender, scoutDefenseSquad))
              {
  			    WorkerManager::Instance().setCombatWorker(workerDefender);
                  _squadData.assignUnitToSquad(workerDefender, scoutDefenseSquad);
              }
  		}
      }
      // if our squad is not empty and we shouldn't have a worker chasing then take him out of the squad
      else if (!scoutDefenseSquad.isEmpty() && !assignScoutDefender)
      {
          for (auto & unit : scoutDefenseSquad.getUnits())
          {
              unit->stop();
              if (unit->getType().isWorker())
              {
                  WorkerManager::Instance().finishedWithWorker(unit);
              }
          }
  
          scoutDefenseSquad.clear();
      }
  }
  
  void CombatCommander::updateDefenseSquads() 
  {
  	if (_combatUnits.empty()) 
      { 
          return; 
      }
      
      BWTA::BaseLocation * enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy());
      BWTA::Region * enemyRegion = nullptr;
      if (enemyBaseLocation)
      {
          enemyRegion = BWTA::getRegion(enemyBaseLocation->getPosition());
      }
  
  	// for each of our occupied regions
  	for (BWTA::Region * myRegion : InformationManager::Instance().getOccupiedRegions(BWAPI::Broodwar->self()))
  	{
          // don't defend inside the enemy region, this will end badly when we are stealing gas
          if (myRegion == enemyRegion)
          {
              continue;
          }
  
  		BWAPI::Position regionCenter = myRegion->getCenter();
  		if (!regionCenter.isValid())
  		{
  			continue;
  		}
  
  		// start off assuming all enemy units in region are just workers
  		int numDefendersPerEnemyUnit = 2;
  
  		// all of the enemy units in this region
  		BWAPI::Unitset enemyUnitsInRegion;
          for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
          {
              // if it's an overlord, don't worry about it for defense, we don't care what they see
              if (unit->getType() == BWAPI::UnitTypes::Zerg_Overlord)
              {
                  continue;
              }
  
              if (BWTA::getRegion(BWAPI::TilePosition(unit->getPosition())) == myRegion)
              {
                  enemyUnitsInRegion.insert(unit);
              }
          }
  
          // we can ignore the first enemy worker in our region since we assume it is a scout
          for (auto & unit : enemyUnitsInRegion)
          {
              if (unit->getType().isWorker())
              {
                  enemyUnitsInRegion.erase(unit);
                  break;
              }
          }
  
          int numEnemyFlyingInRegion = std::count_if(enemyUnitsInRegion.begin(), enemyUnitsInRegion.end(), [](BWAPI::Unit u) { return u->isFlying(); });
          int numEnemyGroundInRegion = std::count_if(enemyUnitsInRegion.begin(), enemyUnitsInRegion.end(), [](BWAPI::Unit u) { return !u->isFlying(); });
  
          std::stringstream squadName;
          squadName << "Base Defense " << regionCenter.x << " " << regionCenter.y; 
          
          // if there's nothing in this region to worry about
          if (enemyUnitsInRegion.empty())
          {
              // if a defense squad for this region exists, remove it
              if (_squadData.squadExists(squadName.str()))
              {
                  _squadData.getSquad(squadName.str()).clear();
              }
              
              // and return, nothing to defend here
              continue;
          }
          else 
          {
              // if we don't have a squad assigned to this region already, create one
              if (!_squadData.squadExists(squadName.str()))
              {
                  SquadOrder defendRegion(SquadOrderTypes::Defend, regionCenter, 32 * 25, "Defend Region!");
                  _squadData.addSquad(squadName.str(), Squad(squadName.str(), defendRegion, BaseDefensePriority));
              }
          }
  
          // assign units to the squad
          if (_squadData.squadExists(squadName.str()))
          {
              Squad & defenseSquad = _squadData.getSquad(squadName.str());
  
              // figure out how many units we need on defense
  	        int flyingDefendersNeeded = numDefendersPerEnemyUnit * numEnemyFlyingInRegion;
  	        int groundDefensersNeeded = numDefendersPerEnemyUnit * numEnemyGroundInRegion;
  
              updateDefenseSquadUnits(defenseSquad, flyingDefendersNeeded, groundDefensersNeeded);
          }
          else
          {
              UAB_ASSERT_WARNING(false, "Squad should have existed: %s", squadName.str().c_str());
          }
  	}
  
      // for each of our defense squads, if there aren't any enemy units near the position, remove the squad
      std::set<std::string> uselessDefenseSquads;
      for (const auto & kv : _squadData.getSquads())
      {
          const Squad & squad = kv.second;
          const SquadOrder & order = squad.getSquadOrder();
  
          if (order.getType() != SquadOrderTypes::Defend)
          {
              continue;
          }
  
          bool enemyUnitInRange = false;
          for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
          {
              if (unit->getPosition().getDistance(order.getPosition()) < order.getRadius())
              {
                  enemyUnitInRange = true;
                  break;
              }
          }
  
          if (!enemyUnitInRange)
          {
              _squadData.getSquad(squad.getName()).clear();
          }
      }
  }
  
  void CombatCommander::updateDefenseSquadUnits(Squad & defenseSquad, const size_t & flyingDefendersNeeded, const size_t & groundDefendersNeeded)
  {
      const BWAPI::Unitset & squadUnits = defenseSquad.getUnits();
      size_t flyingDefendersInSquad = std::count_if(squadUnits.begin(), squadUnits.end(), UnitUtil::CanAttackAir);
      size_t groundDefendersInSquad = std::count_if(squadUnits.begin(), squadUnits.end(), UnitUtil::CanAttackGround);
  
      // if there's nothing left to defend, clear the squad
      if (flyingDefendersNeeded == 0 && groundDefendersNeeded == 0)
      {
          defenseSquad.clear();
          return;
      }
  
      // add flying defenders if we still need them
      size_t flyingDefendersAdded = 0;
      while (flyingDefendersNeeded > flyingDefendersInSquad + flyingDefendersAdded)
      {
          BWAPI::Unit defenderToAdd = findClosestDefender(defenseSquad, defenseSquad.getSquadOrder().getPosition(), true);
  
          // if we find a valid flying defender, add it to the squad
          if (defenderToAdd)
          {
              _squadData.assignUnitToSquad(defenderToAdd, defenseSquad);
              ++flyingDefendersAdded;
          }
          // otherwise we'll never find another one so break out of this loop
          else
          {
              break;
          }
      }
  
      // add ground defenders if we still need them
      size_t groundDefendersAdded = 0;
      while (groundDefendersNeeded > groundDefendersInSquad + groundDefendersAdded)
      {
          BWAPI::Unit defenderToAdd = findClosestDefender(defenseSquad, defenseSquad.getSquadOrder().getPosition(), false);
  
          // if we find a valid ground defender add it
          if (defenderToAdd)
          {
              _squadData.assignUnitToSquad(defenderToAdd, defenseSquad);
              ++groundDefendersAdded;
          }
          // otherwise we'll never find another one so break out of this loop
          else
          {
              break;
          }
      }
  }
  
  BWAPI::Unit CombatCommander::findClosestDefender(const Squad & defenseSquad, BWAPI::Position pos, bool flyingDefender) 
  {
  	BWAPI::Unit closestDefender = nullptr;
  	double minDistance = std::numeric_limits<double>::max();
  
      int zerglingsInOurBase = numZerglingsInOurBase();
      bool zerglingRush = zerglingsInOurBase > 0 && BWAPI::Broodwar->getFrameCount() < 5000;
  
  	for (auto & unit : _combatUnits) 
  	{
  		if ((flyingDefender && !UnitUtil::CanAttackAir(unit)) || (!flyingDefender && !UnitUtil::CanAttackGround(unit)))
          {
              continue;
          }
  
          if (!_squadData.canAssignUnitToSquad(unit, defenseSquad))
          {
              continue;
          }
  
          // add workers to the defense squad if we are being rushed very quickly
          if (!Config::Micro::WorkersDefendRush || (unit->getType().isWorker() && !zerglingRush && !beingBuildingRushed()))
          {
              continue;
          }
  
          double dist = unit->getDistance(pos);
          if (!closestDefender || (dist < minDistance))
          {
              closestDefender = unit;
              minDistance = dist;
          }
  	}
  
  	return closestDefender;
  }
  
  BWAPI::Position CombatCommander::getDefendLocation()
  {
  	return BWTA::getRegion(BWTA::getStartLocation(BWAPI::Broodwar->self())->getTilePosition())->getCenter();
  }
  
  void CombatCommander::drawSquadInformation(int x, int y)
  {
  	_squadData.drawSquadInformation(x, y);
  }
  
  BWAPI::Position CombatCommander::getMainAttackLocation()
  {
      BWTA::BaseLocation * enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy());
  
      // First choice: Attack an enemy region if we can see units inside it
      if (enemyBaseLocation)
      {
          BWAPI::Position enemyBasePosition = enemyBaseLocation->getPosition();
  
          // get all known enemy units in the area
          BWAPI::Unitset enemyUnitsInArea;
  		MapGrid::Instance().GetUnits(enemyUnitsInArea, enemyBasePosition, 800, false, true);
  
          bool onlyOverlords = true;
          for (auto & unit : enemyUnitsInArea)
          {
              if (unit->getType() != BWAPI::UnitTypes::Zerg_Overlord)
              {
                  onlyOverlords = false;
              }
          }
  
          if (!BWAPI::Broodwar->isExplored(BWAPI::TilePosition(enemyBasePosition)) || !enemyUnitsInArea.empty())
          {
              if (!onlyOverlords)
              {
                  return enemyBaseLocation->getPosition();
              }
          }
      }
  
      // Second choice: Attack known enemy buildings
      for (const auto & kv : InformationManager::Instance().getUnitInfo(BWAPI::Broodwar->enemy()))
      {
          const UnitInfo & ui = kv.second;
  
          if (ui.type.isBuilding() && ui.lastPosition != BWAPI::Positions::None)
  		{
  			return ui.lastPosition;	
  		}
      }
  
      // Third choice: Attack visible enemy units that aren't overlords
      for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
  	{
          if (unit->getType() == BWAPI::UnitTypes::Zerg_Overlord)
          {
              continue;
          }
  
  		if (UnitUtil::IsValidUnit(unit) && unit->isVisible())
  		{
  			return unit->getPosition();
  		}
  	}
  
      // Fourth choice: We can't see anything so explore the map attacking along the way
      return MapGrid::Instance().getLeastExplored();
  }
  
  BWAPI::Unit CombatCommander::findClosestWorkerToTarget(BWAPI::Unitset & unitsToAssign, BWAPI::Unit target)
  {
      UAB_ASSERT(target != nullptr, "target was null");
  
      if (!target)
      {
          return nullptr;
      }
  
      BWAPI::Unit closestMineralWorker = nullptr;
      double closestDist = 100000;
      
      // for each of our workers
  	for (auto & unit : unitsToAssign)
  	{
          if (!unit->getType().isWorker())
          {
              continue;
          }
  
  		// if it is a move worker
          if (WorkerManager::Instance().isFree(unit)) 
  		{
  			double dist = unit->getDistance(target);
  
              if (!closestMineralWorker || dist < closestDist)
              {
                  closestMineralWorker = unit;
                  dist = closestDist;
              }
  		}
  	}
  
      return closestMineralWorker;
  }
  
  // when do we want to defend with our workers?
  // this function can only be called if we have no fighters to defend with
  int CombatCommander::defendWithWorkers()
  {
  	// our home nexus position
  	BWAPI::Position homePosition = BWTA::getStartLocation(BWAPI::Broodwar->self())->getPosition();;
  
  	// enemy units near our workers
  	int enemyUnitsNearWorkers = 0;
  
  	// defense radius of nexus
  	int defenseRadius = 300;
  
  	// fill the set with the types of units we're concerned about
  	for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
  	{
  		// if it's a zergling or a worker we want to defend
  		if (unit->getType() == BWAPI::UnitTypes::Zerg_Zergling)
  		{
  			if (unit->getDistance(homePosition) < defenseRadius)
  			{
  				enemyUnitsNearWorkers++;
  			}
  		}
  	}
  
  	// if there are enemy units near our workers, we want to defend
  	return enemyUnitsNearWorkers;
  }
  
  int CombatCommander::numZerglingsInOurBase()
  {
      int concernRadius = 600;
      int zerglings = 0;
      BWAPI::Position ourBasePosition = BWAPI::Position(BWAPI::Broodwar->self()->getStartLocation());
      
      // check to see if the enemy has zerglings as the only attackers in our base
      for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
      {
          if (unit->getType() != BWAPI::UnitTypes::Zerg_Zergling)
          {
              continue;
          }
  
          if (unit->getDistance(ourBasePosition) < concernRadius)
          {
              zerglings++;
          }
      }
  
      return zerglings;
  }
  
  bool CombatCommander::beingBuildingRushed()
  {
      int concernRadius = 1200;
      BWAPI::Position ourBasePosition = BWAPI::Position(BWAPI::Broodwar->self()->getStartLocation());
      
      // check to see if the enemy has zerglings as the only attackers in our base
      for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
      {
          if (unit->getType().isBuilding())
          {
              return true;
          }
      }
  
      return false;
  }