Skip to content
Snippets Groups Projects
Commit 8e7849e6 authored by David Bergström's avatar David Bergström
Browse files

Remove WorkerManager and everything that depends on it

parent f4b6ae7b
No related branches found
No related tags found
No related merge requests found
#include "Common.h"
#include "BuildingManager.h"
#include "IDABot.h"
#include "Util.h"
BuildingManager::BuildingManager(IDABot & bot)
: m_bot(bot)
, m_buildingPlacer(bot)
, m_debugMode(false)
, m_reservedMinerals(0)
, m_reservedGas(0)
{
}
void BuildingManager::onStart()
{
m_buildingPlacer.onStart();
}
// gets called every frame from GameCommander
void BuildingManager::onFrame()
{
for (auto unit : m_bot.UnitInfo().getUnits(Players::Self))
{
// filter out units which aren't buildings under construction
if (m_bot.Data(unit).isBuilding)
{
std::stringstream ss;
ss << unit.getID();
m_bot.Map().drawText(unit.getPosition(), ss.str());
}
}
validateWorkersAndBuildings(); // check to see if assigned workers have died en route or while constructing
assignWorkersToUnassignedBuildings(); // assign workers to the unassigned buildings and label them 'planned'
constructAssignedBuildings(); // for each planned building, if the worker isn't constructing, send the command
checkForStartedConstruction(); // check to see if any buildings have started construction and update data structures
checkForDeadTerranBuilders(); // if we are terran and a building is under construction without a worker, assign a new one
checkForCompletedBuildings(); // check to see if any buildings have completed and update data structures
drawBuildingInformation();
}
bool BuildingManager::isBeingBuilt(UnitType type)
{
for (auto & b : m_buildings)
{
if (b.type == type)
{
return true;
}
}
return false;
}
// STEP 1: DO BOOK KEEPING ON WORKERS WHICH MAY HAVE DIED
void BuildingManager::validateWorkersAndBuildings()
{
// TODO: if a terran worker dies while constructing and its building
// is under construction, place unit back into buildingsNeedingBuilders
std::vector<Building> toRemove;
// find any buildings which have become obsolete
for (auto & b : m_buildings)
{
if (b.status != BuildingStatus::UnderConstruction)
{
continue;
}
auto buildingUnit = b.buildingUnit;
// TODO: || !b.buildingUnit->getType().isBuilding()
if (!buildingUnit.isValid())
{
toRemove.push_back(b);
}
}
removeBuildings(toRemove);
}
// STEP 2: ASSIGN WORKERS TO BUILDINGS WITHOUT THEM
void BuildingManager::assignWorkersToUnassignedBuildings()
{
// for each building that doesn't have a builder, assign one
for (Building & b : m_buildings)
{
if (b.status != BuildingStatus::Unassigned)
{
continue;
}
BOT_ASSERT(!b.builderUnit.isValid(), "Error: Tried to assign a builder to a building that already had one ");
if (m_debugMode) { printf("Assigning Worker To: %s", b.type.getName().c_str()); }
// grab a worker unit from WorkerManager which is closest to this final position
CCTilePosition testLocation = getBuildingLocation(b);
if (!m_bot.Map().isValidTile(testLocation) || (testLocation.x == 0 && testLocation.y == 0))
{
continue;
}
b.finalPosition = testLocation;
// grab the worker unit from WorkerManager which is closest to this final position
Unit builderUnit = m_bot.Workers().getBuilder(b);
b.builderUnit = builderUnit;
if (!b.builderUnit.isValid())
{
continue;
}
// reserve this building's space
m_buildingPlacer.reserveTiles((int)b.finalPosition.x, (int)b.finalPosition.y, b.type.tileWidth(), b.type.tileHeight());
b.status = BuildingStatus::Assigned;
}
}
// STEP 3: ISSUE CONSTRUCTION ORDERS TO ASSIGN BUILDINGS AS NEEDED
void BuildingManager::constructAssignedBuildings()
{
for (auto & b : m_buildings)
{
if (b.status != BuildingStatus::Assigned)
{
continue;
}
// TODO: not sure if this is the correct way to tell if the building is constructing
//sc2::AbilityID buildAbility = m_bot.Data(b.type).buildAbility;
Unit builderUnit = b.builderUnit;
bool isConstructing = false;
// if we're zerg and the builder unit is null, we assume it morphed into the building
if (Util::IsZerg(m_bot.GetPlayerRace(Players::Self)))
{
if (!builderUnit.isValid())
{
isConstructing = true;
}
}
else
{
BOT_ASSERT(builderUnit.isValid(), "null builder unit");
isConstructing = builderUnit.isConstructing(b.type);
}
// if that worker is not currently constructing
if (!isConstructing)
{
// if we haven't explored the build position, go there
if (!isBuildingPositionExplored(b))
{
builderUnit.move(b.finalPosition);
}
// if this is not the first time we've sent this guy to build this
// it must be the case that something was in the way of building
else if (b.buildCommandGiven)
{
// TODO: in here is where we would check to see if the builder died on the way
// or if things are taking too long, or the build location is no longer valid
}
else
{
// if it's a refinery, the build command has to be on the geyser unit tag
if (b.type.isRefinery())
{
// first we find the geyser at the desired location
Unit geyser;
for (auto unit : m_bot.GetAllUnits())
{
if (unit.getType().isGeyser() && Util::Dist(Util::GetPosition(b.finalPosition), unit.getPosition()) < 3)
{
geyser = unit;
break;
}
}
if (geyser.isValid())
{
b.builderUnit.buildTarget(b.type, geyser);
}
else
{
std::cout << "WARNING: NO VALID GEYSER UNIT FOUND TO BUILD ON, SKIPPING REFINERY\n";
}
}
// if it's not a refinery, we build right on the position
else
{
b.builderUnit.build(b.type, b.finalPosition);
}
// set the flag to true
b.buildCommandGiven = true;
}
}
}
}
// STEP 4: UPDATE DATA STRUCTURES FOR BUILDINGS STARTING CONSTRUCTION
void BuildingManager::checkForStartedConstruction()
{
// for each building unit which is being constructed
for (auto buildingStarted : m_bot.UnitInfo().getUnits(Players::Self))
{
// filter out units which aren't buildings under construction
if (!buildingStarted.getType().isBuilding() || !buildingStarted.isBeingConstructed())
{
continue;
}
// check all our building status objects to see if we have a match and if we do, update it
for (auto & b : m_buildings)
{
if (b.status != BuildingStatus::Assigned)
{
continue;
}
// check if the positions match
int dx = b.finalPosition.x - buildingStarted.getTilePosition().x;
int dy = b.finalPosition.y - buildingStarted.getTilePosition().y;
if (dx*dx + dy*dy < Util::TileToPosition(1.0f))
{
if (b.buildingUnit.isValid())
{
std::cout << "Building mis-match somehow\n";
}
// the resources should now be spent, so unreserve them
m_reservedMinerals -= buildingStarted.getType().mineralPrice();
m_reservedGas -= buildingStarted.getType().gasPrice();
// flag it as started and set the buildingUnit
b.underConstruction = true;
b.buildingUnit = buildingStarted;
// if we are zerg, the buildingUnit now becomes nullptr since it's destroyed
if (Util::IsZerg(m_bot.GetPlayerRace(Players::Self)))
{
b.builderUnit = Unit();
}
else if (Util::IsProtoss(m_bot.GetPlayerRace(Players::Self)))
{
m_bot.Workers().finishedWithWorker(b.builderUnit);
b.builderUnit = Unit();
}
// put it in the under construction vector
b.status = BuildingStatus::UnderConstruction;
// free this space
m_buildingPlacer.freeTiles((int)b.finalPosition.x, (int)b.finalPosition.y, b.type.tileWidth(), b.type.tileHeight());
// only one building will match
break;
}
}
}
}
// STEP 5: IF WE ARE TERRAN, THIS MATTERS, SO: LOL
void BuildingManager::checkForDeadTerranBuilders() {}
// STEP 6: CHECK FOR COMPLETED BUILDINGS
void BuildingManager::checkForCompletedBuildings()
{
std::vector<Building> toRemove;
// for each of our buildings under construction
for (auto & b : m_buildings)
{
if (b.status != BuildingStatus::UnderConstruction)
{
continue;
}
// if the unit has completed
if (b.buildingUnit.isCompleted())
{
// if we are terran, give the worker back to worker manager
if (Util::IsTerran(m_bot.GetPlayerRace(Players::Self)))
{
m_bot.Workers().finishedWithWorker(b.builderUnit);
}
// remove this unit from the under construction vector
toRemove.push_back(b);
}
}
removeBuildings(toRemove);
}
// add a new building to be constructed
void BuildingManager::addBuildingTask(const UnitType & type, const CCTilePosition & desiredPosition)
{
m_reservedMinerals += m_bot.Data(type).mineralCost;
m_reservedGas += m_bot.Data(type).gasCost;
Building b(type, desiredPosition);
b.status = BuildingStatus::Unassigned;
m_buildings.push_back(b);
}
// TODO: may need to iterate over all tiles of the building footprint
bool BuildingManager::isBuildingPositionExplored(const Building & b) const
{
return m_bot.Map().isExplored(b.finalPosition);
}
char BuildingManager::getBuildingWorkerCode(const Building & b) const
{
return b.builderUnit.isValid() ? 'W' : 'X';
}
int BuildingManager::getReservedMinerals()
{
return m_reservedMinerals;
}
int BuildingManager::getReservedGas()
{
return m_reservedGas;
}
void BuildingManager::drawBuildingInformation()
{
m_buildingPlacer.drawReservedTiles();
std::stringstream ss;
ss << "Building Information " << m_buildings.size() << "\n\n\n";
int yspace = 0;
for (const auto & b : m_buildings)
{
std::stringstream dss;
if (b.builderUnit.isValid())
{
dss << "\n\nBuilder: " << b.builderUnit.getID() << "\n";
}
if (b.buildingUnit.isValid())
{
dss << "Building: " << b.buildingUnit.getID() << "\n" << b.buildingUnit.getBuildPercentage();
m_bot.Map().drawText(b.buildingUnit.getPosition(), dss.str());
}
if (b.status == BuildingStatus::Unassigned)
{
ss << "Unassigned " << b.type.getName() << " " << getBuildingWorkerCode(b) << "\n";
}
else if (b.status == BuildingStatus::Assigned)
{
ss << "Assigned " << b.type.getName() << " " << b.builderUnit.getID() << " " << getBuildingWorkerCode(b) << " (" << b.finalPosition.x << "," << b.finalPosition.y << ")\n";
int x1 = b.finalPosition.x;
int y1 = b.finalPosition.y;
int x2 = b.finalPosition.x + b.type.tileWidth();
int y2 = b.finalPosition.y + b.type.tileHeight();
m_bot.Map().drawBox((CCPositionType)x1, (CCPositionType)y1, (CCPositionType)x2, (CCPositionType)y2, CCColor(255, 0, 0));
//m_bot.Map().drawLine(b.finalPosition, m_bot.GetUnit(b.builderUnitTag)->pos, CCColors::Yellow);
}
else if (b.status == BuildingStatus::UnderConstruction)
{
ss << "Constructing " << b.type.getName() << " " << getBuildingWorkerCode(b) << "\n";
}
}
m_bot.Map().drawTextScreen(0.3f, 0.05f, ss.str());
}
std::vector<UnitType> BuildingManager::buildingsQueued() const
{
std::vector<UnitType> buildingsQueued;
for (const auto & b : m_buildings)
{
if (b.status == BuildingStatus::Unassigned || b.status == BuildingStatus::Assigned)
{
buildingsQueued.push_back(b.type);
}
}
return buildingsQueued;
}
CCTilePosition BuildingManager::getBuildingLocation(const Building & b)
{
size_t numPylons = m_bot.UnitInfo().getUnitTypeCount(Players::Self, Util::GetSupplyProvider(m_bot.GetPlayerRace(Players::Self), m_bot), true);
// TODO: if requires psi and we have no pylons return 0
if (b.type.isRefinery())
{
return m_buildingPlacer.getRefineryPosition();
}
if (b.type.isResourceDepot())
{
return m_bot.Bases().getNextExpansion(Players::Self)->getDepotPosition();
}
// get a position within our region
// TODO: put back in special pylon / cannon spacing
return m_buildingPlacer.getBuildLocationNear(b, 1);
}
void BuildingManager::removeBuildings(const std::vector<Building> & toRemove)
{
for (auto & b : toRemove)
{
const auto & it = std::find(m_buildings.begin(), m_buildings.end(), b);
if (it != m_buildings.end())
{
m_buildings.erase(it);
}
}
}
\ No newline at end of file
#pragma once
#include "Common.h"
#include "BuildingPlacer.h"
class IDABot;
class BuildingManager
{
IDABot & m_bot;
BuildingPlacer m_buildingPlacer;
std::vector<Building> m_buildings;
bool m_debugMode;
int m_reservedMinerals; // minerals reserved for planned buildings
int m_reservedGas; // gas reserved for planned buildings
bool isBuildingPositionExplored(const Building & b) const;
void removeBuildings(const std::vector<Building> & toRemove);
void validateWorkersAndBuildings(); // STEP 1
void assignWorkersToUnassignedBuildings(); // STEP 2
void constructAssignedBuildings(); // STEP 3
void checkForStartedConstruction(); // STEP 4
void checkForDeadTerranBuilders(); // STEP 5
void checkForCompletedBuildings(); // STEP 6
char getBuildingWorkerCode(const Building & b) const;
public:
BuildingManager(IDABot & bot);
void onStart();
void onFrame();
void addBuildingTask(const UnitType & type, const CCTilePosition & desiredPosition);
void drawBuildingInformation();
CCTilePosition getBuildingLocation(const Building & b);
int getReservedMinerals();
int getReservedGas();
bool isBeingBuilt(UnitType type);
std::vector<UnitType> buildingsQueued() const;
};
......@@ -462,7 +462,6 @@ IDABot::IDABot()
: m_map(*this)
, m_bases(*this)
, m_unitInfo(*this)
, m_workers(*this)
, m_techTree(*this)
, m_buildingPlacer(*this)
{
......@@ -592,11 +591,6 @@ const TechTree & IDABot::TechTree() const
return m_techTree;
}
WorkerManager & IDABot::Workers()
{
return m_workers;
}
int IDABot::GetCurrentSupply() const
{
return Observation()->GetFoodUsed();
......
......@@ -8,7 +8,6 @@
#include "MapTools.h"
#include "BaseLocationManager.h"
#include "UnitInfoManager.h"
#include "WorkerManager.h"
#include "BuildingPlacer.h"
#include "TechTree.h"
#include "MetaType.h"
......@@ -19,7 +18,6 @@ class IDABot : public sc2::Agent
MapTools m_map;
BaseLocationManager m_bases;
UnitInfoManager m_unitInfo;
WorkerManager m_workers;
TechTree m_techTree;
// TODO: This should not be exported for student use
BuildingPlacer m_buildingPlacer;
......@@ -110,7 +108,6 @@ public:
API for students
*/
const TechTree & TechTree() const;
WorkerManager & Workers();
const BaseLocationManager & Bases() const;
const MapTools & Map() const;
const UnitInfoManager & UnitInfo() const;
......
#include "ProductionManager.h"
#include "Util.h"
#include "IDABot.h"
ProductionManager::ProductionManager(IDABot & bot)
: m_bot (bot)
, m_buildingManager (bot)
, m_queue (bot)
{
}
void ProductionManager::setBuildOrder(const BuildOrder & buildOrder)
{
m_queue.clearAll();
for (size_t i(0); i<buildOrder.size(); ++i)
{
m_queue.queueAsLowestPriority(buildOrder[i], true);
}
}
void ProductionManager::onStart()
{
m_buildingManager.onStart();
//setBuildOrder(m_bot.Strategy().getOpeningBookBuildOrder());
}
void ProductionManager::onFrame()
{
fixBuildOrderDeadlock();
manageBuildOrderQueue();
// TODO: if nothing is currently building, get a new goal from the strategy manager
// TODO: detect if there's a build order deadlock once per second
// TODO: triggers for game things like cloaked units etc
m_buildingManager.onFrame();
drawProductionInformation();
}
// on unit destroy
void ProductionManager::onUnitDestroy(const Unit & unit)
{
// TODO: might have to re-do build order if a vital unit died
}
void ProductionManager::manageBuildOrderQueue()
{
// if there is nothing in the queue, oh well
if (m_queue.isEmpty())
{
return;
}
// the current item to be used
BuildOrderItem & currentItem = m_queue.getHighestPriorityItem();
// while there is still something left in the queue
while (!m_queue.isEmpty())
{
// this is the unit which can produce the currentItem
Unit producer = getProducer(currentItem.type);
// check to see if we can make it right now
bool canMake = canMakeNow(producer, currentItem.type);
// TODO: if it's a building and we can't make it yet, predict the worker movement to the location
// if we can make the current item
if (producer.isValid() && canMake)
{
// create it and remove it from the _queue
create(producer, currentItem);
m_queue.removeCurrentHighestPriorityItem();
// don't actually loop around in here
break;
}
// otherwise, if we can skip the current item
else if (m_queue.canSkipItem())
{
// skip it
m_queue.skipItem();
// and get the next one
currentItem = m_queue.getNextHighestPriorityItem();
}
else
{
// so break out
break;
}
}
}
void ProductionManager::fixBuildOrderDeadlock()
{
if (m_queue.isEmpty()) { return; }
BuildOrderItem & currentItem = m_queue.getHighestPriorityItem();
// check to see if we have the prerequisites for the topmost item
bool hasRequired = m_bot.Data(currentItem.type).requiredUnits.empty();
for (auto & required : m_bot.Data(currentItem.type).requiredUnits)
{
if (m_bot.UnitInfo().getUnitTypeCount(Players::Self, required, false) > 0 || m_buildingManager.isBeingBuilt(required))
{
hasRequired = true;
break;
}
}
if (!hasRequired)
{
std::cout << currentItem.type.getName() << " needs " << m_bot.Data(currentItem.type).requiredUnits[0].getName() << "\n";
m_queue.queueAsHighestPriority(MetaType(m_bot.Data(currentItem.type).requiredUnits[0], m_bot), true);
fixBuildOrderDeadlock();
return;
}
// build the producer of the unit if we don't have one
bool hasProducer = m_bot.Data(currentItem.type).whatBuilds.empty();
for (auto & producer : m_bot.Data(currentItem.type).whatBuilds)
{
if (m_bot.UnitInfo().getUnitTypeCount(Players::Self, producer, false) > 0 || m_buildingManager.isBeingBuilt(producer))
{
hasProducer = true;
break;
}
}
if (!hasProducer)
{
m_queue.queueAsHighestPriority(MetaType(m_bot.Data(currentItem.type).whatBuilds[0], m_bot), true);
fixBuildOrderDeadlock();
}
// build a refinery if we don't have one and the thing costs gas
auto refinery = Util::GetRefinery(m_bot.GetPlayerRace(Players::Self), m_bot);
if (m_bot.Data(currentItem.type).gasCost > 0 && m_bot.UnitInfo().getUnitTypeCount(Players::Self, refinery, false) == 0)
{
m_queue.queueAsHighestPriority(MetaType(refinery, m_bot), true);
}
// build supply if we need some
auto supplyProvider = Util::GetSupplyProvider(m_bot.GetPlayerRace(Players::Self), m_bot);
if (m_bot.Data(currentItem.type).supplyCost > (m_bot.GetMaxSupply() - m_bot.GetCurrentSupply()) && !m_buildingManager.isBeingBuilt(supplyProvider))
{
m_queue.queueAsHighestPriority(MetaType(supplyProvider, m_bot), true);
}
}
Unit ProductionManager::getProducer(const MetaType & type, CCPosition closestTo)
{
// get all the types of units that cna build this type
auto & producerTypes = m_bot.Data(type).whatBuilds;
// make a set of all candidate producers
std::vector<Unit> candidateProducers;
for (auto unit : m_bot.UnitInfo().getUnits(Players::Self))
{
// reasons a unit can not train the desired type
if (std::find(producerTypes.begin(), producerTypes.end(), unit.getType()) == producerTypes.end()) { continue; }
if (!unit.isCompleted()) { continue; }
if (m_bot.Data(unit).isBuilding && unit.isTraining()) { continue; }
if (unit.isFlying()) { continue; }
// TODO: if unit is not powered continue
// TODO: if the type is an addon, some special cases
// TODO: if the type requires an addon and the producer doesn't have one
// if we haven't cut it, add it to the set of candidates
candidateProducers.push_back(unit);
}
return getClosestUnitToPosition(candidateProducers, closestTo);
}
Unit ProductionManager::getClosestUnitToPosition(const std::vector<Unit> & units, CCPosition closestTo)
{
if (units.size() == 0)
{
return Unit();
}
// if we don't care where the unit is return the first one we have
if (closestTo.x == 0 && closestTo.y == 0)
{
return units[0];
}
Unit closestUnit;
double minDist = std::numeric_limits<double>::max();
for (auto & unit : units)
{
double distance = Util::Dist(unit, closestTo);
if (!closestUnit.isValid() || distance < minDist)
{
closestUnit = unit;
minDist = distance;
}
}
return closestUnit;
}
// this function will check to see if all preconditions are met and then create a unit
void ProductionManager::create(const Unit & producer, BuildOrderItem & item)
{
if (!producer.isValid())
{
return;
}
// if we're dealing with a building
if (item.type.isBuilding())
{
if (item.type.getUnitType().isMorphedBuilding())
{
producer.morph(item.type.getUnitType());
}
else
{
m_buildingManager.addBuildingTask(item.type.getUnitType(), Util::GetTilePosition(m_bot.GetStartLocation()));
}
}
// if we're dealing with a non-building unit
else if (item.type.isUnit())
{
producer.train(item.type.getUnitType());
}
else if (item.type.isUpgrade())
{
// TODO: UPGRADES
//Micro::SmartAbility(producer, m_bot.Data(item.type.getUpgradeID()).buildAbility, m_bot);
}
}
bool ProductionManager::canMakeNow(const Unit & producer, const MetaType & type)
{
if (!producer.isValid() || !meetsReservedResources(type))
{
return false;
}
#ifdef SC2API
sc2::AvailableAbilities available_abilities = m_bot.Query()->GetAbilitiesForUnit(producer.getUnitPtr());
// quick check if the unit can't do anything it certainly can't build the thing we want
if (available_abilities.abilities.empty())
{
return false;
}
else
{
// check to see if one of the unit's available abilities matches the build ability type
sc2::AbilityID MetaTypeAbility = m_bot.Data(type).buildAbility;
for (const sc2::AvailableAbility & available_ability : available_abilities.abilities)
{
if (available_ability.ability_id == MetaTypeAbility)
{
return true;
}
}
}
return false;
#else
bool canMake = meetsReservedResources(type);
if (canMake)
{
if (type.isUnit())
{
canMake = BWAPI::Broodwar->canMake(type.getUnitType().getAPIUnitType(), producer.getUnitPtr());
}
else if (type.isTech())
{
canMake = BWAPI::Broodwar->canResearch(type.getTechType(), producer.getUnitPtr());
}
else if (type.isUpgrade())
{
canMake = BWAPI::Broodwar->canUpgrade(type.getUpgrade(), producer.getUnitPtr());
}
else
{
BOT_ASSERT(false, "Unknown type");
}
}
return canMake;
#endif
}
bool ProductionManager::detectBuildOrderDeadlock()
{
// TODO: detect build order deadlocks here
return false;
}
int ProductionManager::getFreeMinerals()
{
return m_bot.GetMinerals() - m_buildingManager.getReservedMinerals();
}
int ProductionManager::getFreeGas()
{
return m_bot.GetGas() - m_buildingManager.getReservedGas();
}
// return whether or not we meet resources, including building reserves
bool ProductionManager::meetsReservedResources(const MetaType & type)
{
// return whether or not we meet the resources
int minerals = m_bot.Data(type).mineralCost;
int gas = m_bot.Data(type).gasCost;
return (m_bot.Data(type).mineralCost <= getFreeMinerals()) && (m_bot.Data(type).gasCost <= getFreeGas());
}
void ProductionManager::drawProductionInformation()
{
std::stringstream ss;
ss << "Production Information\n\n";
for (auto & unit : m_bot.UnitInfo().getUnits(Players::Self))
{
if (unit.isBeingConstructed())
{
//ss << sc2::UnitTypeToName(unit.unit_type) << " " << unit.build_progress << "\n";
}
}
ss << m_queue.getQueueInformation();
m_bot.Map().drawTextScreen(0.01f, 0.01f, ss.str(), CCColor(255, 255, 0));
}
#pragma once
#include "Common.h"
#include "BuildOrder.h"
#include "BuildingManager.h"
#include "BuildOrderQueue.h"
class IDABot;
class ProductionManager
{
IDABot & m_bot;
BuildingManager m_buildingManager;
BuildOrderQueue m_queue;
Unit getClosestUnitToPosition(const std::vector<Unit> & units, CCPosition closestTo);
bool meetsReservedResources(const MetaType & type);
bool canMakeNow(const Unit & producer, const MetaType & type);
bool detectBuildOrderDeadlock();
void setBuildOrder(const BuildOrder & buildOrder);
void create(const Unit & producer, BuildOrderItem & item);
void manageBuildOrderQueue();
int getFreeMinerals();
int getFreeGas();
void fixBuildOrderDeadlock();
public:
ProductionManager(IDABot & bot);
void onStart();
void onFrame();
void onUnitDestroy(const Unit & unit);
void drawProductionInformation();
Unit getProducer(const MetaType & type, CCPosition closestTo = CCPosition(0, 0));
};
#include "WorkerManager.h"
#include "IDABot.h"
#include "Util.h"
#include "Building.h"
WorkerManager::WorkerManager(IDABot & bot)
: m_bot (bot)
, m_workerData (bot)
{
}
void WorkerManager::onStart()
{
}
void WorkerManager::onFrame()
{
m_workerData.updateAllWorkerData();
handleGasWorkers();
handleIdleWorkers();
drawResourceDebugInfo();
drawWorkerInformation();
m_workerData.drawDepotDebugInfo();
handleRepairWorkers();
}
void WorkerManager::setRepairWorker(Unit worker, const Unit & unitToRepair)
{
m_workerData.setWorkerJob(worker, WorkerJobs::Repair, unitToRepair);
}
void WorkerManager::stopRepairing(Unit worker)
{
m_workerData.setWorkerJob(worker, WorkerJobs::Idle);
}
void WorkerManager::handleGasWorkers()
{
// for each unit we have
for (auto & unit : m_bot.UnitInfo().getUnits(Players::Self))
{
// if that unit is a refinery
if (unit.getType().isRefinery() && unit.isCompleted())
{
// get the number of workers currently assigned to it
int numAssigned = m_workerData.getNumAssignedWorkers(unit);
// if it's less than we want it to be, fill 'er up
for (int i=0; i<(3-numAssigned); ++i)
{
auto gasWorker = getGasWorker(unit);
if (gasWorker.isValid())
{
m_workerData.setWorkerJob(gasWorker, WorkerJobs::Gas, unit);
}
}
}
}
}
void WorkerManager::handleIdleWorkers()
{
// for each of our workers
for (auto & worker : m_workerData.getWorkers())
{
if (!worker.isValid()) { continue; }
bool isIdle = worker.isIdle();
if (worker.isIdle() &&
(m_workerData.getWorkerJob(worker) != WorkerJobs::Build) &&
(m_workerData.getWorkerJob(worker) != WorkerJobs::Move) &&
(m_workerData.getWorkerJob(worker) != WorkerJobs::Scout))
{
m_workerData.setWorkerJob(worker, WorkerJobs::Idle);
}
// if it is idle
if (m_workerData.getWorkerJob(worker) == WorkerJobs::Idle)
{
setMineralWorker(worker);
}
}
}
void WorkerManager::handleRepairWorkers()
{
// TODO
}
Unit WorkerManager::getClosestMineralWorkerTo(const CCPosition & pos) const
{
Unit closestMineralWorker;
double closestDist = std::numeric_limits<double>::max();
// for each of our workers
for (auto & worker : m_workerData.getWorkers())
{
if (!worker.isValid()) { continue; }
// if it is a mineral worker
if (m_workerData.getWorkerJob(worker) == WorkerJobs::Minerals)
{
double dist = Util::DistSq(worker.getPosition(), pos);
if (!closestMineralWorker.isValid() || dist < closestDist)
{
closestMineralWorker = worker;
dist = closestDist;
}
}
}
return closestMineralWorker;
}
// set a worker to mine minerals
void WorkerManager::setMineralWorker(const Unit & unit)
{
// check if there is a mineral available to send the worker to
auto depot = getClosestDepot(unit);
// if there is a valid mineral
if (depot.isValid())
{
// update m_workerData with the new job
m_workerData.setWorkerJob(unit, WorkerJobs::Minerals, depot);
}
}
Unit WorkerManager::getClosestDepot(Unit worker) const
{
Unit closestDepot;
double closestDistance = std::numeric_limits<double>::max();
for (auto & unit : m_bot.UnitInfo().getUnits(Players::Self))
{
if (!unit.isValid()) { continue; }
if (unit.getType().isResourceDepot() && unit.isCompleted())
{
double distance = Util::Dist(unit, worker);
if (!closestDepot.isValid() || distance < closestDistance)
{
closestDepot = unit;
closestDistance = distance;
}
}
}
return closestDepot;
}
// other managers that need workers call this when they're done with a unit
void WorkerManager::finishedWithWorker(const Unit & unit)
{
if (m_workerData.getWorkerJob(unit) != WorkerJobs::Scout)
{
m_workerData.setWorkerJob(unit, WorkerJobs::Idle);
}
}
Unit WorkerManager::getGasWorker(Unit refinery) const
{
return getClosestMineralWorkerTo(refinery.getPosition());
}
void WorkerManager::setBuildingWorker(Unit worker, Building & b)
{
m_workerData.setWorkerJob(worker, WorkerJobs::Build, b.buildingUnit);
}
// gets a builder for BuildingManager to use
// if setJobAsBuilder is true (default), it will be flagged as a builder unit
// set 'setJobAsBuilder' to false if we just want to see which worker will build a building
Unit WorkerManager::getBuilder(Building & b, bool setJobAsBuilder) const
{
Unit builderWorker = getClosestMineralWorkerTo(Util::GetPosition(b.finalPosition));
// if the worker exists (one may not have been found in rare cases)
if (builderWorker.isValid() && setJobAsBuilder)
{
m_workerData.setWorkerJob(builderWorker, WorkerJobs::Build, b.builderUnit);
}
return builderWorker;
}
// sets a worker as a scout
void WorkerManager::setScoutWorker(Unit workerTag)
{
m_workerData.setWorkerJob(workerTag, WorkerJobs::Scout);
}
void WorkerManager::setCombatWorker(Unit workerTag)
{
m_workerData.setWorkerJob(workerTag, WorkerJobs::Combat);
}
void WorkerManager::drawResourceDebugInfo()
{
for (auto & worker : m_workerData.getWorkers())
{
if (!worker.isValid()) { continue; }
if (worker.isIdle())
{
m_bot.Map().drawText(worker.getPosition(), m_workerData.getJobCode(worker));
}
auto depot = m_workerData.getWorkerDepot(worker);
if (depot.isValid())
{
m_bot.Map().drawLine(worker.getPosition(), depot.getPosition());
}
}
}
void WorkerManager::drawWorkerInformation()
{
std::stringstream ss;
ss << "Workers: " << m_workerData.getWorkers().size() << "\n";
int yspace = 0;
for (auto & worker : m_workerData.getWorkers())
{
ss << m_workerData.getJobCode(worker) << " " << worker.getID() << "\n";
m_bot.Map().drawText(worker.getPosition(), m_workerData.getJobCode(worker));
}
m_bot.Map().drawTextScreen(0.75f, 0.2f, ss.str());
}
bool WorkerManager::isFree(Unit worker) const
{
return m_workerData.getWorkerJob(worker) == WorkerJobs::Minerals || m_workerData.getWorkerJob(worker) == WorkerJobs::Idle;
}
bool WorkerManager::isWorkerScout(Unit worker) const
{
return (m_workerData.getWorkerJob(worker) == WorkerJobs::Scout);
}
bool WorkerManager::isBuilder(Unit worker) const
{
return (m_workerData.getWorkerJob(worker) == WorkerJobs::Build);
}
int WorkerManager::getNumMineralWorkers()
{
return m_workerData.getWorkerJobCount(WorkerJobs::Minerals);
}
int WorkerManager::getNumGasWorkers()
{
return m_workerData.getWorkerJobCount(WorkerJobs::Gas);
}
#pragma once
#include "WorkerData.h"
class Building;
class IDABot;
class WorkerManager
{
IDABot & m_bot;
mutable WorkerData m_workerData;
Unit m_previousClosestWorker;
void setMineralWorker(const Unit & unit);
void handleIdleWorkers();
void handleGasWorkers();
void handleRepairWorkers();
public:
WorkerManager(IDABot & bot);
void onStart();
void onFrame();
void finishedWithWorker(const Unit & unit);
void drawResourceDebugInfo();
void drawWorkerInformation();
void setScoutWorker(Unit worker);
void setCombatWorker(Unit worker);
void setBuildingWorker(Unit worker, Building & b);
void setRepairWorker(Unit worker,const Unit & unitToRepair);
void stopRepairing(Unit worker);
int getNumMineralWorkers();
int getNumGasWorkers();
bool isWorkerScout(Unit worker) const;
bool isFree(Unit worker) const;
bool isBuilder(Unit worker) const;
Unit getBuilder(Building & b,bool setJobAsBuilder = true) const;
Unit getClosestDepot(Unit worker) const;
Unit getGasWorker(Unit refinery) const;
Unit getClosestMineralWorkerTo(const CCPosition & pos) const;
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment