suvorov-bot
suvorov-bot copied to clipboard
Fix gas saturation
We constantly have too many workers (e.g. 5 instead of 3) gathering the same refinery.
@alkurbatov It looks like there is currently no way I can iterate through the "Cache<Worker> m_busy_workers". I'd like to iterate through the m_busy_workers to find a vespene gatherer for a specific Refinery. (to avoid a call to GetUnits which iterates through the whole unit_pool)
void Hub::RemoveVespeneHarvester(const sc2::Unit& refinery_) {
auto it = std::find_if(m_busy_workers.begin(), m_busy_workers.end(),
[refinery_](const Worker& worker_)->const sc2::Unit* {
const sc2::Unit* unit = gAPI->observer().GetUnit(worker_.Tag());
if (unit && !unit->orders.empty() &&
unit->orders.front().target_unit_tag == refinery_.tag &&
unit->last_seen_game_loop == gAPI->observer().GetGameLoop()) {
return unit;
}
return nullptr;
});
...
Should a begin() and end() be added to Cache for iteration purposes? Or should Cache be able to take a lambda and return the first valid object?
My current fix relies on PR #50 , so I believe I have to wait til that's resolved to make a PR for this one.
But, here's the relevant new code:
const sc2::Unit* Hub::RemoveVespeneHarvester(const sc2::Unit& refinery_) {
Units workers = gAPI->observer().GetUnits(sc2::IsUnit(m_current_worker_type));
auto vespener = std::find_if(workers().begin(), workers().end(),
[refinery_](const sc2::Unit* unit_)->const sc2::Unit* {
if (!unit_->orders.empty() &&
unit_->orders.front().target_unit_tag == refinery_.tag &&
unit_->last_seen_game_loop == gAPI->observer().GetGameLoop()) {
return unit_;
}
return nullptr;
});
if (!*vespener)
return nullptr;
return *vespener;
}
// this is in Miner.cpp
void SecureVespeneIncome() {
auto refineries = gAPI->observer().GetUnits(IsRefinery());
auto town_halls = gAPI->observer().GetUnits(sc2::IsTownHall());
for (const auto& i : refineries()) {
if (i->assigned_harvesters > 3) { // gas ideal_harvesters is always 3
const sc2::Unit* removed = gHub->RemoveVespeneHarvester(*i);
DistrubuteMineralWorker(removed);
continue;
}
if (i->assigned_harvesters == 3) // gas already saturated
continue;
auto close_town_hall = town_halls.GetClosestUnit(i->pos);
if (close_town_hall &&
close_town_hall->assigned_harvesters > close_town_hall->ideal_harvesters) {
gHub->AssignVespeneHarvester(*i); //reassign overflow mineral workers
continue;
}
gHub->AssignVespeneHarvester(*i);
}
}
What do you think about keeping list of assigned gatherers in each geyser object? Although I am not sure that such precise estimation is needed. The simplest option in the current design is to allow retrieval of a const reference to the busy workers list.
Yes, I do think keeping a list of assigned gatherers per geyser is good.
(also a list per mineral-patch will be needed eventually, in order to maintain 2 workers on close-mineral-patches, since sometimes they stray on auto-gather).
Since you prefer lambdas, I changed RemoveVespeneHarvester to:
const sc2::Unit* Hub::RemoveVespeneHarvester(const sc2::Unit& refinery_) {
const std::list<Worker>& workers = m_busy_workers();
auto vespener = std::find_if(workers.begin(), workers.end(),
[refinery_](const Worker& worker_) {
const sc2::Unit* unit = gAPI->observer().GetUnit(worker_.Tag());
return !unit->orders.empty() &&
unit->orders.front().target_unit_tag == refinery_.tag &&
unit->last_seen_game_loop == gAPI->observer().GetGameLoop();
});
if (vespener == workers.end())
return nullptr;
m_busy_workers.Swap(*vespener, m_free_workers);
return gAPI->observer().GetUnit((*vespener).Tag());
}
If you want this function (and AssignVespeneHarvester) moved from Hub to Miner, I'll have to add a Hub::GetBusyWorkers that returns a const list reference.