Skip to content
Snippets Groups Projects
BaseLocation.cpp 7.94 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "BaseLocation.h"
    #include "Util.h"
    #include "IDABot.h"
    #include <sstream>
    #include <iostream>
    
    const int NearBaseLocationTileDistance = 20;
    
    BaseLocation::BaseLocation(IDABot & bot, int baseID, const std::vector<Unit> & resources)
        : m_bot(bot)
        , m_baseID               (baseID)
        , m_isStartLocation      (false)
        , m_left                 (std::numeric_limits<CCPositionType>::max())
        , m_right                (std::numeric_limits<CCPositionType>::lowest())
        , m_top                  (std::numeric_limits<CCPositionType>::lowest())
        , m_bottom               (std::numeric_limits<CCPositionType>::max())
    {
        m_isPlayerStartLocation[0] = false;
        m_isPlayerStartLocation[1] = false;
        m_isPlayerOccupying[0] = false;
        m_isPlayerOccupying[1] = false;
    
        CCPositionType resourceCenterX = 0;
        CCPositionType resourceCenterY = 0;
    
        // add each of the resources to its corresponding container
        for (auto & resource : resources)
        {
            if (resource.getType().isMineral())
            {
                m_minerals.push_back(resource);
                m_mineralPositions.push_back(resource.getPosition());
    
                // add the position of the minerals to the center
                resourceCenterX += resource.getPosition().x;
                resourceCenterY += resource.getPosition().y;
            }
            else
            {
                m_geysers.push_back(resource);
                m_geyserPositions.push_back(resource.getPosition());
    
                // pull the resource center toward the geyser if it exists
                resourceCenterX += resource.getPosition().x;
                resourceCenterY += resource.getPosition().y;
            }
    
            // set the limits of the base location bounding box
            CCPositionType resWidth = Util::TileToPosition(1);
            CCPositionType resHeight = Util::TileToPosition(0.5);
    
            m_left   = std::min(m_left,   resource.getPosition().x - resWidth);
            m_right  = std::max(m_right,  resource.getPosition().x + resWidth);
            m_top    = std::max(m_top,    resource.getPosition().y + resHeight);
            m_bottom = std::min(m_bottom, resource.getPosition().y - resHeight);
        }
    
        // calculate the center of the resources
        size_t numResources = m_minerals.size() + m_geysers.size();
    
        m_centerOfResources = CCPosition(m_left + (m_right-m_left)/2, m_top + (m_bottom-m_top)/2);
    
        // compute this BaseLocation's DistanceMap, which will compute the ground distance
        // from the center of its recourses to every other tile on the map
        m_distanceMap = m_bot.Map().getDistanceMap(m_centerOfResources);
    
        // check to see if this is a start location for the map
        for (auto & pos : m_bot.GetStartLocations())
        {
            if (containsPosition(pos))
            {
                m_isStartLocation = true;
                m_depotPosition = Util::GetTilePosition(pos);
            }
        }
        
        // if this base location position is near our own resource depot, it's our start location
        for (auto & unit : m_bot.GetAllUnits())
        {
            if (unit.getPlayer() == Players::Self && unit.getType().isResourceDepot() && containsPosition(unit.getPosition()))
            {
                m_isPlayerStartLocation[Players::Self] = true;
                m_isStartLocation = true;
                m_isPlayerOccupying[Players::Self] = true;
                break;
            }
        }
        
        // if it's not a start location, we need to calculate the depot position
        if (!isStartLocation())
        {
            UnitType depot = Util::GetTownHall(m_bot.GetPlayerRace(Players::Self), m_bot);
    #ifdef SC2API
            int offsetX = 0;
            int offsetY = 0;
    #else
            int offsetX = 1;
            int offsetY = 1;
    #endif
            
            // the position of the depot will be the closest spot we can build one from the resource center
            for (auto & tile : getClosestTiles())
            {
                // the build position will be up-left of where this tile is
                // this means we are positioning the center of the resouce depot
                CCTilePosition buildTile(tile.x - offsetX, tile.y - offsetY);
    
                if (m_bot.Map().canBuildTypeAtPosition(buildTile.x, buildTile.y, depot))
                {
                    m_depotPosition = buildTile;
                    break;
                }
            }
        }
    }
    
    // TODO: calculate the actual depot position
    const CCTilePosition & BaseLocation::getDepotPosition() const
    {
        return m_depotPosition;
    }
    
    void BaseLocation::setPlayerOccupying(CCPlayer player, bool occupying)
    {
        m_isPlayerOccupying[player] = occupying;
    
        // if this base is a start location that's occupied by the enemy, it's that enemy's start location
        if (occupying && player == Players::Enemy && isStartLocation() && m_isPlayerStartLocation[player] == false)
        {
            m_isPlayerStartLocation[player] = true;
        }
    }
    
    bool BaseLocation::isInResourceBox(int tileX, int tileY) const
    {
        CCPositionType px = Util::TileToPosition((float)tileX);
        CCPositionType py = Util::TileToPosition((float)tileY);
        return px >= m_left && px < m_right && py < m_top && py >= m_bottom;
    }
    
    bool BaseLocation::isOccupiedByPlayer(CCPlayer player) const
    {
        return m_isPlayerOccupying.at(player);
    }
    
    bool BaseLocation::isExplored() const
    {
        return m_bot.Map().isExplored(m_centerOfResources);
    }
    
    bool BaseLocation::isPlayerStartLocation(CCPlayer player) const
    {
        return m_isPlayerStartLocation.at(player);
    }
    
    bool BaseLocation::containsPosition(const CCPosition & pos) const
    {
        if (!m_bot.Map().isValidPosition(pos) || (pos.x == 0 && pos.y == 0))
        {
            return false;
        }
    
        return getGroundDistance(pos) < NearBaseLocationTileDistance;
    }
    
    const std::vector<Unit> & BaseLocation::getGeysers() const
    {
        return m_geysers;
    }
    
    const std::vector<Unit> & BaseLocation::getMinerals() const
    {
        return m_minerals;
    }
    
    const CCPosition & BaseLocation::getPosition() const
    {
        return m_centerOfResources;
    }
    
    int BaseLocation::getGroundDistance(const CCPosition & pos) const
    {
        return m_distanceMap.getDistance(pos);
    }
    
    int BaseLocation::getGroundDistance(const CCTilePosition & pos) const
    {
        return m_distanceMap.getDistance(pos);
    }
    
    bool BaseLocation::isStartLocation() const
    {
        return m_isStartLocation;
    }
    
    const std::vector<CCTilePosition> & BaseLocation::getClosestTiles() const
    {
        return m_distanceMap.getSortedTiles();
    }
    
    void BaseLocation::draw()
    {
        CCPositionType radius = Util::TileToPosition(1.0f);
    
        m_bot.Map().drawCircle(m_centerOfResources, radius, CCColor(255, 255, 0));
    
        std::stringstream ss;
        ss << "BaseLocation: " << m_baseID << "\n";
        ss << "Start Loc:    " << (isStartLocation() ? "true" : "false") << "\n";
        ss << "Minerals:     " << m_mineralPositions.size() << "\n";
        ss << "Geysers:      " << m_geyserPositions.size() << "\n";
        ss << "Occupied By:  ";
    
        if (isOccupiedByPlayer(Players::Self))
        {
            ss << "Self ";
        }
    
        if (isOccupiedByPlayer(Players::Enemy))
        {
            ss << "Enemy ";
        }
    
        m_bot.Map().drawText(CCPosition(m_left, m_top+3), ss.str().c_str());
        m_bot.Map().drawText(CCPosition(m_left, m_bottom), ss.str().c_str());
    
        // draw the base bounding box
        m_bot.Map().drawBox(m_left, m_top, m_right, m_bottom);
    
        for (CCPositionType x=m_left; x < m_right; x += Util::TileToPosition(1.0f))
        {
            //m_bot.Map().drawLine(x, m_top, x, m_bottom, CCColor(160, 160, 160));
        }
    
        for (CCPositionType y=m_bottom; y<m_top; y += Util::TileToPosition(1.0f))
        {
            //m_bot.Map().drawLine(m_left, y, m_right, y, CCColor(160, 160, 160));
        }
    
        for (auto & mineralPos : m_mineralPositions)
        {
            m_bot.Map().drawCircle(mineralPos, radius, CCColor(0, 128, 128));
        }
    
        for (auto & geyserPos : m_geyserPositions)
        {
            m_bot.Map().drawCircle(geyserPos, radius, CCColor(0, 255, 0));
        }
    
        if (m_isStartLocation)
        {
            m_bot.Map().drawCircle(Util::GetPosition(m_depotPosition), radius, CCColor(255, 0, 0));
        }
    
        m_bot.Map().drawTile(m_depotPosition.x, m_depotPosition.y, CCColor(0, 0, 255)); 
    
        //m_distanceMap.draw(m_bot);
    }
    
    bool BaseLocation::isMineralOnly() const
    {
        return getGeysers().empty();
    }