From b4ee0a72d965c57c2b4e206fbe2cbee54ef1903f Mon Sep 17 00:00:00 2001 From: Michael Holtan Date: Mon, 1 Mar 2021 23:48:09 -0600 Subject: [PATCH] Fix Issue #35 "Improve mineral saturation" This will send new/idle workers to a non-saturated townhall. Townhalls will build an extra worker over Ideal and it will go to a new non-saturated townhall upon creation, so each townhall will continually build workers if there are any townhalls that are not yet saturated (until 80 total). After constructing a building, workers will go to an unsaturated townhall instead of the closest/starting one. --- src/Hub.cpp | 132 +++++++++++++++++++++------ src/Hub.h | 11 ++- src/blueprints/TownHall.cpp | 9 +- src/core/Map.cpp | 6 +- src/core/Map.h | 11 ++- src/plugins/Miner.cpp | 19 ++-- src/strategies/terran/MarinePush.cpp | 15 +-- 7 files changed, 144 insertions(+), 59 deletions(-) diff --git a/src/Hub.cpp b/src/Hub.cpp index f8e5d1d..5d47125 100644 --- a/src/Hub.cpp +++ b/src/Hub.cpp @@ -38,7 +38,8 @@ bool SortByDistance::operator()(const Expansion& lhs_, const Expansion& rhs_) co Hub::Hub(sc2::Race current_race_, const Expansions& expansions_): m_current_race(current_race_), m_expansions(expansions_), m_current_supply_type(sc2::UNIT_TYPEID::INVALID), - m_current_worker_type(sc2::UNIT_TYPEID::INVALID) { + m_current_worker_type(sc2::UNIT_TYPEID::INVALID), + m_current_townhall_type(sc2::UNIT_TYPEID::INVALID) { std::sort(m_expansions.begin(), m_expansions.end(), SortByDistance(gAPI->observer().StartingLocation())); @@ -46,16 +47,19 @@ Hub::Hub(sc2::Race current_race_, const Expansions& expansions_): case sc2::Race::Protoss: m_current_supply_type = sc2::UNIT_TYPEID::PROTOSS_PYLON; m_current_worker_type = sc2::UNIT_TYPEID::PROTOSS_PROBE; + m_current_townhall_type = sc2::UNIT_TYPEID::PROTOSS_NEXUS; return; case sc2::Race::Terran: m_current_supply_type = sc2::UNIT_TYPEID::TERRAN_SUPPLYDEPOT; m_current_worker_type = sc2::UNIT_TYPEID::TERRAN_SCV; + m_current_townhall_type = sc2::UNIT_TYPEID::TERRAN_COMMANDCENTER; return; case sc2::Race::Zerg: m_current_supply_type = sc2::UNIT_TYPEID::ZERG_OVERLORD; m_current_worker_type = sc2::UNIT_TYPEID::ZERG_DRONE; + m_current_townhall_type = sc2::UNIT_TYPEID::ZERG_HATCHERY; return; default: @@ -98,17 +102,9 @@ void Hub::OnUnitCreated(const sc2::Unit& unit_) { case sc2::UNIT_TYPEID::PROTOSS_NEXUS: case sc2::UNIT_TYPEID::TERRAN_COMMANDCENTER: case sc2::UNIT_TYPEID::ZERG_HATCHERY: - for (auto& i : m_expansions) { - if (std::floor(i.town_hall_location.x) != std::floor(unit_.pos.x) || - std::floor(i.town_hall_location.y) != std::floor(unit_.pos.y)) - continue; - - i.owner = Owner::SELF; - gHistory.info() << "Capture region: (" << - unit_.pos.x << ", " << unit_.pos.y << - ")" << std::endl; - return; - } + UpdateExpansionOwner(unit_); + gHistory.info() << "Capture region: (" << unit_.pos.x << + ", " << unit_.pos.y << ")" << std::endl; return; default: @@ -127,6 +123,21 @@ void Hub::OnUnitDestroyed(const sc2::Unit& unit_) { } m_free_workers.Remove(Worker(unit_)); + + for (auto& i : m_expansions) { + // check if enroute to build TownHall + if (i.owner == Owner::CONTESTED && i.worker_tag == unit_.tag) { + i.owner = Owner::NEUTRAL; + i.worker_tag = sc2::NullTag; + } + + // check if in-progress constructing TownHall + if (i.owner == Owner::SELF && i.worker_tag == unit_.tag) { + // NOTE (impulsecloud): decide whether to cancel or send new worker + // possibly with a military escort + } + } + return; } @@ -152,17 +163,9 @@ void Hub::OnUnitDestroyed(const sc2::Unit& unit_) { case sc2::UNIT_TYPEID::ZERG_HATCHERY: case sc2::UNIT_TYPEID::ZERG_HIVE: case sc2::UNIT_TYPEID::ZERG_LAIR: - for (auto& i : m_expansions) { - if (std::floor(i.town_hall_location.x) != std::floor(unit_.pos.x) || - std::floor(i.town_hall_location.y) != std::floor(unit_.pos.y)) - continue; - - i.owner = Owner::NEUTRAL; - gHistory.info() << "Lost region: (" << - unit_.pos.x << ", " << unit_.pos.y << - ")" << std::endl; - return; - } + UpdateExpansionOwner(unit_); + gHistory.info() << "Lost region: (" << unit_.pos.x + << ", " << unit_.pos.y << ")" << std::endl; return; default: @@ -175,7 +178,7 @@ void Hub::OnUnitIdle(const sc2::Unit& unit_) { case sc2::UNIT_TYPEID::PROTOSS_PROBE: case sc2::UNIT_TYPEID::TERRAN_SCV: case sc2::UNIT_TYPEID::ZERG_DRONE: { - if (m_free_workers.Swap(Worker(unit_), m_busy_workers)) + if (m_busy_workers.Swap(Worker(unit_), m_free_workers)) gHistory.info() << "Our busy worker has finished task" << std::endl; return; @@ -233,6 +236,10 @@ sc2::UNIT_TYPEID Hub::GetCurrentWorkerType() const { return m_current_worker_type; } +sc2::UNIT_TYPEID Hub::GetCurrentTownHallType() const { + return m_current_townhall_type; +} + bool Hub::AssignRefineryConstruction(Order* order_, const sc2::Unit* geyser_) { Worker* worker = GetClosestFreeWorker(geyser_->pos); if (!worker) @@ -243,13 +250,13 @@ bool Hub::AssignRefineryConstruction(Order* order_, const sc2::Unit* geyser_) { return true; } -bool Hub::AssignBuildTask(Order* order_, const sc2::Point2D& point_) { +sc2::Tag Hub::AssignBuildTask(Order* order_, const sc2::Point2D& point_) { Worker* worker = GetClosestFreeWorker(point_); if (!worker) - return false; + return sc2::NullTag; worker->Build(order_, point_); - return true; + return worker->Tag(); } void Hub::AssignVespeneHarvester(const sc2::Unit& refinery_) { @@ -260,6 +267,38 @@ void Hub::AssignVespeneHarvester(const sc2::Unit& refinery_) { worker->GatherVespene(refinery_); } +void Hub::DistrubuteMineralWorker(const sc2::Unit* unit_) { + if (!unit_) + return; + + sc2::Point2D worker_loc = unit_->pos; + sc2::Point2D target_loc = gAPI->observer().StartingLocation(); + float target_dist = std::numeric_limits::max(); + + for (auto& i : m_expansions) { + if (i.owner != Owner::SELF) + continue; + + const sc2::Unit* th = gAPI->observer().GetUnit(i.town_hall_tag); + float dist = DistanceSquared2D(worker_loc, th->pos); + + if (dist < target_dist && th->build_progress == 1.0f + && th->assigned_harvesters < th->ideal_harvesters) { + target_dist = dist; + target_loc = th->pos; + } + } + + auto patches = gAPI->observer().GetUnits( + sc2::IsVisibleMineralPatch(), sc2::Unit::Alliance::Neutral); + const sc2::Unit* mineral_target = patches.GetClosestUnit(target_loc); + + if (!mineral_target) + return; + + gAPI->action().Cast(*unit_, sc2::ABILITY_ID::SMART, *mineral_target); +} + bool Hub::AssignLarva(Order* order_) { if (m_larva.Empty()) return false; @@ -279,7 +318,7 @@ const Expansions& Hub::GetExpansions() const { return m_expansions; } -const sc2::Point3D* Hub::GetNextExpansion() { +Expansion* Hub::GetNextExpansion() { auto it = std::find_if(m_expansions.begin(), m_expansions.end(), [](const Expansion& expansion_) { return expansion_.owner == Owner::NEUTRAL; @@ -289,7 +328,42 @@ const sc2::Point3D* Hub::GetNextExpansion() { return nullptr; it->owner = Owner::CONTESTED; - return &(it->town_hall_location); + return &(*it); +} + +void Hub::UpdateExpansionOwner(const sc2::Unit& unit_) { + for (auto& i : m_expansions) { + if (std::floor(i.town_hall_location.x) != std::floor(unit_.pos.x) || + std::floor(i.town_hall_location.y) != std::floor(unit_.pos.y)) + continue; + + if (!unit_.is_alive) { + i.town_hall_tag = sc2::NullTag; + i.worker_tag = sc2::NullTag; + i.owner = Owner::NEUTRAL; + return; + } + + i.town_hall_tag = unit_.tag; + + switch (unit_.alliance) { + case sc2::Unit::Alliance::Self: { + // NOTE (impulsecloud): should be transitioning from CONTESTED. + // worker_tag should already be set, and still valid + i.owner = Owner::SELF; + return; + } + + case sc2::Unit::Alliance::Enemy: { + i.owner = Owner::ENEMY; + i.worker_tag = sc2::NullTag; + return; + } + + default: + return; + } + } } std::unique_ptr gHub; diff --git a/src/Hub.h b/src/Hub.h index 97a4838..524e8b0 100644 --- a/src/Hub.h +++ b/src/Hub.h @@ -137,25 +137,32 @@ struct Hub { sc2::UNIT_TYPEID GetCurrentWorkerType() const; + sc2::UNIT_TYPEID GetCurrentTownHallType() const; + bool AssignRefineryConstruction(Order* order_, const sc2::Unit* geyser_); - bool AssignBuildTask(Order* order_, const sc2::Point2D& point_); + sc2::Tag AssignBuildTask(Order* order_, const sc2::Point2D& point_); void AssignVespeneHarvester(const sc2::Unit& refinery_); + void DistrubuteMineralWorker(const sc2::Unit* unit_); + bool AssignLarva(Order* order_); const Cache& GetLarvas() const; const Expansions& GetExpansions() const; - const sc2::Point3D* GetNextExpansion(); + Expansion* GetNextExpansion(); + + void UpdateExpansionOwner(const sc2::Unit& unit_); private: sc2::Race m_current_race; Expansions m_expansions; sc2::UNIT_TYPEID m_current_supply_type; sc2::UNIT_TYPEID m_current_worker_type; + sc2::UNIT_TYPEID m_current_townhall_type; Cache m_captured_geysers; diff --git a/src/blueprints/TownHall.cpp b/src/blueprints/TownHall.cpp index acf70fb..2872338 100644 --- a/src/blueprints/TownHall.cpp +++ b/src/blueprints/TownHall.cpp @@ -7,9 +7,12 @@ #include "core/API.h" bool TownHall::Build(Order* order_) { - const sc2::Point3D* town_hall_location = gHub->GetNextExpansion(); - if (!town_hall_location) + Expansion* next_expand = gHub->GetNextExpansion(); + if (!next_expand) return false; - return gHub->AssignBuildTask(order_, *town_hall_location); + sc2::Tag builder = gHub->AssignBuildTask(order_, next_expand->town_hall_location); + next_expand->worker_tag = builder; + + return next_expand->worker_tag; } diff --git a/src/core/Map.cpp b/src/core/Map.cpp index df26fd5..a7edf79 100644 --- a/src/core/Map.cpp +++ b/src/core/Map.cpp @@ -94,7 +94,8 @@ sc2::Point3D Cluster::Center() const { } // namespace Expansion::Expansion(const sc2::Point3D& town_hall_location_): - town_hall_location(town_hall_location_), owner(Owner::NEUTRAL) { + town_hall_location(town_hall_location_), owner(Owner::NEUTRAL), + town_hall_tag(sc2::NullTag), worker_tag(sc2::NullTag){ } Expansions CalculateExpansionLocations() { @@ -151,6 +152,9 @@ Expansions CalculateExpansionLocations() { start_index += query_size[i.id]; } + + // Include start location. TownHall tag will be added during its OnCreated event. + expansions.emplace_back(gAPI->observer().StartingLocation()); return expansions; } diff --git a/src/core/Map.h b/src/core/Map.h index 54b7500..8cefc62 100644 --- a/src/core/Map.h +++ b/src/core/Map.h @@ -10,10 +10,10 @@ #include enum Owner { - NEUTRAL = 0, - CONTESTED = 1, - ENEMY = 2, - SELF = 3, + NEUTRAL = 0, // No Townhalls at expansion, no workers enroute + CONTESTED = 1, // Self Worker enroute to build TownHall at expansion + ENEMY = 2, // Enemy TownHall at expansion + SELF = 3, // Self TownHall at expansion, possibly under construction }; struct Expansion { @@ -21,6 +21,9 @@ struct Expansion { sc2::Point3D town_hall_location; Owner owner; + sc2::Tag town_hall_tag; // valid for Owner::SELF or ENEMY + sc2::Tag worker_tag; // valid for Owner::CONTESTED or SELF + // NOTE (impulsecloud): check for dead builder, send new }; typedef std::vector Expansions; diff --git a/src/plugins/Miner.cpp b/src/plugins/Miner.cpp index 959e95c..cfe3d40 100644 --- a/src/plugins/Miner.cpp +++ b/src/plugins/Miner.cpp @@ -18,13 +18,14 @@ const int mule_energy_cost = 50; void SecureMineralsIncome(Builder* builder_) { auto town_halls = gAPI->observer().GetUnits(sc2::IsTownHall()); + auto numTotalWorkers = gAPI->observer().CountUnitType(gHub->GetCurrentWorkerType()); for (const auto& i : town_halls()) { - if (i->assigned_harvesters >= i->ideal_harvesters) - continue; - - if (!i->orders.empty()) + if (!i->orders.empty() || i->build_progress != 1.0f) continue; + + if (i->assigned_harvesters >= i->ideal_harvesters + 1 || numTotalWorkers > 80) + continue; // NOTE (impulsecloud): one extra, never >80 (pros stop at 77-ish?) if (builder_->CountScheduledOrders(gHub->GetCurrentWorkerType()) > 0) continue; @@ -98,19 +99,11 @@ void Miner::OnUnitCreated(const sc2::Unit* unit_, Builder*) { } void Miner::OnUnitIdle(const sc2::Unit* unit_, Builder*) { - auto units = gAPI->observer().GetUnits(sc2::IsVisibleMineralPatch(), - sc2::Unit::Alliance::Neutral); - switch (unit_->unit_type.ToType()) { case sc2::UNIT_TYPEID::PROTOSS_PROBE: case sc2::UNIT_TYPEID::TERRAN_SCV: case sc2::UNIT_TYPEID::ZERG_DRONE: { - const sc2::Unit* mineral_target = units.GetClosestUnit( - gAPI->observer().StartingLocation()); - if (!mineral_target) - return; - - gAPI->action().Cast(*unit_, sc2::ABILITY_ID::SMART, *mineral_target); + gHub->DistrubuteMineralWorker(unit_); break; } diff --git a/src/strategies/terran/MarinePush.cpp b/src/strategies/terran/MarinePush.cpp index 4eeb64b..c03677d 100644 --- a/src/strategies/terran/MarinePush.cpp +++ b/src/strategies/terran/MarinePush.cpp @@ -15,19 +15,20 @@ MarinePush::MarinePush(): Strategy(16.0f) { void MarinePush::OnGameStart(Builder* builder_) { builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_SUPPLYDEPOT); builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); - builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_COMMANDCENTER); builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_REFINERY); builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_ORBITALCOMMAND); builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); - builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKSTECHLAB); + builder_->ScheduleOptionalOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKSTECHLAB); builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); builder_->ScheduleObligatoryOrder(sc2::UPGRADE_ID::SHIELDWALL); builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); - builder_->ScheduleOptionalOrder(sc2::UPGRADE_ID::STIMPACK); - builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKSREACTOR); - builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKSREACTOR); - builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKSREACTOR); - builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKSREACTOR); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_COMMANDCENTER); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); + builder_->ScheduleObligatoryOrder(sc2::UNIT_TYPEID::TERRAN_BARRACKS); } void MarinePush::OnUnitIdle(const sc2::Unit* unit_, Builder* builder_) {