kora-bot/Source/CBase.cpp
2023-04-10 18:31:02 -05:00

327 lines
8.5 KiB
C++

#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;
for (auto a : Util::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<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)
{
TilePosition average = Military::getChokepointCenter(area, neighborArea);
// 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;
}