526 lines
13 KiB
C++
526 lines
13 KiB
C++
#include "Macro.h"
|
|
|
|
std::vector<CBase> Macro::bases;
|
|
std::vector<int> Macro::enemyIDs;
|
|
std::vector<Location> Macro::locations;
|
|
std::vector<CPlayer> Macro::players;
|
|
std::vector<QueueEntry> Macro::queue;
|
|
std::vector<CUnit> Macro::scouts;
|
|
std::vector<Squad> Macro::squads;
|
|
int Macro::lastAttack = 0;
|
|
int Macro::selfID;
|
|
TilePosition Macro::lastAttackLocation;
|
|
TilePosition Macro::lastBuildLocation;
|
|
bool Macro::macroHatch;
|
|
bool Macro::newBase;
|
|
bool Macro::stopProduction;
|
|
bool Macro::workerScout = false;
|
|
namespace { auto & map = BWEM::Map::Instance(); }
|
|
|
|
void Macro::addBase(TilePosition tp)
|
|
{
|
|
bases.push_back(CBase(tp));
|
|
}
|
|
|
|
void Macro::addLocation(TilePosition tp, bool StartLocation)
|
|
{
|
|
locations.push_back(Location(tp, StartLocation));
|
|
}
|
|
|
|
void Macro::addPlayer(BWAPI::Player player)
|
|
{
|
|
stopProduction = false; // needed to initialize it somewhere
|
|
players.push_back(CPlayer(player));
|
|
}
|
|
|
|
void Macro::addSquad()
|
|
{
|
|
squads.push_back(Squad());
|
|
}
|
|
|
|
// does top-level analysis of what to build
|
|
//@TODO clean this up
|
|
void Macro::analyzeQueue()
|
|
{
|
|
std::string entry = "";
|
|
bool status = false;
|
|
double score = 0;
|
|
int index = -1;
|
|
int count = 0;
|
|
|
|
for (auto e : queue)
|
|
{
|
|
if ((!e.unit.isBuilding() && e.unit.supplyProvided() == 0) || !e.inProgress)
|
|
{
|
|
if (e.isTech()) e.score = Analysis::analyze(e.tech);
|
|
else if (e.isUnit()) e.score = Analysis::analyze(e.unit);
|
|
else if (e.isUpgrade()) e.score = Analysis::analyze(e.upgrade);
|
|
if (e.score > score)
|
|
{
|
|
score = e.score;
|
|
index = count;
|
|
}
|
|
}
|
|
count++;
|
|
}
|
|
|
|
if (index > -1)
|
|
{
|
|
if (queue.at(index).isUnit())
|
|
{
|
|
if (!queue.at(index).unit.isBuilding())
|
|
{
|
|
if (!stopProduction || Broodwar->self()->minerals() > 300 + queue.at(index).unit.mineralPrice())
|
|
{
|
|
queue.at(index).inProgress = getTrainingBase().unit->train(queue.at(index).unit);
|
|
if (queue.at(index).inProgress) queue.erase(queue.begin() + index);
|
|
}
|
|
}
|
|
else if (queue.at(index).unit.isBuilding())
|
|
{
|
|
if (queue.at(index).unit == UnitTypes::Zerg_Lair || queue.at(index).unit == UnitTypes::Zerg_Hive)
|
|
{
|
|
queue.at(index).inProgress = players.at(selfID).bases.front().base->morph(queue.at(index).unit);
|
|
}
|
|
else
|
|
{
|
|
queue.at(index).inProgress = true;
|
|
// find a drone and send them off
|
|
int workerIndex = Util::buildWorkerIndex();
|
|
|
|
if (workerIndex > -1)
|
|
{
|
|
players.at(selfID).bases.at(players.at(selfID).units.at(workerIndex).miningBase).subtractWorker("minerals");
|
|
if (newBase && queue.at(index).unit == UnitTypes::Zerg_Hatchery && !stopProduction)
|
|
{
|
|
players.at(selfID).units.at(workerIndex).action = "expand";
|
|
CBase base = Util::getNextExpand(selfID);
|
|
players.at(selfID).units.at(workerIndex).target = base.tilePosition;
|
|
players.at(selfID).units.at(workerIndex).unit->move(Position(players.at(selfID).units.at(workerIndex).target));
|
|
stopProduction = true;
|
|
}
|
|
else
|
|
{
|
|
players.at(selfID).units.at(workerIndex).action = "build";
|
|
TilePosition target = BuildingPlacement::getPosition(players.at(selfID).units.at(workerIndex).targetUnit);
|
|
if (queue.at(index).unit == UnitTypes::Zerg_Sunken_Colony
|
|
|| queue.at(index).unit == UnitTypes::Zerg_Spore_Colony)
|
|
{
|
|
players.at(selfID).units.at(workerIndex).targetUnit = UnitTypes::Zerg_Creep_Colony;
|
|
target = BuildingPlacement::getPosition(queue.at(index).unit);
|
|
}
|
|
else
|
|
{
|
|
players.at(selfID).units.at(workerIndex).targetUnit = queue.at(index).unit;
|
|
}
|
|
players.at(selfID).units.at(workerIndex).target = target;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (queue.at(index).isUpgrade())
|
|
{
|
|
if (!stopProduction || Broodwar->self()->minerals() > 300 + queue.at(index).upgrade.mineralPrice())
|
|
{
|
|
if (Util::findUpgradeBuilding(queue.at(index).upgrade) > -1)
|
|
{
|
|
players.at(selfID).units.at(Util::findUpgradeBuilding(queue.at(index).upgrade)).unit->upgrade(queue.at(index).upgrade);
|
|
queue.erase(queue.begin() + index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// assigns a base to a player
|
|
void Macro::assignBase(int playerID, CBase base)
|
|
{
|
|
players.at(playerID).bases.push_back(base);
|
|
}
|
|
|
|
// find the base that's closest to the unit and also unsaturated
|
|
int Macro::findMiningBase(BWAPI::Unit unit, std::string type)
|
|
{
|
|
bool check = false;
|
|
int distance = 10000;
|
|
int index = -1;
|
|
index = Util::getClosestOwnedBase(unit);
|
|
|
|
if (players.at(selfID).bases.at(index).isSaturated(type)) index = -1;
|
|
|
|
if (index == -1)
|
|
{
|
|
for (size_t i = 0; i < players.at(selfID).bases.size(); i++)
|
|
{
|
|
if (players.at(selfID).bases.at(i).complete)
|
|
{
|
|
int d = unit->getDistance(players.at(selfID).bases.at(i).base);
|
|
|
|
if (d < distance && !players.at(selfID).bases.at(i).isSaturated(type))
|
|
{
|
|
distance = d;
|
|
index = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// stops a drone from mining minerals or gas
|
|
void Macro::freeUnit(int index)
|
|
{
|
|
if (players.at(selfID).units.at(index).isMining())
|
|
{
|
|
int base = players.at(selfID).units.at(index).miningBase;
|
|
if (base > -1)
|
|
players.at(selfID).bases.at(base).subtractWorker(players.at(selfID).units.at(index).action);
|
|
}
|
|
players.at(selfID).units.at(index).action = "";
|
|
players.at(selfID).units.at(index).miningBase = -1;
|
|
players.at(selfID).units.at(index).target = TilePosition(-1, -1);
|
|
players.at(selfID).units.at(index).unit->move(players.at(selfID).units.at(index).unit->getPosition());
|
|
//players.at(selfID).units.at(index).unit->stop();
|
|
}
|
|
|
|
// find a base with larva to morph a unit at
|
|
CUnit Macro::getTrainingBase()
|
|
{
|
|
int id = 0;
|
|
int count = 0;
|
|
for (auto u : players.at(selfID).units)
|
|
{
|
|
if (u.unit->getType() == UnitTypes::Zerg_Hatchery) id = count;
|
|
if (u.unit->getLarva().size() > 0)
|
|
{
|
|
return u;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
return players.at(selfID).units.at(id);
|
|
}
|
|
|
|
void Macro::morphCreepColonies()
|
|
{
|
|
for (size_t i = 0; i < players.at(selfID).units.size(); i++)
|
|
{
|
|
if (players.at(selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Creep_Colony)
|
|
{
|
|
int base = Util::getClosestOwnedBase(players.at(selfID).units.at(i).unit);
|
|
if(players.at(selfID).bases.at(base).sunkenCount < players.at(selfID).bases.at(base).desiredSunkenCount())
|
|
{
|
|
players.at(selfID).units.at(i).unit->morph(UnitTypes::Zerg_Sunken_Colony);
|
|
}
|
|
else players.at(selfID).units.at(i).unit->morph(UnitTypes::Zerg_Spore_Colony);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Macro::onUnitComplete(Unit unit)
|
|
{
|
|
if (int id = unit->getPlayer()->getID() == selfID
|
|
&& unit->getType() != UnitTypes::Zerg_Sunken_Colony
|
|
&& unit->getType() != UnitTypes::Zerg_Spore_Colony)
|
|
{
|
|
int index = -1;
|
|
int count = 0;
|
|
for (auto e : queue)
|
|
{
|
|
if (e.unit == unit->getType() && e.inProgress)
|
|
{
|
|
index = count;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
if(index > -1) queue.erase(queue.begin()+index);
|
|
if (unit->getType() == UnitTypes::Zerg_Hatchery)
|
|
{
|
|
bool chk = false;
|
|
|
|
for (auto b : bases)
|
|
{
|
|
if (Util::isSameTilePosition(unit->getTilePosition(), b.tilePosition))
|
|
{
|
|
chk = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Util::isBase(unit) && players.at(selfID).bases.size() > 0) players.at(selfID).bases.back().complete = true;
|
|
}
|
|
int unitIndex = Util::getUnitIndex(unit);
|
|
if (unitIndex > -1)
|
|
{
|
|
players.at(selfID).units.at(unitIndex).unitType = unit->getType();
|
|
}
|
|
}
|
|
}
|
|
|
|
// assigns a unit or base to the respective player
|
|
void Macro::onUnitCreate(Unit unit)
|
|
{
|
|
if (unit->getPlayer() != Broodwar->neutral())
|
|
{
|
|
int id = unit->getPlayer()->getID();
|
|
if (unit->getType().isResourceDepot())
|
|
{
|
|
for (auto b : bases)
|
|
{
|
|
if (Util::isSameTilePosition(unit->getTilePosition(), b.tilePosition))
|
|
{
|
|
CBase base = CBase(unit);
|
|
base.complete = true;
|
|
assignBase(id, base);
|
|
}
|
|
}
|
|
}
|
|
players.at(id).units.push_back(CUnit(unit));
|
|
}
|
|
}
|
|
|
|
//@TODO clean this up
|
|
void Macro::onUnitDestroy(Unit unit)
|
|
{
|
|
if (unit->getPlayer() != Broodwar->neutral())
|
|
{
|
|
int id = unit->getPlayer()->getID();
|
|
int index = Util::getUnitIndex(unit);
|
|
players.at(id).gasLost += unit->getType().gasPrice();
|
|
players.at(id).mineralsLost += unit->getType().mineralPrice();
|
|
|
|
if (index > -1)
|
|
{
|
|
if (unit->getPlayer() == Broodwar->self())
|
|
{
|
|
if (players.at(id).units.at(index).action == "build"
|
|
|| players.at(id).units.at(index).action == "expand")
|
|
{
|
|
//TODO newbase isn't being cleared properly when a drone dies
|
|
// on the way to build a hatchery
|
|
if (players.at(id).units.at(index).action == "expand")
|
|
{
|
|
newBase = false;
|
|
stopProduction = false;
|
|
}
|
|
int queueIndex = -1;
|
|
for (size_t i = 0; i < queue.size(); i++)
|
|
{
|
|
if (queue.at(i).unit == players.at(id).units.at(index).targetUnit)
|
|
{
|
|
queueIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (queueIndex > -1) queue.erase(queue.begin() + queueIndex);
|
|
}
|
|
|
|
else if (players.at(id).units.at(index).action == "minerals")
|
|
{
|
|
int baseIndex = players.at(id).units.at(index).miningBase;
|
|
if (baseIndex > -1)
|
|
{
|
|
players.at(id).bases.at(baseIndex).mineralWorkers--;
|
|
}
|
|
}
|
|
|
|
else if (players.at(id).units.at(index).action == "gas")
|
|
{
|
|
int baseIndex = players.at(id).units.at(index).miningBase;
|
|
if (baseIndex > -1)
|
|
{
|
|
players.at(id).bases.at(baseIndex).gasWorkers--;
|
|
}
|
|
}
|
|
|
|
int squadindex = -1;
|
|
int unitindex = -1;
|
|
for (size_t j = 0; j < squads.size(); j++)
|
|
{
|
|
for (size_t k = 0; k < squads.at(j).units.size(); k++)
|
|
{
|
|
if (squads.at(j).units.at(k).id == unit->getID())
|
|
{
|
|
squadindex = j;
|
|
unitindex = k;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (unitindex > -1)
|
|
{
|
|
squads.at(squadindex).units.erase(squads.at(squadindex).units.begin() + unitindex);
|
|
}
|
|
int scoutindex = -1;
|
|
for (size_t j = 0; j < scouts.size(); j++)
|
|
{
|
|
if (scouts.at(j).unit->getID() == unit->getID())
|
|
{
|
|
scoutindex = j;
|
|
break;
|
|
}
|
|
}
|
|
if (scoutindex > -1) scouts.erase(scouts.begin() + scoutindex);
|
|
|
|
}
|
|
players.at(id).units.erase(players.at(id).units.begin() + index);
|
|
}
|
|
|
|
if (unit->getType().isResourceDepot())
|
|
{
|
|
index = Util::getBaseIndex(unit);
|
|
|
|
if (index > -1) players.at(id).bases.erase(players.at(id).bases.begin() + index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Macro::onUnitDiscover(Unit unit)
|
|
{
|
|
if (unit->getPlayer() != Broodwar->neutral() && unit->getPlayer() != Broodwar->self() && !Util::unitCounted(unit))
|
|
{
|
|
int id = unit->getPlayer()->getID();
|
|
players.at(id).gasSpent += unit->getType().gasPrice();
|
|
players.at(id).mineralsSpent += unit->getType().mineralPrice();
|
|
if (unit->getType().isResourceDepot())
|
|
{
|
|
for (auto b : bases)
|
|
{
|
|
if (unit->getTilePosition().x == b.tilePosition.x && unit->getTilePosition().y == b.tilePosition.y)
|
|
{
|
|
CBase base = CBase(unit);
|
|
base.complete = unit->isCompleted();
|
|
assignBase(id, base);
|
|
}
|
|
}
|
|
}
|
|
CUnit u = CUnit(unit);
|
|
u.lastSeen = u.unit->getTilePosition();
|
|
players.at(id).units.push_back(u);
|
|
}
|
|
}
|
|
|
|
void Macro::onUnitMorph(Unit unit)
|
|
{
|
|
if (unit->getPlayer()->getID() == selfID)
|
|
{
|
|
if (unit->getType().isRefinery())
|
|
{
|
|
players.at(selfID).units.push_back(CUnit(unit));
|
|
}
|
|
int index = Util::getQueueIndex(unit->getType());
|
|
|
|
if (index > -1)
|
|
{
|
|
queue.erase(queue.begin() + index);
|
|
}
|
|
|
|
if (unit->getType() == UnitTypes::Zerg_Hatchery && newBase
|
|
&& Util::isBasePosition(unit->getTilePosition()))
|
|
{
|
|
players.at(selfID).bases.push_back(CBase(unit));
|
|
newBase = false;
|
|
stopProduction = false;
|
|
}
|
|
else if (unit->getType() == UnitTypes::Zerg_Hatchery && macroHatch)
|
|
{
|
|
macroHatch = false;
|
|
}
|
|
|
|
int unitIndex = Util::getUnitIndex(unit);
|
|
if (unitIndex > -1)
|
|
{
|
|
players.at(selfID).units.at(unitIndex).unitType = unit->getType();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Macro::onUnitShow(Unit unit)
|
|
{
|
|
if (!unit->getPlayer()->isNeutral() && !unit->getPlayer()->getID() == Broodwar->self()->getID())
|
|
{
|
|
int id = unit->getPlayer()->getID();
|
|
for (size_t i = 0; i < players.at(id).units.size(); i++)
|
|
{
|
|
if (players.at(id).units.at(i).unit->getID() == unit->getID())
|
|
{
|
|
players.at(id).units.at(i).lastSeen = unit->getTilePosition();
|
|
players.at(id).units.at(i).unitType = unit->getType();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int Macro::queueCount(BWAPI::UnitType unit)
|
|
{
|
|
int count = 0;
|
|
for (auto e : queue)
|
|
{
|
|
if (e.unit == unit) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
bool Macro::queueHas(BWAPI::TechType tech)
|
|
{
|
|
for (auto e : queue)
|
|
{
|
|
if (e.tech == tech) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Macro::queueHas(BWAPI::UnitType unit)
|
|
{
|
|
for (auto e : queue)
|
|
{
|
|
if (e.unit == unit) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Macro::queueHas(BWAPI::UpgradeType upgrade)
|
|
{
|
|
for (auto e : queue)
|
|
{
|
|
if (e.upgrade == upgrade) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Macro::setEnemyID(int id)
|
|
{
|
|
enemyIDs.push_back(id);
|
|
}
|
|
|
|
void Macro::setLastAttack(int frame)
|
|
{
|
|
lastAttack = frame;
|
|
}
|
|
|
|
void Macro::setLastBuildLocation(TilePosition tp)
|
|
{
|
|
lastBuildLocation = tp;
|
|
}
|
|
|
|
void Macro::setMacroHatch(bool status)
|
|
{
|
|
macroHatch = status;
|
|
}
|
|
|
|
void Macro::setNewBase(bool status)
|
|
{
|
|
newBase = status;
|
|
}
|
|
|
|
void Macro::setSelfID(int id)
|
|
{
|
|
selfID = id;
|
|
}
|
|
|
|
void Macro::setStopProduction(bool status)
|
|
{
|
|
stopProduction = status;
|
|
} |