From 78be93ac748e3e637376a59bb8f44b69609f94fd Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Fri, 4 Sep 2020 12:09:55 +0600 Subject: [PATCH] Update to SteamHammer 3.1 --- Steamhammer/Source/Base.h | 3 +- Steamhammer/Source/Config.cpp | 3 +- Steamhammer/Source/Config.h | 1 + Steamhammer/Source/GameCommander.cpp | 1 + Steamhammer/Source/MacroAct.cpp | 105 +++++++--- Steamhammer/Source/MacroAct.h | 5 +- Steamhammer/Source/MacroCommand.cpp | 183 +++++++++++++++++ Steamhammer/Source/MacroCommand.h | 185 ++---------------- Steamhammer/Source/MapTools.cpp | 83 ++++++-- Steamhammer/Source/MapTools.h | 1 + Steamhammer/Source/Micro.cpp | 19 +- Steamhammer/Source/Micro.h | 1 + Steamhammer/Source/MicroRanged.cpp | 27 +-- Steamhammer/Source/ParseUtils.cpp | 6 +- Steamhammer/Source/ProductionManager.cpp | 19 +- Steamhammer/Source/ProductionManager.h | 1 + Steamhammer/Source/ScoutBoss.cpp | 7 - Steamhammer/Source/ScoutBoss.h | 30 --- Steamhammer/Source/StrategyBossZerg.cpp | 96 +++++++-- Steamhammer/Source/StrategyBossZerg.h | 2 + Steamhammer/Source/StrategyManager.cpp | 8 +- Steamhammer/Source/WorkerData.cpp | 13 ++ Steamhammer/Source/WorkerData.h | 3 +- Steamhammer/Source/WorkerManager.cpp | 46 ++++- Steamhammer/VisualStudio/UAlbertaBot.vcxproj | 3 +- .../VisualStudio/UAlbertaBot.vcxproj.filters | 3 +- 26 files changed, 551 insertions(+), 303 deletions(-) create mode 100644 Steamhammer/Source/MacroCommand.cpp delete mode 100644 Steamhammer/Source/ScoutBoss.cpp delete mode 100644 Steamhammer/Source/ScoutBoss.h diff --git a/Steamhammer/Source/Base.h b/Steamhammer/Source/Base.h index fe3447f..714c2b2 100644 --- a/Steamhammer/Source/Base.h +++ b/Steamhammer/Source/Base.h @@ -58,7 +58,8 @@ class Base const GridDistances & getDistances() const { return distances; }; int getTileDistance(const BWAPI::Position & pos) const { return distances.at(pos); }; int getTileDistance(const BWAPI::TilePosition & pos) const { return distances.at(pos); }; - int getDistance(const BWAPI::Position & pos) const { return 32 * getTileDistance(pos); }; + int getDistance(const BWAPI::TilePosition & pos) const { return 32 * getTileDistance(pos); }; + int getDistance(const BWAPI::Position & pos) const { return 32 * getTileDistance(pos); }; void setOwner(BWAPI::Unit depot, BWAPI::Player player); void setInferredEnemyBase(); diff --git a/Steamhammer/Source/Config.cpp b/Steamhammer/Source/Config.cpp index 1368c31..451c0bd 100644 --- a/Steamhammer/Source/Config.cpp +++ b/Steamhammer/Source/Config.cpp @@ -12,7 +12,7 @@ namespace Config { bool ConfigFileFound = false; bool ConfigFileParsed = false; - std::string ConfigFileLocation = "bwapi-data/AI/Steamhammer_3.0.2.json"; + std::string ConfigFileLocation = "bwapi-data/AI/Steamhammer_3.1.json"; } namespace IO @@ -96,6 +96,7 @@ namespace Config bool DrawMapDistances = false; bool DrawTerrainHeights = false; bool DrawBaseInfo = false; + bool DrawExpoScores = false; bool DrawStrategyBossInfo = false; bool DrawUnitTargets = false; bool DrawUnitOrders = false; diff --git a/Steamhammer/Source/Config.h b/Steamhammer/Source/Config.h index 0a579fa..1e416fe 100644 --- a/Steamhammer/Source/Config.h +++ b/Steamhammer/Source/Config.h @@ -90,6 +90,7 @@ namespace Config extern bool DrawMapDistances; extern bool DrawTerrainHeights; extern bool DrawBaseInfo; + extern bool DrawExpoScores; extern bool DrawStrategyBossInfo; extern bool DrawSquadInfo; extern bool DrawClusters; diff --git a/Steamhammer/Source/GameCommander.cpp b/Steamhammer/Source/GameCommander.cpp index f3511bf..7f5fb15 100644 --- a/Steamhammer/Source/GameCommander.cpp +++ b/Steamhammer/Source/GameCommander.cpp @@ -118,6 +118,7 @@ void GameCommander::drawDebugInterface() drawUnitCounts(345, 30); Bases::Instance().drawBaseInfo(); Bases::Instance().drawBaseOwnership(575, 30); + the.map.drawExpoScores(); InformationManager::Instance().drawResourceAmounts(); BuildingManager::Instance().drawBuildingInformation(200, 50); the.placer.drawReservedTiles(); diff --git a/Steamhammer/Source/MacroAct.cpp b/Steamhammer/Source/MacroAct.cpp index f28e448..dfe898d 100644 --- a/Steamhammer/Source/MacroAct.cpp +++ b/Steamhammer/Source/MacroAct.cpp @@ -9,7 +9,10 @@ using namespace UAlbertaBot; -MacroLocation MacroAct::getMacroLocationFromString(const std::string & s) +// Map unit type names to unit types. +static std::map _unitTypesByName; + +MacroLocation MacroAct::getMacroLocationFromString(const std::string & s) const { if (s == "main") { @@ -65,6 +68,30 @@ MacroLocation MacroAct::getMacroLocationFromString(const std::string & s) return MacroLocation::Anywhere; } +void MacroAct::initializeUnitTypesByName() +{ + if (_unitTypesByName.size() == 0) // if not already initialized + { + for (BWAPI::UnitType unitType : BWAPI::UnitTypes::allUnitTypes()) + { + std::string typeName = TrimRaceName(unitType.getName()); + std::replace(typeName.begin(), typeName.end(), '_', ' '); + std::transform(typeName.begin(), typeName.end(), typeName.begin(), ::tolower); + _unitTypesByName[typeName] = unitType; + } + } +} + +BWAPI::UnitType MacroAct::getUnitTypeFromString(const std::string & s) const +{ + auto it = _unitTypesByName.find(s); + if (it != _unitTypesByName.end()) + { + return (*it).second; + } + return BWAPI::UnitTypes::Unknown; +} + MacroAct::MacroAct () : _type(MacroActs::Default) , _macroLocation(MacroLocation::Anywhere) @@ -77,9 +104,24 @@ MacroAct::MacroAct(const std::string & name) : _type(MacroActs::Default) , _macroLocation(MacroLocation::Anywhere) { + initializeUnitTypesByName(); + + // Normalize the input string. std::string inputName(name); std::replace(inputName.begin(), inputName.end(), '_', ' '); std::transform(inputName.begin(), inputName.end(), inputName.begin(), ::tolower); + if (inputName.substr(0, 7) == "terran ") + { + inputName = inputName.substr(7, std::string::npos); + } + else if (inputName.substr(0, 8) == "protoss ") + { + inputName = inputName.substr(8, std::string::npos); + } + else if (inputName.substr(0, 5) == "zerg ") + { + inputName = inputName.substr(5, std::string::npos); + } // You can specify a location, like "hatchery @ expo" or "go post worker @ enemy natural". MacroLocation specifiedMacroLocation(MacroLocation::Anywhere); // the default @@ -98,20 +140,38 @@ MacroAct::MacroAct(const std::string & name) for (MacroCommandType t : MacroCommand::allCommandTypes()) { std::string commandName = MacroCommand::getName(t); - if (MacroCommand::hasArgument(t)) + if (MacroCommand::hasNumericArgument(t)) { // There's an argument. Match the command name and parse out the argument. std::regex commandWithArgRegex(commandName + " (\\d+)"); std::smatch m; - if (std::regex_match(inputName, m, commandWithArgRegex)) { + if (std::regex_match(inputName, m, commandWithArgRegex)) + { int amount = GetIntFromString(m[1].str()); - if (amount >= 0) { + if (amount >= 0) + { *this = MacroAct(t, amount); _macroLocation = specifiedMacroLocation; return; } } } + else if (MacroCommand::hasUnitArgument(t)) + { + // There's an argument. Match the command name and parse out the argument. + std::regex commandWithArgRegex(commandName + " ([A-Za-z_ ]+)"); + std::smatch m; + if (std::regex_match(inputName, m, commandWithArgRegex)) + { + BWAPI::UnitType unitType = getUnitTypeFromString(m[1].str()); + if (unitType != BWAPI::UnitTypes::Unknown && unitType != BWAPI::UnitTypes::None) + { + *this = MacroAct(t, unitType); + _macroLocation = specifiedMacroLocation; + return; + } + } + } else { // No argument. Just compare for equality. @@ -125,28 +185,12 @@ MacroAct::MacroAct(const std::string & name) } } - for (BWAPI::UnitType unitType : BWAPI::UnitTypes::allUnitTypes()) + BWAPI::UnitType unitType = getUnitTypeFromString(inputName); + if (unitType != BWAPI::UnitTypes::Unknown && unitType != BWAPI::UnitTypes::None) { - // Check whether the names match exactly. - std::string typeName = unitType.getName(); - std::replace(typeName.begin(), typeName.end(), '_', ' '); - std::transform(typeName.begin(), typeName.end(), typeName.begin(), ::tolower); - if (typeName == inputName) - { - *this = MacroAct(unitType); - _macroLocation = specifiedMacroLocation; - return; - } - - // Check whether the names match without the race prefix. - std::string raceName = unitType.getRace().getName(); - std::transform(raceName.begin(), raceName.end(), raceName.begin(), ::tolower); - if ((typeName.length() > raceName.length()) && (typeName.compare(raceName.length() + 1, typeName.length(), inputName) == 0)) - { - *this = MacroAct(unitType); - _macroLocation = specifiedMacroLocation; - return; - } + *this = MacroAct(unitType); + _macroLocation = specifiedMacroLocation; + return; } for (BWAPI::TechType techType : BWAPI::TechTypes::allTechTypes()) @@ -164,7 +208,7 @@ MacroAct::MacroAct(const std::string & name) for (BWAPI::UpgradeType upgradeType : BWAPI::UpgradeTypes::allUpgradeTypes()) { - std::string typeName = upgradeType.getName(); + std::string typeName = TrimRaceName(upgradeType.getName()); std::replace(typeName.begin(), typeName.end(), '_', ' '); std::transform(typeName.begin(), typeName.end(), typeName.begin(), ::tolower); if (typeName == inputName) @@ -220,6 +264,13 @@ MacroAct::MacroAct(MacroCommandType t, int amount) { } +MacroAct::MacroAct(MacroCommandType t, BWAPI::UnitType type) + : _macroCommandType(t, type) + , _type(MacroActs::Command) + , _macroLocation(MacroLocation::Anywhere) +{ +} + size_t MacroAct::type() const { return _type; @@ -541,7 +592,7 @@ bool MacroAct::hasEventualProducer() const // any condition that makes it unable to produce ever. if (unit->getType() == producerType && unit->isPowered() && // replacing a pylon is a separate queue item - !unit->isLifted() && // lifting/landing a building will be a separate queue item when implemented + !unit->isLifted() && // lifting/landing a building is a separate queue item (!producerType.isAddon() || unit->getAddon() == nullptr)) { return true; diff --git a/Steamhammer/Source/MacroAct.h b/Steamhammer/Source/MacroAct.h index 576da9c..70e88ba 100644 --- a/Steamhammer/Source/MacroAct.h +++ b/Steamhammer/Source/MacroAct.h @@ -36,7 +36,9 @@ class MacroAct MacroLocation _macroLocation; - MacroLocation getMacroLocationFromString(const std::string & s); + void initializeUnitTypesByName(); + MacroLocation getMacroLocationFromString(const std::string & s) const; + BWAPI::UnitType getUnitTypeFromString(const std::string & s) const; public: @@ -48,6 +50,7 @@ class MacroAct MacroAct(BWAPI::UpgradeType t); MacroAct(MacroCommandType t); MacroAct(MacroCommandType t, int amount); + MacroAct(MacroCommandType t, BWAPI::UnitType type); bool isUnit() const; bool isWorker() const; diff --git a/Steamhammer/Source/MacroCommand.cpp b/Steamhammer/Source/MacroCommand.cpp new file mode 100644 index 0000000..fbb7864 --- /dev/null +++ b/Steamhammer/Source/MacroCommand.cpp @@ -0,0 +1,183 @@ +#include "MacroCommand.h" + +using namespace UAlbertaBot; + +// Default constructor for when the value doesn't matter. +MacroCommand::MacroCommand() + : _type(MacroCommandType::None) + , _amount(0) + , _unitType(BWAPI::UnitTypes::None) +{ +} + +MacroCommand::MacroCommand(MacroCommandType type) + : _type(type) + , _amount(0) + , _unitType(BWAPI::UnitTypes::None) +{ + UAB_ASSERT(!hasNumericArgument(type), "missing MacroCommand argument"); +} + +MacroCommand::MacroCommand(MacroCommandType type, int amount) + : _type(type) + , _amount(amount) + , _unitType(BWAPI::UnitTypes::None) +{ + UAB_ASSERT(hasNumericArgument(type), "extra MacroCommand argument"); +} + +MacroCommand::MacroCommand(MacroCommandType type, BWAPI::UnitType unitType) + : _type(type) + , _amount(0) + , _unitType(unitType) +{ + UAB_ASSERT(hasUnitArgument(type), "extra MacroCommand argument"); +} + +const std::list MacroCommand::allCommandTypes() +{ + return std::list + { MacroCommandType::Scout + , MacroCommandType::ScoutIfNeeded + , MacroCommandType::ScoutLocation + , MacroCommandType::ScoutOnceOnly + , MacroCommandType::ScoutWhileSafe + , MacroCommandType::StartGas + , MacroCommandType::StopGas + , MacroCommandType::GasUntil + , MacroCommandType::StealGas + , MacroCommandType::ExtractorTrickDrone + , MacroCommandType::ExtractorTrickZergling + , MacroCommandType::Aggressive + , MacroCommandType::Defensive + , MacroCommandType::PullWorkers + , MacroCommandType::PullWorkersLeaving + , MacroCommandType::ReleaseWorkers + , MacroCommandType::PostWorker + , MacroCommandType::UnpostWorkers + , MacroCommandType::Nonadaptive + , MacroCommandType::Lift + , MacroCommandType::QueueBarrier + }; +} + +// The command has a numeric argument, the _amount. +bool MacroCommand::hasNumericArgument(MacroCommandType t) +{ + return + t == MacroCommandType::GasUntil || + t == MacroCommandType::PullWorkers || + t == MacroCommandType::PullWorkersLeaving; +} + +// The command has a unit type argument, the _unitType. +bool MacroCommand::hasUnitArgument(MacroCommandType t) +{ + return + t == MacroCommandType::Lift; +} + +const std::string MacroCommand::getName(MacroCommandType t) +{ + if (t == MacroCommandType::Scout) + { + return "go scout"; + } + if (t == MacroCommandType::ScoutIfNeeded) + { + return "go scout if needed"; + } + if (t == MacroCommandType::ScoutLocation) + { + return "go scout location"; + } + if (t == MacroCommandType::ScoutOnceOnly) + { + return "go scout once around"; + } + if (t == MacroCommandType::ScoutWhileSafe) + { + return "go scout while safe"; + } + if (t == MacroCommandType::StartGas) + { + return "go start gas"; + } + if (t == MacroCommandType::StopGas) + { + return "go stop gas"; + } + if (t == MacroCommandType::GasUntil) + { + return "go gas until"; + } + if (t == MacroCommandType::StealGas) + { + return "go steal gas"; + } + if (t == MacroCommandType::ExtractorTrickDrone) + { + return "go extractor trick drone"; + } + if (t == MacroCommandType::ExtractorTrickZergling) + { + return "go extractor trick zergling"; + } + if (t == MacroCommandType::Aggressive) + { + return "go aggressive"; + } + if (t == MacroCommandType::Defensive) + { + return "go defensive"; + } + if (t == MacroCommandType::PullWorkers) + { + return "go pull workers"; + } + if (t == MacroCommandType::PullWorkersLeaving) + { + return "go pull workers leaving"; + } + if (t == MacroCommandType::ReleaseWorkers) + { + return "go release workers"; + } + if (t == MacroCommandType::PostWorker) + { + return "go post worker"; + } + if (t == MacroCommandType::UnpostWorkers) + { + return "go unpost workers"; + } + if (t == MacroCommandType::Nonadaptive) + { + return "go nonadaptive"; + } + if (t == MacroCommandType::Lift) + { + return "go lift"; + } + if (t == MacroCommandType::QueueBarrier) + { + return "go queue barrier"; + } + + UAB_ASSERT(t == MacroCommandType::None, "unrecognized MacroCommandType"); + return "go none"; +} + +const std::string MacroCommand::getName() const +{ + if (hasNumericArgument(_type)) + { + std::stringstream name; + name << getName(_type) << " " << _amount; + return name.str(); + } + else + { + return getName(_type); + } +} diff --git a/Steamhammer/Source/MacroCommand.h b/Steamhammer/Source/MacroCommand.h index 025cc51..25517c0 100644 --- a/Steamhammer/Source/MacroCommand.h +++ b/Steamhammer/Source/MacroCommand.h @@ -26,7 +26,7 @@ enum class MacroCommandType , PostWorker , UnpostWorkers , Nonadaptive - , Attack + , Lift , QueueBarrier }; @@ -34,180 +34,25 @@ class MacroCommand { MacroCommandType _type; int _amount; + BWAPI::UnitType _unitType; public: - static const std::list allCommandTypes() - { - return std::list - { MacroCommandType::Scout - , MacroCommandType::ScoutIfNeeded - , MacroCommandType::ScoutLocation - , MacroCommandType::ScoutOnceOnly - , MacroCommandType::ScoutWhileSafe - , MacroCommandType::StartGas - , MacroCommandType::StopGas - , MacroCommandType::GasUntil - , MacroCommandType::StealGas - , MacroCommandType::ExtractorTrickDrone - , MacroCommandType::ExtractorTrickZergling - , MacroCommandType::Aggressive - , MacroCommandType::Defensive - , MacroCommandType::PullWorkers - , MacroCommandType::PullWorkersLeaving - , MacroCommandType::ReleaseWorkers - , MacroCommandType::PostWorker - , MacroCommandType::UnpostWorkers - , MacroCommandType::Nonadaptive - , MacroCommandType::Attack - , MacroCommandType::QueueBarrier - }; - } + MacroCommand(); + MacroCommand(MacroCommandType type); + MacroCommand(MacroCommandType type, int amount); + MacroCommand(MacroCommandType type, BWAPI::UnitType unitType); - // Default constructor for when the value doesn't matter. - MacroCommand() - : _type(MacroCommandType::None) - , _amount(0) - { - } + static const std::list allCommandTypes(); + static bool hasNumericArgument(MacroCommandType t); + static bool hasUnitArgument(MacroCommandType t); + static const std::string getName(MacroCommandType t); - MacroCommand(MacroCommandType type) - : _type(type) - , _amount(0) - { - UAB_ASSERT(!hasArgument(type), "missing MacroCommand argument"); - } + MacroCommandType getType() const { return _type; } + int getAmount() const { return _amount; } + BWAPI::UnitType getUnitType() const { return _unitType; } - MacroCommand(MacroCommandType type, int amount) - : _type(type) - , _amount(amount) - { - UAB_ASSERT(hasArgument(type), "extra MacroCommand argument"); - } - - const int getAmount() const - { - return _amount; - } - - const MacroCommandType & getType() const - { - return _type; - } - - // The command has a numeric argument, the _amount. - static const bool hasArgument(MacroCommandType t) - { - return - t == MacroCommandType::GasUntil || - t == MacroCommandType::PullWorkers || - t == MacroCommandType::PullWorkersLeaving; - } - - static const std::string getName(MacroCommandType t) - { - if (t == MacroCommandType::Scout) - { - return "go scout"; - } - if (t == MacroCommandType::ScoutIfNeeded) - { - return "go scout if needed"; - } - if (t == MacroCommandType::ScoutLocation) - { - return "go scout location"; - } - if (t == MacroCommandType::ScoutOnceOnly) - { - return "go scout once around"; - } - if (t == MacroCommandType::ScoutWhileSafe) - { - return "go scout while safe"; - } - if (t == MacroCommandType::StartGas) - { - return "go start gas"; - } - if (t == MacroCommandType::StopGas) - { - return "go stop gas"; - } - if (t == MacroCommandType::GasUntil) - { - return "go gas until"; - } - if (t == MacroCommandType::StealGas) - { - return "go steal gas"; - } - if (t == MacroCommandType::ExtractorTrickDrone) - { - return "go extractor trick drone"; - } - if (t == MacroCommandType::ExtractorTrickZergling) - { - return "go extractor trick zergling"; - } - if (t == MacroCommandType::Aggressive) - { - return "go aggressive"; - } - if (t == MacroCommandType::Defensive) - { - return "go defensive"; - } - if (t == MacroCommandType::PullWorkers) - { - return "go pull workers"; - } - if (t == MacroCommandType::PullWorkersLeaving) - { - return "go pull workers leaving"; - } - if (t == MacroCommandType::ReleaseWorkers) - { - return "go release workers"; - } - if (t == MacroCommandType::PostWorker) - { - return "go post worker"; - } - if (t == MacroCommandType::UnpostWorkers) - { - return "go unpost workers"; - } - if (t == MacroCommandType::Nonadaptive) - { - return "go nonadaptive"; - } - if (t == MacroCommandType::Attack) - { - return "go attack"; - } - if (t == MacroCommandType::QueueBarrier) - { - return "go queue barrier"; - } - - UAB_ASSERT(t == MacroCommandType::None, "unrecognized MacroCommandType"); - return "go none"; - } - - const std::string getName() const - { - if (hasArgument(_type)) - { - std::stringstream name; - name << getName(_type) << " " << _amount; - return name.str(); - } - else - { - return getName(_type); - } - } + const std::string getName() const; }; -} \ No newline at end of file +}; diff --git a/Steamhammer/Source/MapTools.cpp b/Steamhammer/Source/MapTools.cpp index 074af9e..940fcaf 100644 --- a/Steamhammer/Source/MapTools.cpp +++ b/Steamhammer/Source/MapTools.cpp @@ -215,6 +215,15 @@ void MapTools::drawHomeDistances() BWAPI::Broodwar->drawBoxMap(homePosition.x, homePosition.y, homePosition.x + 33, homePosition.y + 33, BWAPI::Colors::Yellow); } +// Make the assumption that we are looking for a mineral-only base. +void MapTools::drawExpoScores() +{ + if (Config::Debug::DrawExpoScores) + { + (void)nextExpansion(false, true, false); + } +} + Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) const { UAB_ASSERT(wantMinerals || wantGas, "unwanted expansion"); @@ -222,6 +231,7 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // Abbreviations. BWAPI::Player player = BWAPI::Broodwar->self(); BWAPI::Player enemy = BWAPI::Broodwar->enemy(); + BWAPI::Position offset(-32, -8); // We'll go through the bases and pick the one with the best score. Base * bestBase = nullptr; @@ -235,13 +245,21 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // Don't expand to an existing base, or a reserved base. if (base->getOwner() != BWAPI::Broodwar->neutral() || base->isReserved()) { + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%ctaken", red); + } continue; } // Do we demand a gas base? if (wantGas && base->getInitialGas() == 0) { - continue; + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset+offset, "%cno gas", red); + } + continue; } const int estimatedMinerals = base->getLastKnownMinerals(); @@ -250,7 +268,11 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // The constant is an arbitrary limit "enough minerals to be worth it". if (wantMinerals && estimatedMinerals < 500) { - continue; + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%cno minerals", red); + } + continue; } BWAPI::TilePosition topLeft = base->getTilePosition(); @@ -259,6 +281,10 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // No good if the building location is known to be in range of enemy static defense. if (the.groundAttacks.inRange(player->getRace().getCenter(), topLeft)) { + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%cendangered", red); + } continue; } @@ -278,6 +304,10 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con lastTilePos.y >= topLeft.y && lastTilePos.y < bottomRight.y) { + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%cblocked (known)", red); + } buildingInTheWay = true; goto buildingLoopExit; } @@ -292,7 +322,11 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con { if (the.placer.isReserved(topLeft.x + x, topLeft.y + y)) { - // This happens if we were already planning to expand here. Try somewhere else. + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%creserved tiles", yellow); + } + // This happens if we were already planning to expand here. Try somewhere else. buildingInTheWay = true; goto buildingLoopExit; } @@ -306,6 +340,10 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // TODO This is waiting until the code to lift is written. // !(unit->getPlayer() == BWAPI::Broodwar->self() && unit->canLift())) { + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%cblocked (tile)a", red); + } buildingInTheWay = true; goto buildingLoopExit; } @@ -330,7 +368,8 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // as a backup. // Want to be close to our own base (unless this is to be a hidden base). - int distanceFromUs = the.bases.myStart()->getTileDistance(topLeft); + // Pixel distance. + int distanceFromUs = the.bases.myStart()->getDistance(topLeft); // If it is not connected by ground, skip this potential base. if (distanceFromUs < 0) @@ -348,46 +387,58 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con // Want to be far from the enemy base. Base * enemyBase = the.bases.enemyStart(); // may be null or no longer enemy-owned - double distanceFromEnemy = 0.0; + // Pixel distance. + int distanceFromEnemy = 0; if (enemyBase) { - BWAPI::TilePosition enemyTile = enemyBase->getTilePosition(); - distanceFromEnemy = enemyBase->getTileDistance(topLeft); + distanceFromEnemy = enemyBase->getDistance(topLeft); if (distanceFromEnemy < 0) { // No ground distance found, so again substitute air distance. + BWAPI::TilePosition enemyTile = enemyBase->getTilePosition(); if (the.partitions.id(topLeft) == the.partitions.id(enemyTile)) { - distanceFromEnemy = enemyTile.getDistance(topLeft); + distanceFromEnemy = enemyTile.getApproxDistance(topLeft); } else { - distanceFromEnemy = 0.0; + distanceFromEnemy = 0; } } } // Add up the score. - score = hidden ? (distanceFromEnemy + distanceFromUs / 2.0) : (distanceFromEnemy / 2.0 - distanceFromUs); + score = hidden ? (distanceFromEnemy + distanceFromUs / 2) : (distanceFromEnemy / 2 - distanceFromUs); // Far from the edge of the map -> worse. // It's a proxy for "how wide open is this base?" Usually a base on the edge is // relatively sheltered and a base in the middle is more open (though not always). int edgeXdist = std::min(topLeft.x, BWAPI::Broodwar->mapWidth() - topLeft.x); int edgeYdist = std::min(topLeft.y, BWAPI::Broodwar->mapHeight() - topLeft.y); - int edgeDistance = std::min(edgeXdist, edgeYdist); - score += -3.0 * edgeDistance; + int edgeDistance = std::min(edgeXdist, edgeYdist); // tile distance + edgeDistance = std::max(0, edgeDistance - 12); // within this distance is always OK + if (edgeDistance > std::min(BWAPI::Broodwar->mapWidth(), BWAPI::Broodwar->mapHeight()) / 3) + { + // If we're very far from the edge, it's worse. + edgeDistance *= 4; + } + score -= 12.0 * edgeDistance; // More resources -> better. // NOTE The number of mineral patchers/geysers controls how fast we can mine. // The resource amount controls how long we can keep mining. if (wantMinerals) { - score += 10.0 * base->getMinerals().size() + 0.01 * estimatedMinerals; + score += 5.0 * base->getMinerals().size() + 0.005 * estimatedMinerals; } if (wantGas) { - score += 50.0 * base->getGeysers().size() + 0.025 * base->getLastKnownGas(); + score += 20.0 * base->getGeysers().size() + 0.01 * base->getLastKnownGas(); } + else + { + // We didn't ask for gas, but it may be useful anyway. + score += 5.0 * base->getGeysers().size() + 0.0025 * base->getLastKnownGas(); + } /* TODO on a flat map, all mains may be in the same zone // Big penalty for enemy buildings in the same region. @@ -398,6 +449,10 @@ Base * MapTools::nextExpansion(bool hidden, bool wantMinerals, bool wantGas) con */ // BWAPI::Broodwar->printf("base score %d, %d -> %f", tile.x, tile.y, score); + if (Config::Debug::DrawExpoScores) + { + BWAPI::Broodwar->drawTextMap(base->getCenter()+offset, "%c%g", green, score); + } if (score > bestScore) { diff --git a/Steamhammer/Source/MapTools.h b/Steamhammer/Source/MapTools.h index 781e932..a58634f 100644 --- a/Steamhammer/Source/MapTools.h +++ b/Steamhammer/Source/MapTools.h @@ -50,6 +50,7 @@ class MapTools const std::vector & getClosestTilesTo(BWAPI::Position pos); void drawHomeDistances(); + void drawExpoScores(); Base * nextExpansion(bool hidden, bool wantMinerals, bool wantGas) const; BWAPI::TilePosition getNextExpansion(bool hidden, bool wantMinerals, bool wantGas) const; diff --git a/Steamhammer/Source/Micro.cpp b/Steamhammer/Source/Micro.cpp index 8e8677e..7c1e362 100644 --- a/Steamhammer/Source/Micro.cpp +++ b/Steamhammer/Source/Micro.cpp @@ -1,4 +1,3 @@ -#include "Micro.h" #include "Base.h" #include "InformationManager.h" @@ -13,7 +12,7 @@ size_t TotalCommands = 0; // not all commands are counted // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // Complain if there is an "obvious" problem. -// So far, the only problem is issuing two orders during the same frame. +// So far, the only problem detected is issuing two orders during the same frame. void MicroState::check(BWAPI::Unit u, BWAPI::Order o) const { return; // TODO only check when debugging @@ -824,6 +823,7 @@ bool Micro::Build(BWAPI::Unit builder, BWAPI::UnitType building, const BWAPI::Ti builder->getPlayer() != BWAPI::Broodwar->self() || !building.isBuilding() || !location.isValid()) { + // TODO this error happens sometimes - fix it UAB_ASSERT(false, "bad building"); return false; } @@ -879,6 +879,21 @@ bool Micro::Cancel(BWAPI::Unit unit) return unit->cancelConstruction(); } +bool Micro::Lift(BWAPI::Unit terranBuilding) +{ + if (!terranBuilding || !terranBuilding->exists() || + !terranBuilding->getType().isBuilding() || + terranBuilding->getPlayer() != the.self()) + { + UAB_ASSERT(false, "bad unit"); + return false; + } + + orders[terranBuilding].setOrder(terranBuilding, BWAPI::Orders::LiftingOff); + + return terranBuilding->lift(); +} + // Burrow a zerg unit. bool Micro::Burrow(BWAPI::Unit unit) { diff --git a/Steamhammer/Source/Micro.h b/Steamhammer/Source/Micro.h index 3a0d663..4a3e373 100644 --- a/Steamhammer/Source/Micro.h +++ b/Steamhammer/Source/Micro.h @@ -90,6 +90,7 @@ class Micro bool Build(BWAPI::Unit builder, BWAPI::UnitType building, const BWAPI::TilePosition & location); bool Make(BWAPI::Unit producer, BWAPI::UnitType type); bool Cancel(BWAPI::Unit unit); + bool Lift(BWAPI::Unit terranBuilding); bool Burrow(BWAPI::Unit unit); bool Unburrow(BWAPI::Unit unit); diff --git a/Steamhammer/Source/MicroRanged.cpp b/Steamhammer/Source/MicroRanged.cpp index 2c02c34..cba6dd7 100644 --- a/Steamhammer/Source/MicroRanged.cpp +++ b/Steamhammer/Source/MicroRanged.cpp @@ -158,7 +158,7 @@ BWAPI::Unit MicroRanged::getTarget(BWAPI::Unit rangedUnit, const BWAPI::Unitset int bestScore = INT_MIN; BWAPI::Unit bestTarget = nullptr; - for (const auto target : targets) + for (BWAPI::Unit target : targets) { // Skip targets under dark swarm that we can't hit. if (target->isUnderDarkSwarm() && !target->getType().isBuilding() && !goodUnderDarkSwarm(rangedUnit->getType())) @@ -276,18 +276,18 @@ BWAPI::Unit MicroRanged::getTarget(BWAPI::Unit rangedUnit, const BWAPI::Unitset { if (target->getType().size() == BWAPI::UnitSizeTypes::Large) { - score += 32; + score += 48; } } else if (damage == BWAPI::DamageTypes::Concussive) { if (target->getType().size() == BWAPI::UnitSizeTypes::Small) { - score += 32; + score += 48; } else if (target->getType().size() == BWAPI::UnitSizeTypes::Large) { - score -= 32; + score -= 48; } } @@ -389,14 +389,6 @@ int MicroRanged::getAttackPriority(BWAPI::Unit rangedUnit, BWAPI::Unit target) } } - // Failing, that, give higher priority to air units hitting tanks. - // Not quite as high a priority as hitting reavers or high templar, though. - if (rangedType.isFlyer() && - (targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode || targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode)) - { - return 10; - } - if (targetType == BWAPI::UnitTypes::Protoss_High_Templar || targetType == BWAPI::UnitTypes::Zerg_Defiler) { @@ -404,7 +396,9 @@ int MicroRanged::getAttackPriority(BWAPI::Unit rangedUnit, BWAPI::Unit target) } if (targetType == BWAPI::UnitTypes::Protoss_Reaver || - targetType == BWAPI::UnitTypes::Protoss_Arbiter) + targetType == BWAPI::UnitTypes::Protoss_Arbiter || + targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode || + targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode) { return 11; } @@ -467,10 +461,9 @@ int MicroRanged::getAttackPriority(BWAPI::Unit rangedUnit, BWAPI::Unit target) return 9; } - // Important combat units that we may not have targeted above (esp. if we're a flyer). - if (targetType == BWAPI::UnitTypes::Protoss_Carrier || - targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode || - targetType == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode) + + // Important combat units that we may not have targeted above. + if (targetType == BWAPI::UnitTypes::Protoss_Carrier) { return 8; } diff --git a/Steamhammer/Source/ParseUtils.cpp b/Steamhammer/Source/ParseUtils.cpp index 151a519..c78005c 100644 --- a/Steamhammer/Source/ParseUtils.cpp +++ b/Steamhammer/Source/ParseUtils.cpp @@ -124,7 +124,8 @@ void ParseUtils::ParseConfigFile(const std::string & filename) JSONTools::ReadBool("DrawMapDistances", debug, Config::Debug::DrawMapDistances); JSONTools::ReadBool("DrawTerrainHeights", debug, Config::Debug::DrawTerrainHeights); JSONTools::ReadBool("DrawBaseInfo", debug, Config::Debug::DrawBaseInfo); - JSONTools::ReadBool("DrawStrategyBossInfo", debug, Config::Debug::DrawStrategyBossInfo); + JSONTools::ReadBool("DrawExpoScores", debug, Config::Debug::DrawExpoScores); + JSONTools::ReadBool("DrawStrategyBossInfo", debug, Config::Debug::DrawStrategyBossInfo); JSONTools::ReadBool("DrawUnitTargets", debug, Config::Debug::DrawUnitTargets); JSONTools::ReadBool("DrawUnitOrders", debug, Config::Debug::DrawUnitOrders); JSONTools::ReadBool("DrawMicroState", debug, Config::Debug::DrawMicroState); @@ -467,7 +468,8 @@ void ParseUtils::ParseTextCommand(const std::string & commandString) else if (variableName == "drawmapdistances") { Config::Debug::DrawMapDistances = GetBoolFromString(val); } else if (variableName == "drawterrainheights") { Config::Debug::DrawTerrainHeights = GetBoolFromString(val); } else if (variableName == "drawbaseinfo") { Config::Debug::DrawBaseInfo = GetBoolFromString(val); } - else if (variableName == "drawstrategybossinfo") { Config::Debug::DrawStrategyBossInfo = GetBoolFromString(val); } + else if (variableName == "drawexposcores") { Config::Debug::DrawExpoScores = GetBoolFromString(val); } + else if (variableName == "drawstrategybossinfo") { Config::Debug::DrawStrategyBossInfo = GetBoolFromString(val); } else if (variableName == "drawsquadinfo") { Config::Debug::DrawSquadInfo = GetBoolFromString(val); } else if (variableName == "drawclusters") { Config::Debug::DrawClusters = GetBoolFromString(val); } else if (variableName == "drawworkerinfo") { Config::Debug::DrawWorkerInfo = GetBoolFromString(val); } diff --git a/Steamhammer/Source/ProductionManager.cpp b/Steamhammer/Source/ProductionManager.cpp index b5f7ff9..51ae9bd 100644 --- a/Steamhammer/Source/ProductionManager.cpp +++ b/Steamhammer/Source/ProductionManager.cpp @@ -249,8 +249,8 @@ void ProductionManager::manageBuildOrderQueue() // if (currentItem.macroAct.isAddon() || currentItem.macroAct.isUpgrade() || currentItem.macroAct.isTech()) if (currentItem.macroAct.isAddon()) { - _goals.push_front(ProductionGoal(currentItem.macroAct)); - _queue.doneWithHighestPriorityItem(); + _goals.push_front(ProductionGoal(currentItem.macroAct)); + _queue.doneWithHighestPriorityItem(); _lastProductionFrame = the.now(); continue; } @@ -847,6 +847,10 @@ void ProductionManager::executeCommand(const MacroAct & act) { StrategyBossZerg::Instance().setNonadaptive(true); } + else if (cmd == MacroCommandType::Lift) + { + liftBuildings(act.getCommandType().getUnitType()); + } else if (cmd == MacroCommandType::QueueBarrier) { // It does nothing! Every command is a queue barrier. @@ -1103,6 +1107,17 @@ void ProductionManager::doExtractorTrick() } } +void ProductionManager::liftBuildings(BWAPI::UnitType type) const +{ + for (BWAPI::Unit u : the.self()->getUnits()) + { + if (u->getType() == type && u->canLift()) + { + (void)the.micro.Lift(u); + } + } +} + void ProductionManager::queueGasSteal() { _queue.queueAsHighestPriority(MacroAct(the.self()->getRace().getRefinery(), MacroLocation::GasSteal), true); diff --git a/Steamhammer/Source/ProductionManager.h b/Steamhammer/Source/ProductionManager.h index 89fc0f0..0b3c00c 100644 --- a/Steamhammer/Source/ProductionManager.h +++ b/Steamhammer/Source/ProductionManager.h @@ -67,6 +67,7 @@ class ProductionManager void onUnitDestroy(BWAPI::Unit unit); void drawProductionInformation(int x, int y); void startExtractorTrick(BWAPI::UnitType type); + void liftBuildings(BWAPI::UnitType type) const; void queueGasSteal(); bool isGasStealInQueue() const; diff --git a/Steamhammer/Source/ScoutBoss.cpp b/Steamhammer/Source/ScoutBoss.cpp deleted file mode 100644 index 579b0e6..0000000 --- a/Steamhammer/Source/ScoutBoss.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "ScoutBoss.h" - -using namespace UAlbertaBot; - -#include "ScoutBoss.h" - -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- diff --git a/Steamhammer/Source/ScoutBoss.h b/Steamhammer/Source/ScoutBoss.h deleted file mode 100644 index e88a10a..0000000 --- a/Steamhammer/Source/ScoutBoss.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -// TODO in progress, still unused - -namespace UAlbertaBot -{ -struct ScoutGoal -{ - BWAPI::Position position; - int priority; -}; - -class ScoutBoss -{ - -private: - std::set check; - std::set monitor; - -public: - - ScoutBoss(); - - void update(); - -}; - -} \ No newline at end of file diff --git a/Steamhammer/Source/StrategyBossZerg.cpp b/Steamhammer/Source/StrategyBossZerg.cpp index 35d9406..8ecca54 100644 --- a/Steamhammer/Source/StrategyBossZerg.cpp +++ b/Steamhammer/Source/StrategyBossZerg.cpp @@ -44,6 +44,7 @@ StrategyBossZerg::StrategyBossZerg() , _recommendEnsnare(0) , _recommendBroodling(0) , _recommendQueens(0) + , _recommendSunkens(false) { resetTechScores(); setUnitMix(BWAPI::UnitTypes::Zerg_Drone, BWAPI::UnitTypes::None); @@ -2096,10 +2097,55 @@ void StrategyBossZerg::checkGroundDefenses(BuildOrderQueue & queue) _emergencyStartFrame = _lastUpdateFrame; } - // _emergencyNow is updated independently from _emergencyGroundDefense. + // _emergencyNow is updated independently of _emergencyGroundDefense. _emergencyNow = enemyPowerInOurFace > ourPower; } +// Possibly add static defenses at various bases. +bool StrategyBossZerg::buildStaticDefense() +{ + if (!_recommendSunkens) + { + return false; + } + + if (nDrones <= 10 || + the.my.all.count(BWAPI::UnitTypes::Zerg_Creep_Colony) > 0 || + the.my.all.count(BWAPI::UnitTypes::Zerg_Sunken_Colony) > the.my.completed.count(BWAPI::UnitTypes::Zerg_Sunken_Colony) || + isBeingBuilt(BWAPI::UnitTypes::Zerg_Creep_Colony)) + { + return false; + } + + const bool expectDrop = + the.your.seen.count(BWAPI::UnitTypes::Terran_Dropship) > 0 || + the.your.seen.count(BWAPI::UnitTypes::Protoss_Shuttle) > 0; + + for (Base * base : the.bases.getAll()) + { + if (base->getOwner() == the.self() && + base->getDepot() && + base->getDepot()->isCompleted() && + (expectDrop || !base->getNatural() || base->getNatural()->getOwner() != the.self())) + { + BWAPI::Unitset sunks = BWAPI::Broodwar->getUnitsInRadius( + base->getCenter(), + 7 * 32, + BWAPI::Filter::GetType == BWAPI::UnitTypes::Zerg_Sunken_Colony && BWAPI::Filter::GetPlayer == the.self() + ); + if (sunks.empty()) + { + // Post directly to building manager so we can control the position. + MacroAct act(BWAPI::UnitTypes::Zerg_Creep_Colony); + BuildingManager::Instance().addBuildingTask(act, base->getTilePosition(), nullptr, false); + return true; + } + } + } + + return false; +} + // If the enemy expanded or made static defense, we can spawn extra drones. // Also try to compensate if we made sunkens. // Exception: Static defense near our base is a proxy. @@ -2327,6 +2373,10 @@ void StrategyBossZerg::recommendTech() (the.your.seen.count(BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode) + the.your.seen.count(BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode) + 1) / 3; + + _recommendSunkens = + the.your.ever.count(BWAPI::UnitTypes::Terran_Vulture) > 0 || // assume vultures will keep coming + the.your.seen.count(BWAPI::UnitTypes::Terran_Dropship) > 0; // assume dropships may give up } else if (_enemyRace == BWAPI::Races::Protoss) { @@ -2346,6 +2396,10 @@ void StrategyBossZerg::recommendTech() ? 1 : 0; _recommendBroodling = (the.your.seen.count(BWAPI::UnitTypes::Protoss_High_Templar) + 1) / 3; + + _recommendSunkens = + the.your.seen.count(BWAPI::UnitTypes::Protoss_Dark_Templar) > 0 || + the.your.seen.count(BWAPI::UnitTypes::Protoss_Shuttle) > 0 && the.your.seen.count(BWAPI::UnitTypes::Protoss_Reaver) > 0; } else { @@ -2361,8 +2415,11 @@ void StrategyBossZerg::recommendTech() the.your.seen.count(BWAPI::UnitTypes::Zerg_Lurker) / 6 + the.your.seen.count(BWAPI::UnitTypes::Zerg_Defiler) / 2 + the.your.seen.count(BWAPI::UnitTypes::Zerg_Ultralisk) / 2; + + _recommendSunkens = false; } + // Queen counts. // Don't recommend high numbers unless we've already started the research. if (!_self->hasResearched(BWAPI::TechTypes::Ensnare) && !_self->isResearching(BWAPI::TechTypes::Ensnare)) { @@ -2381,7 +2438,7 @@ void StrategyBossZerg::recommendTech() void StrategyBossZerg::vProtossTechScores(const PlayerSnapshot & snap) { - _wantAirArmor = snap.count(BWAPI::UnitTypes::Protoss_Corsair) >= 5; + _wantAirArmor = snap.count(BWAPI::UnitTypes::Protoss_Corsair) >= 4; // Bias. techScores[int(TechUnit::Hydralisks)] = 11; @@ -2522,7 +2579,7 @@ void StrategyBossZerg::vProtossTechScores(const PlayerSnapshot & snap) // Decide what units counter the terran unit mix. void StrategyBossZerg::vTerranTechScores(const PlayerSnapshot & snap) { - _wantAirArmor = snap.count(BWAPI::UnitTypes::Terran_Valkyrie) >= 4; + _wantAirArmor = snap.count(BWAPI::UnitTypes::Terran_Valkyrie) >= 3; // Bias. techScores[int(TechUnit::Mutalisks)] = 11; // default lair tech @@ -3580,6 +3637,7 @@ void StrategyBossZerg::produceOtherStuff(int & mineralsLeft, int & gasLeft, bool nGas >= 3 && (nDrones >= 60 || nDrones >= 30 && nQueens >= 4) && !_emergencyGroundDefense && !_emergencyNow && enoughArmy()) { + // NOTE We only get here if ensnare or broodling is already researched. produce(BWAPI::UpgradeTypes::Gamete_Meiosis); mineralsLeft -= 150; gasLeft -= 150; @@ -4183,18 +4241,6 @@ BuildOrder & StrategyBossZerg::freshProductionPlan() updateGameState(); - // Special adaptations to specific opponent plans. - if (adaptToEnemyOpeningPlan()) - { - return _latestBuildOrder; - } - - // We always want at least 9 drones and a spawning pool. - if (rebuildCriticalLosses()) - { - return _latestBuildOrder; - } - // If we have enough idle drones, might as well put them to work gathering gas. if (!WorkerManager::Instance().isCollectingGas() && WorkerManager::Instance().getNumIdleWorkers() >= 3 * nGas) @@ -4202,7 +4248,25 @@ BuildOrder & StrategyBossZerg::freshProductionPlan() produce(MacroCommandType::StartGas); } - // Set the tech target and unit mix. + // Special adaptations to specific opponent plans. + if (adaptToEnemyOpeningPlan()) + { + return _latestBuildOrder; + } + + // We always want at least 9 drones and a spawning pool. + if (rebuildCriticalLosses()) + { + return _latestBuildOrder; + } + + // Possibly add ststic defense at many or all bases. + if (buildStaticDefense()) + { + return _latestBuildOrder; + } + + // Set the tech target and unit mix. chooseStrategy(); // If we're making gas units, short on gas, and not gathering gas, fix that first. diff --git a/Steamhammer/Source/StrategyBossZerg.h b/Steamhammer/Source/StrategyBossZerg.h index 6a47f69..dc24660 100644 --- a/Steamhammer/Source/StrategyBossZerg.h +++ b/Steamhammer/Source/StrategyBossZerg.h @@ -141,6 +141,7 @@ class StrategyBossZerg int _recommendEnsnare; int _recommendBroodling; int _recommendQueens; // max of the above 3 + int _recommendSunkens; // at each base // For choosing the tech target and the unit mix. std::array techScores; @@ -181,6 +182,7 @@ class StrategyBossZerg bool adaptToEnemyOpeningPlan(); bool rebuildCriticalLosses(); + bool buildStaticDefense(); void checkGroundDefenses(BuildOrderQueue & queue); void analyzeExtraDrones(); diff --git a/Steamhammer/Source/StrategyManager.cpp b/Steamhammer/Source/StrategyManager.cpp index 5d87b6f..6ff880d 100644 --- a/Steamhammer/Source/StrategyManager.cpp +++ b/Steamhammer/Source/StrategyManager.cpp @@ -439,7 +439,8 @@ goal.push_back(std::pair(BWAPI::UpgradeTypes::Terran_Infantry_Wea // The rush is over, transition out on the next call. _openingGroup = "tanks"; } - } + ProductionManager::Instance().liftBuildings(BWAPI::UnitTypes::Terran_Barracks); + } else if (_openingGroup == "tanks") { goal.push_back(std::pair(BWAPI::UnitTypes::Terran_Vulture, numVultures + 4)); @@ -462,6 +463,11 @@ goal.push_back(std::pair(BWAPI::UpgradeTypes::Terran_Infantry_Wea { makeVessel = true; } + ProductionManager::Instance().liftBuildings(BWAPI::UnitTypes::Terran_Barracks); + if (hasEBay) + { + ProductionManager::Instance().liftBuildings(BWAPI::UnitTypes::Terran_Engineering_Bay); + } } else if (_openingGroup == "drop") { diff --git a/Steamhammer/Source/WorkerData.cpp b/Steamhammer/Source/WorkerData.cpp index 1111bfb..529673f 100644 --- a/Steamhammer/Source/WorkerData.cpp +++ b/Steamhammer/Source/WorkerData.cpp @@ -271,6 +271,19 @@ int WorkerData::getNumCombatWorkers() const return num; } +int WorkerData::getNumRepairWorkers() const +{ + size_t num = 0; + for (BWAPI::Unit unit : workers) + { + if (workerJobMap.at(unit) == WorkerData::Repair) + { + num++; + } + } + return num; +} + int WorkerData::getNumIdleWorkers() const { size_t num = 0; diff --git a/Steamhammer/Source/WorkerData.h b/Steamhammer/Source/WorkerData.h index c3b2942..5935a87 100644 --- a/Steamhammer/Source/WorkerData.h +++ b/Steamhammer/Source/WorkerData.h @@ -64,7 +64,8 @@ enum WorkerJob int getNumGasWorkers() const; int getNumReturnCargoWorkers() const; int getNumCombatWorkers() const; - int getNumIdleWorkers() const; + int getNumRepairWorkers() const; + int getNumIdleWorkers() const; bool anyUnblocker() const; void getMineralWorkers(std::set & mw); diff --git a/Steamhammer/Source/WorkerManager.cpp b/Steamhammer/Source/WorkerManager.cpp index ac4e55a..91f5936 100644 --- a/Steamhammer/Source/WorkerManager.cpp +++ b/Steamhammer/Source/WorkerManager.cpp @@ -114,7 +114,7 @@ void WorkerManager::updateWorkerStatus() // Idleness. // Order can be PlayerGuard for a drone that tries to build and fails. - // There are other causes. + // There are other causes, e.g. being done with a job like repair. if ((worker->isIdle() || worker->getOrder() == BWAPI::Orders::PlayerGuard) && job != WorkerData::Minerals && job != WorkerData::Build && @@ -387,18 +387,49 @@ void WorkerManager::handleReturnCargoWorkers() // Terran can assign SCVs to repair. void WorkerManager::handleRepairWorkers() { - if (BWAPI::Broodwar->self()->getRace() != BWAPI::Races::Terran) + if (BWAPI::Broodwar->self()->getRace() != BWAPI::Races::Terran || + the.my.completed.count(BWAPI::UnitTypes::Terran_SCV) == 0 || + the.self()->minerals() == 0) { return; } + size_t maxRepairers = + 1 + the.my.completed.count(BWAPI::UnitTypes::Terran_SCV) / 6; + int nAlreadyRepairing = workerData.getNumRepairWorkers(); + if (size_t(nAlreadyRepairing) >= maxRepairers) + { + return; + } + maxRepairers -= nAlreadyRepairing; + for (BWAPI::Unit unit : BWAPI::Broodwar->self()->getUnits()) { - if (unit->getType().isBuilding() && (unit->getHitPoints() < unit->getType().maxHitPoints())) + if (unit->getType().isBuilding() && + unit->getHitPoints() < unit->getType().maxHitPoints() && + unit->isCompleted()) { - BWAPI::Unit repairWorker = getClosestMineralWorkerTo(unit); - setRepairWorker(repairWorker, unit); - break; + size_t nRepairers = 0; + if (unit->getType() == BWAPI::UnitTypes::Terran_Bunker || + unit->getType() == BWAPI::UnitTypes::Terran_Missile_Turret) + { + nRepairers = std::max(size_t(1), the.info.getEnemyFireteam(unit).size()); + } + else if (unit->getHitPoints() < unit->getType().maxHitPoints() / 2) + { + nRepairers = 1; + } + + for (size_t i = nRepairers; i > 0; --i) + { + BWAPI::Unit repairWorker = getClosestMineralWorkerTo(unit); + setRepairWorker(repairWorker, unit); + maxRepairers -= 1; + if (maxRepairers == 0) + { + return; + } + } } } } @@ -439,7 +470,8 @@ void WorkerManager::handleUnblockWorkers() } else { - BWAPI::Unitset patches = BWAPI::Broodwar->getUnitsOnTile(tile); + BWAPI::Unitset patches = + BWAPI::Broodwar->getUnitsOnTile(tile, BWAPI::Filter::IsMineralField); if (patches.empty() || the.groundAttacks.at(tile) > 0) { //BWAPI::Broodwar->printf("releasing unblock worker"); diff --git a/Steamhammer/VisualStudio/UAlbertaBot.vcxproj b/Steamhammer/VisualStudio/UAlbertaBot.vcxproj index e05ad14..6c23fe5 100644 --- a/Steamhammer/VisualStudio/UAlbertaBot.vcxproj +++ b/Steamhammer/VisualStudio/UAlbertaBot.vcxproj @@ -147,6 +147,7 @@ + @@ -176,7 +177,6 @@ - @@ -257,7 +257,6 @@ - diff --git a/Steamhammer/VisualStudio/UAlbertaBot.vcxproj.filters b/Steamhammer/VisualStudio/UAlbertaBot.vcxproj.filters index 60a29fc..b4c7ef3 100644 --- a/Steamhammer/VisualStudio/UAlbertaBot.vcxproj.filters +++ b/Steamhammer/VisualStudio/UAlbertaBot.vcxproj.filters @@ -171,11 +171,11 @@ - + @@ -315,7 +315,6 @@ -