#include "CBase.h" namespace { auto & map = BWEM::Map::Instance(); } CBase::CBase(BWAPI::TilePosition tp) { this->area = map.GetArea(tp); this->tilePosition = tp; this->position = Position(tp.x * 32, tp.y * 32); this->initialize(); } CBase::CBase(BWAPI::Unit unit) { this->base = unit; this->tilePosition = unit->getTilePosition(); this->position = unit->getPosition(); this->initialize(); this->area = map.GetArea(this->tilePosition); } CBase::CBase() { } void CBase::addWorker(std::string resource) { if (resource == "gas") { this->gasWorkers++; } if (resource == "minerals") { this->mineralWorkers++; } } int CBase::desiredSporeCount() { int count = 1; if (!Macro::enemyIDs.empty()) { if (Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Zerg_Spire) > 0) count++; count += ceil(Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Starport) / 2.0); count += ceil(Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Stargate) / 2.0); count += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Zerg_Mutalisk) / 5; } return 1; } // things to consider - // how many resources are left at this base // if it's a main (has tech buildings) // if it's a natural (leads to a main) int CBase::desiredSunkenCount() { if (this->exposedToCenter && Broodwar->getFrameCount() < 600 * 24) return 5; else if (this->exposedToCenter) return 2; else return 0; } int CBase::extractorsBuilt() { int count = 0; for (auto u : Macro::players.at(Macro::selfID).units) { if ((u.unit->getType().isRefinery()) && u.unit->getDistance(this->position) < 400) { count++; } } return count; } void CBase::getDefense() { this->creepColonyCount = 0; this->sunkenCount = 0; this->sporeCount = 0; for (auto u : Macro::players.at(Macro::selfID).units) { if (u.unit->getType() == UnitTypes::Zerg_Sunken_Colony) { if (u.unit->getDistance(this->position) < 400) this->sunkenCount++; } else if (u.unit->getType() == UnitTypes::Zerg_Spore_Colony) { if (u.unit->getDistance(this->position) < 400) this->sporeCount++; } else if (u.unit->getType() == UnitTypes::Zerg_Creep_Colony) { if (u.unit->getDistance(this->position) < 400) this->creepColonyCount++; } } /*Broodwar->registerEvent( [this](Game*) { Broodwar->drawTextMap(Position(this->position.x, this->position.y + 20), "creep count = %d", this->creepColonyCount); }, nullptr, 23);*/ } void CBase::getResources() { if (this->base->isCompleted()) { this->mineralPatches = 0; this->gasPatches = 0; this->gasAmount = 0; this->minsAmount = 0; for (auto unit : BWAPI::Broodwar->getNeutralUnits()) { if ((unit->getType() == BWAPI::UnitTypes::Resource_Vespene_Geyser || unit->getType().isRefinery()) && unit->getPosition().isValid() && unit->getDistance(this->position) < 400) { this->gasPatches++; this->gasAmount += unit->getInitialResources(); } if (unit->getType() == BWAPI::UnitTypes::Resource_Mineral_Field && unit->getPosition().isValid() && unit->getDistance(this->position) < 400) { this->mineralPatches++; this->minsAmount += unit->getInitialResources(); } } } } void CBase::initialize() { this->complete = false; this->creepColonyCount = 0; this->exposedToCenter = this->isExposedToCenter(); this->gasPatches = 0; this->gasWorkers = 0; this->gasAmount = 0; this->lastAttack = 0; this->mineralPatches = 0; this->mineralWorkers = 0; this->minsAmount = 0; this->setDefenseLocation(); this->sporeCount = 0; this->startLocation = isStartLocation(); this->sunkenCount = 0; this->initializeResources(); } // initialize data for this CBase void CBase::initializeResources() { this->mineralPatches = 0; this->gasPatches = 0; this->gasAmount = 0; this->minsAmount = 0; for (const BWEM::Area& a : map.Areas()) { for (const BWEM::Base& b : a.Bases()) { if (b.Location() == this->tilePosition) { this->gasPatches = b.Geysers().size(); this->mineralPatches = b.Minerals().size(); for (auto g : b.Geysers()) { this->gasAmount += g->Amount(); } for (auto m : b.Minerals()) { this->minsAmount += m->Amount(); } } } } } // if a base doesn't have any other bases between it // and the center of the map, then it's easier to attack bool CBase::isExposedToCenter() { const BWEM::Area* area = map.GetArea(this->tilePosition); std::pair target = Util::getTargetArea(area, this->startLocation); const BWEM::Area* targetArea = target.first; bool foundTarget = target.second; if (foundTarget) { // get a path from the target Area to the player's starting base. // if an Area has more than two neighbors, it's not part of a // straight-line path and probably exposed to the center. // if it contains a base, then the target Area is not // exposed to the center int count = 0; for (auto a : CMap::getPath(area, targetArea)) { if (a->Id() == area->Id()) continue; if (a->AccessibleNeighbours().size() > 2) return true; if (a->Bases().size() > 0) { return false; } count++; } } else { Broodwar << "Couldn't find target area in CBase::isExposedToCenter" << std::endl; } return true; } // determines if base is owned by someone bool CBase::isOwned() { for (size_t i = 0; i < Macro::players.size(); i++) { for (size_t j = 0; j < Macro::players.at(i).bases.size(); j++) { int x = Macro::players.at(i).bases.at(j).tilePosition.x; int y = Macro::players.at(i).bases.at(j).tilePosition.y; if (this->tilePosition.x == x && this->tilePosition.y == y) return true; } } return false; } bool CBase::isSaturated(std::string resource) { if (resource == "gas") { return this->gasWorkers >= this->extractorsBuilt() * 3; } else { return this->mineralWorkers >= this->mineralPatches * 2; } } bool CBase::isStartLocation() { for (auto sl : Broodwar->getStartLocations()) { if (TilePosition(sl) == this->tilePosition) return true; } return false; } //@TODO clean this up // find a spot to put static defense by. // if this is a main base, build around the hatchery. // otherwise, build pointed toward another start location void CBase::setDefenseLocation() { const BWEM::Area* area = map.GetArea(this->tilePosition); TilePosition tp = this->tilePosition; std::pair target = Util::getTargetArea(area, this->startLocation); const BWEM::Area* targetArea = target.first; bool foundTarget = target.second; if (foundTarget) { std::pair neighbor = Util::getNeighborArea(area, targetArea); const BWEM::Area* neighborArea = neighbor.first; bool foundNeighbor = neighbor.second; if (foundNeighbor) { TilePosition average = CMap::getChokepointCenter(area, neighborArea); // this caused an out-of-range exception in scouting.cpp: // std::vector direction = BuildingPlacement::getDirection(Macro::players.at(Macro::selfID).bases.at(1).tilePosition); std::vector direction = BuildingPlacement::getDirection(this->tilePosition, average); tp.x += direction.at(x_vector) * -5; tp.y += direction.at(y_vector) * -5; } else { Broodwar << "Couldn't find neighbor area in CBase::setDefenseLocation()" << std::endl; Broodwar << targetArea->TopLeft() << std::endl; } } else { //Broodwar << area->TopLeft() << std::endl; Broodwar << "Couldn't find target area in CBase::setDefenseLocation()" << std::endl; } this->defenseLocation = tp; } void CBase::subtractWorker(std::string resource) { if (resource == "gas") { this->gasWorkers--; } if (resource == "minerals") { this->mineralWorkers--; } } // get number of tech buildings at this base int CBase::techCount() { std::vector techBuildingTypes = { UnitTypes::Zerg_Spawning_Pool, UnitTypes::Zerg_Hydralisk_Den, UnitTypes::Zerg_Evolution_Chamber, UnitTypes::Zerg_Spire, UnitTypes::Zerg_Queens_Nest, UnitTypes::Zerg_Greater_Spire, UnitTypes::Zerg_Ultralisk_Cavern, UnitTypes::Zerg_Defiler_Mound }; int nearbyTech = 0; for (auto u : Macro::players.at(Macro::selfID).units) { if ((std::find(techBuildingTypes.begin(), techBuildingTypes.end(), u.unit->getType()) != techBuildingTypes.end()) && u.unit->getDistance(this->position) < 32 * 15) nearbyTech++; } return nearbyTech; }