Skip to content

Commit

Permalink
Fix Issue #35 "Improve mineral saturation"
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ImpulseCloud committed Mar 4, 2021
1 parent edae1f5 commit b4ee0a7
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 59 deletions.
132 changes: 103 additions & 29 deletions src/Hub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,28 @@ 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()));

switch (m_current_race) {
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:
Expand Down Expand Up @@ -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:
Expand All @@ -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;
}

Expand All @@ -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:
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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_) {
Expand All @@ -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<float>::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;
Expand All @@ -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;
Expand All @@ -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<Hub> gHub;
11 changes: 9 additions & 2 deletions src/Hub.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<GameObject>& 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<Geyser> m_captured_geysers;

Expand Down
9 changes: 6 additions & 3 deletions src/blueprints/TownHall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 5 additions & 1 deletion src/core/Map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
}
11 changes: 7 additions & 4 deletions src/core/Map.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@
#include <vector>

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 {
explicit Expansion(const sc2::Point3D& town_hall_location_);

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<Expansion> Expansions;
Expand Down
19 changes: 6 additions & 13 deletions src/plugins/Miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
15 changes: 8 additions & 7 deletions src/strategies/terran/MarinePush.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_) {
Expand Down

0 comments on commit b4ee0a7

Please sign in to comment.