2023-04-10 23:31:02 +00:00
|
|
|
#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<const BWEM::Area*, bool> 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;
|
2023-04-11 17:56:09 +00:00
|
|
|
for (auto a : CMap::getPath(area, targetArea))
|
2023-04-10 23:31:02 +00:00
|
|
|
{
|
|
|
|
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<const BWEM::Area*, bool> target = Util::getTargetArea(area, this->startLocation);
|
|
|
|
const BWEM::Area* targetArea = target.first;
|
|
|
|
bool foundTarget = target.second;
|
|
|
|
|
|
|
|
if (foundTarget)
|
|
|
|
{
|
|
|
|
std::pair<const BWEM::Area*, bool> neighbor = Util::getNeighborArea(area, targetArea);
|
|
|
|
const BWEM::Area* neighborArea = neighbor.first;
|
|
|
|
bool foundNeighbor = neighbor.second;
|
|
|
|
if (foundNeighbor)
|
|
|
|
{
|
2023-04-12 02:21:27 +00:00
|
|
|
TilePosition average = CMap::getChokepointCenter(area, neighborArea);
|
2023-04-10 23:31:02 +00:00
|
|
|
|
|
|
|
// this caused an out-of-range exception in scouting.cpp:
|
|
|
|
// std::vector<double> direction = BuildingPlacement::getDirection(Macro::players.at(Macro::selfID).bases.at(1).tilePosition);
|
|
|
|
std::vector<double> 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<UnitType> 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;
|
|
|
|
}
|