suvorov-bot icon indicating copy to clipboard operation
suvorov-bot copied to clipboard

Fix gas saturation

Open alkurbatov opened this issue 5 years ago • 5 comments

We constantly have too many workers (e.g. 5 instead of 3) gathering the same refinery.

alkurbatov avatar May 15 '20 07:05 alkurbatov

@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?

ImpulseCloud avatar Mar 04 '21 21:03 ImpulseCloud

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);
    }
}

ImpulseCloud avatar Mar 04 '21 23:03 ImpulseCloud

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.

alkurbatov avatar Mar 05 '21 07:03 alkurbatov

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).

ImpulseCloud avatar Mar 05 '21 17:03 ImpulseCloud

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.

ImpulseCloud avatar Mar 16 '21 20:03 ImpulseCloud