Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • stebr364/pycommandcenter
  • starcraft-ai-course/pycommandcenter
  • eriah592/pycommandcenter
  • edvbe696/pycommandcenter
  • dawab699/pycommandcenter
  • hanja189/pycommandcenter
  • teoga849/pycommandcenter
  • musab250/pycommandcenter
  • emibr898/pycommandcenter
  • chrgu102/pycommandcenter
  • axega544/pycommandcenter
  • edvth289/pycommandcenter
  • jonbo278/py-command-center-v-2
13 results
Show changes
Showing with 1130 additions and 1542 deletions
#pragma once
#include "Common.h"
#include "MetaType.h"
class BuildOrder
{
std::vector<MetaType> m_buildOrder;
public:
BuildOrder();
BuildOrder(const std::vector<MetaType> & buildVector);
void add(const MetaType & type);
size_t size() const;
const MetaType & operator [] (const size_t & index) const;
MetaType & operator [] (const size_t & index);
};
#include "BuildOrderQueue.h"
#include "IDABot.h"
BuildOrderQueue::BuildOrderQueue(IDABot & bot)
: m_bot(bot)
, m_highestPriority(0)
, m_lowestPriority(0)
, m_defaultPrioritySpacing(10)
, m_numSkippedItems(0)
{
}
void BuildOrderQueue::clearAll()
{
// clear the queue
m_queue.clear();
// reset the priorities
m_highestPriority = 0;
m_lowestPriority = 0;
}
BuildOrderItem & BuildOrderQueue::getHighestPriorityItem()
{
// reset the number of skipped items to zero
m_numSkippedItems = 0;
// the queue will be sorted with the highest priority at the back
return m_queue.back();
}
BuildOrderItem & BuildOrderQueue::getNextHighestPriorityItem()
{
assert(m_queue.size() - 1 - m_numSkippedItems >= 0);
// the queue will be sorted with the highest priority at the back
return m_queue[m_queue.size() - 1 - m_numSkippedItems];
}
void BuildOrderQueue::skipItem()
{
// make sure we can skip
assert(canSkipItem());
// skip it
m_numSkippedItems++;
}
bool BuildOrderQueue::canSkipItem()
{
// does the queue have more elements
bool bigEnough = m_queue.size() > (size_t)(1 + m_numSkippedItems);
if (!bigEnough)
{
return false;
}
// is the current highest priority item not blocking a skip
bool highestNotBlocking = !m_queue[m_queue.size() - 1 - m_numSkippedItems].blocking;
// this tells us if we can skip
return highestNotBlocking;
}
void BuildOrderQueue::queueItem(const BuildOrderItem & b)
{
// if the queue is empty, set the highest and lowest priorities
if (m_queue.empty())
{
m_highestPriority = b.priority;
m_lowestPriority = b.priority;
}
// push the item into the queue
if (b.priority <= m_lowestPriority)
{
m_queue.push_front(b);
}
else
{
m_queue.push_back(b);
}
// if the item is somewhere in the middle, we have to sort again
if ((m_queue.size() > 1) && (b.priority < m_highestPriority) && (b.priority > m_lowestPriority))
{
// sort the list in ascending order, putting highest priority at the top
std::sort(m_queue.begin(), m_queue.end());
}
// update the highest or lowest if it is beaten
m_highestPriority = (b.priority > m_highestPriority) ? b.priority : m_highestPriority;
m_lowestPriority = (b.priority < m_lowestPriority) ? b.priority : m_lowestPriority;
}
void BuildOrderQueue::queueAsHighestPriority(const MetaType & type, bool blocking)
{
// the new priority will be higher
int newPriority = m_highestPriority + m_defaultPrioritySpacing;
// queue the item
queueItem(BuildOrderItem(type, newPriority, blocking));
}
void BuildOrderQueue::queueAsLowestPriority(const MetaType & type, bool blocking)
{
// the new priority will be higher
int newPriority = m_lowestPriority - m_defaultPrioritySpacing;
// queue the item
queueItem(BuildOrderItem(type, newPriority, blocking));
}
void BuildOrderQueue::removeHighestPriorityItem()
{
// remove the back element of the vector
m_queue.pop_back();
// if the list is not empty, set the highest accordingly
m_highestPriority = m_queue.empty() ? 0 : m_queue.back().priority;
m_lowestPriority = m_queue.empty() ? 0 : m_lowestPriority;
}
void BuildOrderQueue::removeCurrentHighestPriorityItem()
{
// remove the back element of the vector
m_queue.erase(m_queue.begin() + m_queue.size() - 1 - m_numSkippedItems);
//assert((int)(queue.size()) < size);
// if the list is not empty, set the highest accordingly
m_highestPriority = m_queue.empty() ? 0 : m_queue.back().priority;
m_lowestPriority = m_queue.empty() ? 0 : m_lowestPriority;
}
size_t BuildOrderQueue::size()
{
return m_queue.size();
}
bool BuildOrderQueue::isEmpty()
{
return (m_queue.size() == 0);
}
BuildOrderItem BuildOrderQueue::operator [] (int i)
{
return m_queue[i];
}
std::string BuildOrderQueue::getQueueInformation() const
{
size_t reps = m_queue.size() < 30 ? m_queue.size() : 30;
std::stringstream ss;
// for each unit in the queue
for (size_t i(0); i<reps; i++)
{
const MetaType & type = m_queue[m_queue.size() - 1 - i].type;
ss << type.getName() << "\n";
}
return ss.str();
}
BuildOrderItem::BuildOrderItem(const MetaType & t, int p, bool b)
: type(t)
, priority(p)
, blocking(b)
{
}
bool BuildOrderItem::operator < (const BuildOrderItem & x) const
{
return priority < x.priority;
}
\ No newline at end of file
#pragma once
#include "Common.h"
#include "MetaType.h"
class IDABot;
struct BuildOrderItem
{
MetaType type; // the thing we want to 'build'
int priority; // the priority at which to place it in the queue
bool blocking; // whether or not we block further items
BuildOrderItem(const MetaType & t, int p, bool b);
bool operator<(const BuildOrderItem & x) const;
};
class BuildOrderQueue
{
IDABot & m_bot;
std::deque<BuildOrderItem> m_queue;
int m_lowestPriority;
int m_highestPriority;
int m_defaultPrioritySpacing;
int m_numSkippedItems;
public:
BuildOrderQueue(IDABot & bot);
void clearAll(); // clears the entire build order queue
void skipItem(); // increments skippedItems
void queueAsHighestPriority(const MetaType & type, bool blocking); // queues something at the highest priority
void queueAsLowestPriority(const MetaType & type, bool blocking); // queues something at the lowest priority
void queueItem(const BuildOrderItem & b); // queues something with a given priority
void removeHighestPriorityItem(); // removes the highest priority item
void removeCurrentHighestPriorityItem();
size_t size(); // returns the size of the queue
bool isEmpty();
BuildOrderItem & getHighestPriorityItem(); // returns the highest priority item
BuildOrderItem & getNextHighestPriorityItem(); // returns the highest priority item
bool canSkipItem();
std::string getQueueInformation() const;
// overload the bracket operator for ease of use
BuildOrderItem operator [] (int i);
};
#include "Building.h"
Building::Building()
: desiredPosition (0,0)
, finalPosition (0,0)
, position (0,0)
, type ()
, buildingUnit ()
, builderUnit ()
, lastOrderFrame (0)
, status (BuildingStatus::Unassigned)
, buildCommandGiven (false)
, underConstruction (false)
{}
// constructor we use most often
Building::Building(UnitType t, CCTilePosition desired)
: desiredPosition (desired)
, finalPosition (0,0)
, position (0,0)
, type (t)
, buildingUnit ()
, builderUnit ()
, lastOrderFrame (0)
, status (BuildingStatus::Unassigned)
, buildCommandGiven (false)
, underConstruction (false)
{}
// equals operator
bool Building::operator == (const Building & b)
{
// buildings are equal if their worker unit and building unit are equal
return (b.buildingUnit == buildingUnit)
&& (b.builderUnit == builderUnit)
&& (b.finalPosition.x == finalPosition.x)
&& (b.finalPosition.y == finalPosition.y);
}
\ No newline at end of file
#pragma once
#include "Common.h"
#include "Unit.h"
#include "UnitType.h"
namespace BuildingStatus
{
enum { Unassigned = 0, Assigned = 1, UnderConstruction = 2, Size = 3 };
}
class Building
{
public:
CCTilePosition desiredPosition;
CCTilePosition finalPosition;
CCTilePosition position;
UnitType type;
Unit buildingUnit;
Unit builderUnit;
size_t status;
int lastOrderFrame;
bool buildCommandGiven;
bool underConstruction;
Building();
// constructor we use most often
Building(UnitType t, CCTilePosition desired);
// equals operator
bool operator == (const Building & b);
};
#include "BuildingData.h"
BuildingData::BuildingData()
{
}
void BuildingData::removeBuilding(const Building & b)
{
const auto & building = std::find(_buildings.begin(), _buildings.end(), b);
if (building != _buildings.end())
{
_buildings.erase(building);
}
}
std::vector<Building> & BuildingData::getBuildings()
{
return _buildings;
}
void BuildingData::addBuilding(const Building & b)
{
_buildings.push_back(b);
}
bool BuildingData::isBeingBuilt(UnitType type)
{
for (auto & b : _buildings)
{
if (b.type == type)
{
return true;
}
}
return false;
}
void BuildingData::removeBuildings(const std::vector<Building> & buildings)
{
for (const auto & b : buildings)
{
removeBuilding(b);
}
}
\ No newline at end of file
#pragma once
#include "Common.h"
#include "Building.h"
class BuildingData
{
std::vector<Building> _buildings;
public:
BuildingData();
std::vector<Building> & getBuildings();
void addBuilding(const Building & b);
void removeBuilding(const Building & b);
void removeBuildings(const std::vector<Building> & buildings);
bool isBeingBuilt(UnitType type);
};
\ No newline at end of file
#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;
};
#include "Common.h"
#include "BuildingPlacer.h"
#include "IDABot.h"
#include "Building.h"
#include "Util.h"
BuildingPlacer::BuildingPlacer(IDABot & bot)
......@@ -15,25 +14,47 @@ void BuildingPlacer::onStart()
m_reserveMap = std::vector< std::vector<bool> >(m_bot.Map().width(), std::vector<bool>(m_bot.Map().height(), false));
}
void BuildingPlacer::updateReserved(const std::vector<Unit> & units)
{
freeAllTiles();
for (Unit unit : units) {
reserveTiles(unit.getTilePosition().x, unit.getTilePosition().y, unit.getType().tileWidth(), unit.getType().tileHeight());
}
}
void BuildingPlacer::freeAllTiles() {
for (int x = 0; x < m_reserveMap.size(); x++) {
for (int y = 0; y < m_reserveMap[x].size(); y++) {
m_reserveMap[x][y] = false;
}
}
}
bool BuildingPlacer::isInResourceBox(int tileX, int tileY) const
{
return m_bot.Bases().getPlayerStartingBaseLocation(Players::Self)->isInResourceBox(tileX, tileY);
}
// makes final checks to see if a building can be built at a certain location
bool BuildingPlacer::canBuildHere(int bx, int by, const Building & b) const
bool BuildingPlacer::canBuildHere(int bx, int by, const UnitType & type) const
{
if (isInResourceBox(bx, by))
{
return false;
}
// check the reserve map
for (int x = bx; x < bx + b.type.tileWidth(); x++)
int width = type.tileWidth();
int height = type.tileHeight();
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
// check the reserve map, that it is a valid tile and not a wall
for (int x = bx - xdelta; x < bx + width - xdelta; x++)
{
for (int y = by; y < by + b.type.tileHeight(); y++)
for (int y = by - ydelta; y < by + height - ydelta; y++)
{
if (!m_bot.Map().isValidTile(x, y) || m_reserveMap[x][y])
if (!m_bot.Map().isValidTile(x, y) || isReserved(x, y) || !m_bot.Map().isWalkable(x, y))
{
return false;
}
......@@ -41,7 +62,7 @@ bool BuildingPlacer::canBuildHere(int bx, int by, const Building & b) const
}
// if it overlaps a base location return false
if (tileOverlapsBaseLocation(bx, by, b.type))
if (tileOverlapsBaseLocation(bx, by, type))
{
return false;
}
......@@ -49,45 +70,105 @@ bool BuildingPlacer::canBuildHere(int bx, int by, const Building & b) const
return true;
}
//returns true if we can build this type of unit here with the specified amount of space.
bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const Building & b, int buildDist) const
bool BuildingPlacer::canBuildHereWithSize(int bx, int by, int width, int height)
{
UnitType type = b.type;
if (isInResourceBox(bx, by))
{
return false;
}
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
// check the reserve map, that it is a valid tile and not a wall
for (int x = bx - xdelta; x < bx + width - xdelta; x++)
{
for (int y = by - ydelta; y < by + height - ydelta; y++)
{
if (!m_bot.Map().isValidTile(x, y) || isReserved(x, y) || !m_bot.Map().isWalkable(x, y))
{
return false;
}
}
}
// Check so it doesn't overlap with a baselocation
// dimensions of the proposed location
int tx1 = bx - xdelta;
int ty1 = by - ydelta;
int tx2 = tx1 + width - xdelta;
int ty2 = ty1 + height - ydelta;
// for each base location
for (const BaseLocation * base : m_bot.Bases().getBaseLocations())
{
// dimensions of the base location
int bx1 = (int)base->getDepotPosition().x;
int by1 = (int)base->getDepotPosition().y;
int bx2 = bx1 + Util::GetTownHall(m_bot.GetPlayerRace(Players::Self), m_bot).tileWidth();
int by2 = by1 + Util::GetTownHall(m_bot.GetPlayerRace(Players::Self), m_bot).tileHeight();
// conditions for non-overlap are easy
bool noOverlap = (tx2 < bx1) || (tx1 > bx2) || (ty2 < by1) || (ty1 > by2);
// If there is overlap, return false
if (!noOverlap)
{
return false;
}
}
// otherwise there is no overlap
return true;
}
//returns true if we can build this type of unit here with the specified amount of space.
bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const UnitType & type, int buildDist) const
{
//if we can't build here, we of course can't build here with space
if (!canBuildHere(bx, by, b))
if (!canBuildHere(bx, by, type))
{
return false;
}
// height and width of the building
int width = b.type.tileWidth();
int height = b.type.tileHeight();
int width = type.tileWidth();
int height = type.tileHeight();
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
// TODO: make sure we leave space for add-ons. These types of units can have addons:
// define the rectangle of the building spot
int startx = bx - buildDist;
int starty = by - buildDist;
int endx = bx + width + buildDist;
int endy = by + height + buildDist;
int startx = bx - buildDist - xdelta;
int starty = by - buildDist - ydelta;
int endx = bx + width + buildDist - xdelta;
int endy = by + height + buildDist - ydelta;
// TODO: recalculate start and end positions for addons
// if this rectangle doesn't fit on the map we can't build here
if (startx < 0 || starty < 0 || endx > m_bot.Map().width() || endx < bx + width || endy > m_bot.Map().height())
if (startx < 0 || starty < 0 || endx > m_bot.Map().width() || endx < bx + width - xdelta || endy > m_bot.Map().height() || endy < by + height - ydelta)
{
return false;
}
if (!m_bot.Map().canBuildTypeAtPosition(bx, by, type))
{
return false;
}
// if we can't build here, or space is reserved, or it's in the resource box, we can't build here
for (int x = startx; x < endx; x++)
{
for (int y = starty; y < endy; y++)
{
if (!b.type.isRefinery())
if (!type.isRefinery())
{
if (!buildable(b, x, y) || m_reserveMap[x][y])
if (!buildable(type, x, y) || isReserved(x, y) || !m_bot.Map().isWalkable(x, y)) // added isWalkabale in order to check towards walls
{
return false;
}
......@@ -98,22 +179,24 @@ bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const Building & b, i
return true;
}
CCTilePosition BuildingPlacer::getBuildLocationNear(const Building & b, int buildDist) const
// BuildDist is the distance from the position where the building is gonna be placed.
CCTilePosition BuildingPlacer::getBuildLocationNear(const CCTilePosition & p, const UnitType & t, int buildDist, size_t search_count) const
{
//Timer t;
//t.start();
// get the precomputed vector of tile positions which are sorted closes to this location
auto & closestToBuilding = m_bot.Map().getClosestTilesTo(b.desiredPosition);
auto & closestToBuilding = m_bot.Map().getClosestTilesTo(p);
//double ms1 = t.getElapsedTimeInMilliSec();
// iterate through the list until we've found a suitable location
for (size_t i(0); i < closestToBuilding.size() && i < 1000; ++i)
for (size_t i(0); i < closestToBuilding.size() && (search_count == 0 || i < search_count); ++i)
{
auto & pos = closestToBuilding[i];
if (canBuildHereWithSpace(pos.x, pos.y, b, buildDist))
if (canBuildHereWithSpace(pos.x, pos.y, t, buildDist))
{
//double ms = t.getElapsedTimeInMilliSec();
//printf("Building Placer Took %d iterations, lasting %lf ms @ %lf iterations/ms, %lf setup ms\n", (int)i, ms, (i / ms), ms1);
......@@ -124,6 +207,7 @@ CCTilePosition BuildingPlacer::getBuildLocationNear(const Building & b, int buil
//double ms = t.getElapsedTimeInMilliSec();
//printf("Building Placer Failure: %s - Took %lf ms\n", b.type.getName().c_str(), ms);
std::cout << "Warning! Could not find valid placement for " << t.getName() << " near (" << p.x << ", " << p.y << "). Returning (0, 0) instead.";
return CCTilePosition(0, 0);
}
......@@ -137,10 +221,14 @@ bool BuildingPlacer::tileOverlapsBaseLocation(int x, int y, UnitType type) const
}
// dimensions of the proposed location
int tx1 = x;
int ty1 = y;
int tx2 = tx1 + type.tileWidth();
int ty2 = ty1 + type.tileHeight();
int xdelta = (int)std::ceil((type.tileWidth() - 1.0) / 2);
int ydelta = (int)std::ceil((type.tileHeight() - 1.0) / 2);
int tx1 = x - xdelta;
int ty1 = y - ydelta;
int tx2 = tx1 + type.tileWidth() - xdelta;
int ty2 = ty1 + type.tileHeight() - ydelta;
// for each base location
for (const BaseLocation * base : m_bot.Bases().getBaseLocations())
......@@ -165,10 +253,10 @@ bool BuildingPlacer::tileOverlapsBaseLocation(int x, int y, UnitType type) const
return false;
}
bool BuildingPlacer::buildable(const Building & b, int x, int y) const
bool BuildingPlacer::buildable(const UnitType & type, int x, int y) const
{
// TODO: does this take units on the map into account?
if (!m_bot.Map().isValidTile(x, y) || !m_bot.Map().canBuildTypeAtPosition(x, y, b.type))
if (!m_bot.Map().isValidTile(x, y))
{
return false;
}
......@@ -180,11 +268,16 @@ bool BuildingPlacer::buildable(const Building & b, int x, int y) const
void BuildingPlacer::reserveTiles(int bx, int by, int width, int height)
{
// THIS is never called, that's why spacing doesnt work correctly
int rwidth = (int)m_reserveMap.size();
int rheight = (int)m_reserveMap[0].size();
for (int x = bx; x < bx + width && x < rwidth; x++)
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
for (int x = bx - xdelta; x < bx + width - xdelta && x < rwidth; x++)
{
for (int y = by; y < by + height && y < rheight; y++)
for (int y = by - ydelta; y < by + height - ydelta && y < rheight; y++)
{
m_reserveMap[x][y] = true;
}
......@@ -193,6 +286,7 @@ void BuildingPlacer::reserveTiles(int bx, int by, int width, int height)
void BuildingPlacer::drawReservedTiles()
{
// Why is there a return here? Should we not use the function? /Hannes Jmtner
return;
int rwidth = (int)m_reserveMap.size();
int rheight = (int)m_reserveMap[0].size();
......@@ -214,9 +308,12 @@ void BuildingPlacer::freeTiles(int bx, int by, int width, int height)
int rwidth = (int)m_reserveMap.size();
int rheight = (int)m_reserveMap[0].size();
for (int x = bx; x < bx + width && x < rwidth; x++)
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
for (int x = bx - xdelta; x < bx + width - xdelta && x < rwidth; x++)
{
for (int y = by; y < by + height && y < rheight; y++)
for (int y = by - ydelta; y < by + height - ydelta && y < rheight; y++)
{
m_reserveMap[x][y] = false;
}
......@@ -229,8 +326,12 @@ CCTilePosition BuildingPlacer::getRefineryPosition()
double minGeyserDistanceFromHome = std::numeric_limits<double>::max();
CCPosition homePosition = m_bot.GetStartLocation();
for (auto & unit : m_bot.GetAllUnits())
{
UnitType refinery = Util::GetRefinery(m_bot.GetPlayerRace(Players::Self), m_bot);
if (!unit.getType().isGeyser())
{
continue;
......@@ -238,6 +339,13 @@ CCTilePosition BuildingPlacer::getRefineryPosition()
CCPosition geyserPos(unit.getPosition());
// can't build a refinery on top of another
if (!m_bot.Map().canBuildTypeAtPosition((int)geyserPos.x, (int)geyserPos.y, refinery))
{
continue;
}
// check to see if it's next to one of our depots
bool nearDepot = false;
for (auto & unit : m_bot.UnitInfo().getUnits(Players::Self))
......@@ -259,12 +367,7 @@ CCTilePosition BuildingPlacer::getRefineryPosition()
}
}
}
#ifdef SC2API
return CCTilePosition((int)closestGeyser.x, (int)closestGeyser.y);
#else
return CCTilePosition(closestGeyser);
#endif
}
bool BuildingPlacer::isReserved(int x, int y) const
......
#pragma once
#include "Common.h"
#include "BuildingData.h"
class IDABot;
class BaseLocation;
class UnitType;
class Unit;
class BuildingPlacer
{
......@@ -13,24 +14,26 @@ class BuildingPlacer
std::vector< std::vector<bool> > m_reserveMap;
// queries for various BuildingPlacer data
bool buildable(const Building & b, int x, int y) const;
bool buildable(const UnitType & type, int x, int y) const;
bool isReserved(int x, int y) const;
bool isInResourceBox(int x, int y) const;
bool tileOverlapsBaseLocation(int x, int y, UnitType type) const;
public:
BuildingPlacer(IDABot & bot);
void onStart();
void updateReserved(const std::vector<Unit> & units);
void freeAllTiles();
// determines whether we can build at a given location
bool canBuildHere(int bx, int by, const Building & b) const;
bool canBuildHereWithSpace(int bx, int by, const Building & b, int buildDist) const;
bool canBuildHere(int bx, int by, const UnitType & type) const;
bool canBuildHereWithSize(int bx, int by, int width, int height);
bool canBuildHereWithSpace(int bx, int by, const UnitType & type, int buildDist) const;
// returns a build location near a building's desired location
CCTilePosition getBuildLocationNear(const Building & b, int buildDist) const;
CCTilePosition getBuildLocationNear(const CCTilePosition & p, const UnitType & type, int buildDist, size_t search_count = 1000) const;
void drawReservedTiles();
......
......@@ -2,12 +2,13 @@
file(GLOB BOT_SOURCES "*.cpp" "*.h" "*.hpp")
include_directories(SYSTEM
${PROJECT_SOURCE_DIR}/lib/s2client-api/include
${PROJECT_SOURCE_DIR}/lib/s2client-api/contrib/protobuf/src
${PROJECT_BINARY_DIR}/lib/s2client-api/generated
${PROJECT_SOURCE_DIR}/lib/cpp-sc2/include
${PROJECT_SOURCE_DIR}/lib/cpp-sc2/contrib/protobuf/src
${PROJECT_BINARY_DIR}/lib/cpp-sc2/generated
${PROJECT_SOURCE_DIR}/lib/json/include
)
link_directories(${PROJECT_BINARY_DIR}/s2client-api/bin)
link_directories(${PROJECT_BINARY_DIR}/cpp-sc2/bin)
# Enable compilation of the SC2 version of the bot.
add_definitions(-DSC2API)
......
......@@ -24,10 +24,11 @@ typedef sc2::Tag CCUnitID;
typedef sc2::Race CCRace;
typedef float CCHealth;
typedef float CCPositionType;
typedef sc2::BuffID CCBuff;
typedef size_t CCPlayer;
namespace Players
{
enum {Self = 0u, Enemy = 1u, Neutral = 2u, Ally = 3u, Size = 4u, None = 5u};
}
\ No newline at end of file
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.