// still has bug in it somewhere, use Old version void RangedManager::assignTargetsNew(const BWAPI::Unitset & targets) { const BWAPI::Unitset & rangedUnits = getUnits(); // figure out targets BWAPI::Unitset rangedUnitTargets; std::copy_if(targets.begin(), targets.end(), std::inserter(rangedUnitTargets, rangedUnitTargets.end()), [](BWAPI::Unit u){ return u->isVisible(); }); BWAPI::Unitset rangedUnitsToAssign(rangedUnits); std::map<BWAPI::Unit, int> attackersAssigned; for (auto & unit : rangedUnitTargets) { attackersAssigned[unit] = 0; } // keep assigning targets while we have attackers and targets remaining while (!rangedUnitsToAssign.empty() && !rangedUnitTargets.empty()) { auto attackerAssignment = findClosestUnitPair(rangedUnitsToAssign, rangedUnitTargets); BWAPI::Unit & attacker = attackerAssignment.first; BWAPI::Unit & target = attackerAssignment.second; UAB_ASSERT_WARNING(attacker, "We should have chosen an attacker!"); if (!attacker) { break; } if (!target) { Micro::SmartAttackMove(attacker, order.getPosition()); continue; } if (Config::Micro::KiteWithRangedUnits) { if (attacker->getType() == BWAPI::UnitTypes::Zerg_Mutalisk || attacker->getType() == BWAPI::UnitTypes::Terran_Vulture) { Micro::SmartAttackUnit(attacker, target); } else { //Micro::MutaDanceTarget(attacker, target); } } else { Micro::SmartAttackUnit(attacker, target); } // update the number of units assigned to attack the target we found int & assigned = attackersAssigned[attackerAssignment.second]; assigned++; // if it's a small / fast unit and there's more than 2 things attacking it already, don't assign more if ((target->getType().isWorker() || target->getType() == BWAPI::UnitTypes::Zerg_Zergling) && (assigned > 2)) { rangedUnitTargets.erase(target); } // if it's a building and there's more than 10 things assigned to it already, don't assign more else if (target->getType().isBuilding() && (assigned > 10)) { rangedUnitTargets.erase(target); } rangedUnitsToAssign.erase(attacker); } // if there's no targets left, attack move to the order destination if (rangedUnitTargets.empty()) { for (auto & unit : rangedUnitsToAssign) { if (unit->getDistance(order.getPosition()) > 100) { // move to it Micro::SmartAttackMove(unit, order.getPosition()); } } } }
std::unordered_map<BWAPI::Unit, BWAPI::Unit> RangedManager::assignEnemy(const BWAPI::Unitset &meleeUnits, BWAPI::Unitset & meleeUnitTargets) { std::unordered_map<BWAPI::Unit, BWAPI::Unit> attacker2target; std::vector<PairEdge> edges(meleeUnits.size()*meleeUnitTargets.size()); int top = 0; for (auto &attacker : meleeUnits) { for (auto &target : meleeUnitTargets) { if (!target) continue; edges[top].attacker = attacker; edges[top].target = target; int groundWeaponRange = attacker->getType().groundWeapon().maxRange(); edges[top++].distance = -getRealPriority(attacker, target); } } sort(edges.begin(), edges.end()); sort(edges.begin(), edges.end(), [](PairEdge a, PairEdge b) { return a.target->getID() <b.target->getID(); }); PairEdge dummy; dummy.target = nullptr; edges.push_back(dummy); BWAPI::Unit p = nullptr; int sum = 0; for (auto idx = edges.begin(); idx!=edges.end(); idx++) { if (!idx->target) continue; double Health = (((double)idx->target->getHitPoints() + idx->target->getShields())); if (idx->target->getType().size() == BWAPI::UnitSizeTypes::Small) Health *= 2; if (idx->target->getType().size() == BWAPI::UnitSizeTypes::Medium) Health *= 1.5; if (p != idx->target) { sum = 0; p = idx->target; } else { sum++; //assign at most 7 units attack //BWAPI::Broodwar <<( idx->target->getHitPoints()+idx->target->getShields()) / idx->attacker->getType().groundWeapon().damageAmount() << std::endl; if (sum>std::min(8, 1+(int)(Health / idx->attacker->getType().groundWeapon().damageAmount()))) { idx->attacker = nullptr; if (meleeUnitTargets.find(idx->target) != meleeUnitTargets.end()) meleeUnitTargets.erase(idx->target); } } } int t = 0; for (bool halt=false; halt == false; halt = true) { halt= true; auto tmpRangeStart = edges.begin(); auto maxRangeStart = tmpRangeStart, maxRangeEnd = tmpRangeStart; double tmpsum = 0, tmpres = INT_MIN; for (auto idx = edges.begin(); idx->target != nullptr; idx++) { if (attacker2target.find(idx->attacker) != attacker2target.end()) continue; if (idx->target != (idx + 1)->target) { if (tmpsum > tmpres) { tmpres = tmpsum; maxRangeStart = tmpRangeStart; maxRangeEnd = idx + 1; } tmpsum = 0; tmpRangeStart = idx + 1; } else tmpsum += getRealPriority(idx->attacker, idx->target); } for (auto kdx = maxRangeStart; kdx < maxRangeEnd; kdx++) { if (attacker2target.find(kdx->attacker) != attacker2target.end()) continue; attacker2target[kdx->attacker] = kdx->target; halt = false; } t++; } return attacker2target; }
void RangedManager::assignTargetsOld(const BWAPI::Unitset & targets) { const BWAPI::Unitset & rangedUnits = getUnits(); // figure out targets BWAPI::Unitset rangedUnitTargets; for (auto & target : targets) { // conditions for targeting if (!(target->getType() == BWAPI::UnitTypes::Zerg_Larva) && !(target->getType() == BWAPI::UnitTypes::Zerg_Egg) && !(target->getType() == BWAPI::UnitTypes::Buildings) && (target->isTargetable()) && target->isVisible() && target->getType() != BWAPI::UnitTypes::Resource_Vespene_Geyser) { rangedUnitTargets.insert(target); } } auto attacker2target = assignEnemy(rangedUnits, rangedUnitTargets); BWAPI::Position shome = BWAPI::Position(BWTA::getStartLocation(BWAPI::Broodwar->self())->getPosition()); BWAPI::Unitset _rangedUnits = rangedUnits; auto _rangedUnitTargets = rangedUnitTargets; int maxChoices = rangedUnits.size() / 3 + 1, i = 0; std::map<BWAPI::Unit, std::pair<BWAPI::Unit, BWAPI::Unit> > dodgeUnits; BWAPI::Unitset threatens; for (BWAPI::Unit threatenClosest = NULL, unitClosetEnemy = NULL;;) { if (i++ > maxChoices) break; double dist = BWAPI::Broodwar->self()->getUpgradeLevel(BWAPI::UpgradeTypes::Singularity_Charge)?96:96; double enerange = BWAPI::Broodwar->self()->getUpgradeLevel(BWAPI::UpgradeTypes::Singularity_Charge) ? 80 : 80; for (auto & rangedUnit : _rangedUnits) { for (auto target : _rangedUnitTargets) { if (target->getType().isWorker() || (target->getType().isBuilding())) continue; if (target->getType().groundWeapon().maxRange() <= enerange) { if (rangedUnit->getDistance(target) < dist) { threatenClosest = target; unitClosetEnemy = rangedUnit; dist = rangedUnit->getDistance(target); } } } } if (threatenClosest != NULL) { threatens.insert(threatenClosest); BWAPI::Broodwar->drawCircleMap(threatenClosest->getPosition(), 2, BWAPI::Colors::Red, true); auto vec1 = unitClosetEnemy->getPosition() - threatenClosest->getPosition(); dodgeUnits[unitClosetEnemy] = std::make_pair(unitClosetEnemy, threatenClosest); auto __rangedUnits = rangedUnits; for (auto rangedUnit:__rangedUnits) { if (rangedUnit->getDistance(unitClosetEnemy) > unitClosetEnemy->getDistance(threatenClosest)+64) continue; auto vec2 = rangedUnit->getPosition() - threatenClosest->getPosition(); int judge = (vec1.x*vec2.x + vec1.y*vec2.y)*(vec1.x*vec2.x + vec1.y*vec2.y); if (judge * 6 > (vec1.x*vec1.x + vec1.y*vec1.y)*(vec2.x*vec2.x + vec2.y*vec2.y) * 5) { dodgeUnits[rangedUnit] = std::make_pair(unitClosetEnemy, threatenClosest); _rangedUnits.erase(rangedUnit); } } } else break; } BWAPI::Unitset withoutBuildingsAndWorkers = rangedUnitTargets; auto _withoutBuildingsAndWorkers = withoutBuildingsAndWorkers; for (auto target : _withoutBuildingsAndWorkers) if (target->getType().isBuilding() || target->getType().isWorker()) withoutBuildingsAndWorkers.erase(target); for (auto & rangedUnit : rangedUnits) { // train sub units such as scarabs or interceptors //trainSubUnits(rangedUnit); // if the order is to attack or defend int chokeZealot = 0; for (const auto & zealot : BWAPI::Broodwar->self()->getUnits()) { // trivial case: unit which exists matches the type if (zealot->getType() != BWAPI::UnitTypes::Protoss_Zealot) continue; if (zealot->isCompleted() == false) continue; auto ckpt = BWTA::getNearestChokepoint(zealot->getPosition()); if (zealot->getDistance(rangedUnit->getPosition()) < 300) { if (ckpt && ckpt->getWidth()<256 && ckpt->getCenter().getDistance(zealot->getPosition()) < 128) chokeZealot += 3; else chokeZealot += 1; } } bool danceMode; if (order.getType() == SquadOrderTypes::Defend) danceMode = chokeZealot < 3 && rangedUnits.size()<20; else danceMode = chokeZealot < 3 && rangedUnits.size()<20; if (order.getType() == SquadOrderTypes::Attack || order.getType() == SquadOrderTypes::Defend) { // if there are targets if (!rangedUnitTargets.empty()) { // find the best target for this zealot auto targetIdx = attacker2target.find(rangedUnit); BWAPI::Unit target = targetIdx == attacker2target.end() ? getTarget(rangedUnit, rangedUnitTargets) : targetIdx->second; // attack it if (dodgeUnits.find(rangedUnit) == dodgeUnits.end()) { Micro::SmartAttackUnit(rangedUnit, target); } else { if (danceMode) Micro::SmartKiteTarget(rangedUnit, target, dodgeUnits[rangedUnit].second, withoutBuildingsAndWorkers); else Micro::SmartAttackUnit(rangedUnit, target); //modified //BWAPI::Broodwar->sendText("%s", "old kite"); //Micro::SmartKiteTarget(rangedUnit, dodgeUnits[rangedUnit].second); } } // if there are no targets else { Micro::SmartAttackMove(rangedUnit, order.getPosition()); } } } }
void CombatCommander::updateDefenseSquads() { if (_combatUnits.empty() || BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Terran_Science_Vessel) == 1) { return; } BWTA::BaseLocation * enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy()); BWTA::Region * enemyRegion = nullptr; if (enemyBaseLocation) { enemyRegion = BWTA::getRegion(enemyBaseLocation->getPosition()); } // for each of our occupied regions for (BWTA::Region * myRegion : InformationManager::Instance().getOccupiedRegions(BWAPI::Broodwar->self())) { // don't defend inside the enemy region, this will end badly when we are stealing gas if (myRegion == enemyRegion) { continue; } BWAPI::Position regionCenter = myRegion->getCenter(); if (!regionCenter.isValid()) { continue; } // start off assuming all enemy units in region are just workers int numDefendersPerEnemyUnit = 2; // all of the enemy units in this region BWAPI::Unitset enemyUnitsInRegion; for (auto & unit : BWAPI::Broodwar->enemy()->getUnits()) { // if it's an overlord, don't worry about it for defense, we don't care what they see if (unit->getType() == BWAPI::UnitTypes::Zerg_Overlord) { continue; } if (BWTA::getRegion(BWAPI::TilePosition(unit->getPosition())) == myRegion) { enemyUnitsInRegion.insert(unit); } } // we can ignore the first enemy worker in our region since we assume it is a scout for (auto & unit : enemyUnitsInRegion) { if (unit->getType().isWorker()) { enemyUnitsInRegion.erase(unit); break; } } int numEnemyFlyingInRegion = std::count_if(enemyUnitsInRegion.begin(), enemyUnitsInRegion.end(), [](BWAPI::Unit u) { return u->isFlying(); }); int numEnemyGroundInRegion = std::count_if(enemyUnitsInRegion.begin(), enemyUnitsInRegion.end(), [](BWAPI::Unit u) { return !u->isFlying(); }); std::stringstream squadName; squadName << "Base Defense " << regionCenter.x << " " << regionCenter.y; // if there's nothing in this region to worry about if (enemyUnitsInRegion.empty()) { // if a defense squad for this region exists, remove it if (_squadData.squadExists(squadName.str())) { _squadData.getSquad(squadName.str()).clear(); } // and return, nothing to defend here continue; } else { // if we don't have a squad assigned to this region already, create one if (!_squadData.squadExists(squadName.str())) { SquadOrder defendRegion(SquadOrderTypes::Defend, regionCenter, 32 * 25, "Defend Region!"); _squadData.addSquad(squadName.str(), Squad(squadName.str(), defendRegion, BaseDefensePriority)); } } // assign units to the squad if (_squadData.squadExists(squadName.str())) { Squad & defenseSquad = _squadData.getSquad(squadName.str()); // figure out how many units we need on defense int flyingDefendersNeeded = numDefendersPerEnemyUnit * numEnemyFlyingInRegion; int groundDefensersNeeded = numDefendersPerEnemyUnit * numEnemyGroundInRegion; updateDefenseSquadUnits(defenseSquad, flyingDefendersNeeded, groundDefensersNeeded); } else { UAB_ASSERT_WARNING(false, "Squad should have existed: %s", squadName.str().c_str()); } } // for each of our defense squads, if there aren't any enemy units near the position, remove the squad std::set<std::string> uselessDefenseSquads; for (const auto & kv : _squadData.getSquads()) { const Squad & squad = kv.second; const SquadOrder & order = squad.getSquadOrder(); if (order.getType() != SquadOrderTypes::Defend) { continue; } bool enemyUnitInRange = false; for (auto & unit : BWAPI::Broodwar->enemy()->getUnits()) { if (unit->getPosition().getDistance(order.getPosition()) < order.getRadius()) { enemyUnitInRange = true; break; } } if (!enemyUnitInRange) { _squadData.getSquad(squad.getName()).clear(); } } }
//dist is distance between middle unit and target gravity center, interval is length of arc between two units bool MicroManager::formSquad(const BWAPI::Unitset & targets, int dist, int radius, double angle, int interval) { const BWAPI::Unitset & meleeUnits = getUnits(); bool attacked = false; BWAPI::Position tpos = targets.getPosition(); BWAPI::Position mpos = meleeUnits.getPosition(); //do not from squad when fighting or close to enemy for (auto & unit : meleeUnits){ if (unit->isUnderAttack() || unit->isAttackFrame() || targets.size() == 0|| unit->getDistance(tpos) < 80) attacked = true; } if (attacked) { BWAPI::Broodwar->drawTextScreen(200, 340, "%s", "Attacked or No targets, Stop Formation"); return false; } //if there is a building near the unit, do not form for (auto & target : targets){ auto type = target->getType(); if (type.isBuilding()){ return false; } } //Formation is set false by Squad for 5 seconds after formation finished once if (!getFormation()){ BWAPI::Broodwar->drawTextScreen(200, 340, "%s", "Finished Formation"); return false; } //BWAPI::Broodwar->drawTextScreen(200, 340, "%s", "Forming"); const double PI = 3.14159265; double ang = angle / 180 * PI; //the angle of mid_point on arc double m_ang = atan2(mpos.y - tpos.y, mpos.x - tpos.x); //circle center int cx = (int)(tpos.x - (radius-dist) * cos(m_ang)); int cy = (int)(tpos.y - (radius-dist) * sin(m_ang)); BWAPI::Position c; c.x = cx; c.y = cy; //mid_point on arc BWAPI::Position m; m.x = (int)(cx + radius*cos(m_ang)); m.y = (int)(cy + radius*sin(m_ang)); BWAPI::Broodwar->drawLineMap(c, m, BWAPI::Colors::Yellow); BWAPI::Unitset unassigned; for (auto & unit : meleeUnits){ unassigned.insert(unit); } //move every positions on the arc to the closest unit BWAPI::Position tmp; int try_time = 0; int r = radius; int total_dest_dist = 0; int num_assigned = 0; while (unassigned.size() > 0 && try_time < 5){ double ang_interval = interval * 1.0 / r; double final_ang; int num_to_assign; int max_units = (int)(ang / ang_interval) + 1; if (unassigned.size() < (unsigned)max_units){ num_to_assign = unassigned.size(); final_ang = ang_interval * num_to_assign; } else { num_to_assign = max_units; final_ang = ang; } for (int i = 0; i < num_to_assign; i++) { //assign from two ends to middle double a = m_ang + pow(-1, i % 2)*(final_ang / 2 - (i / 2)*ang_interval); int min_dist = MAXINT; BWAPI::Unit closest_unit = nullptr; tmp.x = (int)(cx + r * cos(a)); tmp.y = (int)(cy + r * sin(a)); for (auto & unit : unassigned){ int d = unit->getDistance(tmp); if (d < min_dist){ min_dist = d; closest_unit = unit; } } //if it's a unit far away from fight, do not assign it to a position if (closest_unit && min_dist > 300){ unassigned.erase(closest_unit); continue; } if (tmp.isValid() && closest_unit){ BWAPI::Broodwar->drawLineMap(closest_unit->getPosition(), tmp, BWAPI::Colors::Red); Micro::SmartMove(closest_unit, tmp); unassigned.erase(closest_unit); //find the total distance between unit and destination total_dest_dist += min_dist; num_assigned++; } } r += 40; try_time++; } //if max destination distance less than 32, means forming has been finished if (num_assigned > 0 && total_dest_dist / num_assigned <= 32){ return true; } else { BWAPI::Broodwar->drawTextScreen(200, 340, "%s", "Forming"); return false; } }
// still has bug in it somewhere, use Old version void MeleeManager::assignTargetsNew(const BWAPI::Unitset & targets) { const BWAPI::Unitset & meleeUnits = getUnits(); // figure out targets BWAPI::Unitset meleeUnitTargets; for (auto & target : targets) { // conditions for targeting if (!(target->getType().isFlyer()) && !(target->isLifted()) && !(target->getType() == BWAPI::UnitTypes::Zerg_Larva) && !(target->getType() == BWAPI::UnitTypes::Zerg_Egg) && target->isVisible()) { meleeUnitTargets.insert(target); } } BWAPI::Unitset meleeUnitsToAssign(meleeUnits); std::map<BWAPI::Unit, int> attackersAssigned; for (auto & unit : meleeUnitTargets) { attackersAssigned[unit] = 0; } int smallThreshold = BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Zerg ? 3 : 1; int bigThreshold = BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Zerg ? 12 : 3; // keep assigning targets while we have attackers and targets remaining while (!meleeUnitsToAssign.empty() && !meleeUnitTargets.empty()) { auto attackerAssignment = findClosestUnitPair(meleeUnitsToAssign, meleeUnitTargets); BWAPI::Unit & attacker = attackerAssignment.first; BWAPI::Unit & target = attackerAssignment.second; UAB_ASSERT_WARNING(attacker, "We should have chosen an attacker!"); if (!attacker) { break; } if (!target) { Micro::SmartMove(attacker, order.getPosition()); continue; } Micro::SmartAttackUnit(attacker, target); // update the number of units assigned to attack the target we found int & assigned = attackersAssigned[attackerAssignment.second]; assigned++; // if it's a small / fast unit and there's more than 2 things attacking it already, don't assign more if ((target->getType().isWorker() || target->getType() == BWAPI::UnitTypes::Zerg_Zergling) && (assigned >= smallThreshold)) { meleeUnitTargets.erase(target); } // if it's a building and there's more than 10 things assigned to it already, don't assign more else if (assigned > bigThreshold) { meleeUnitTargets.erase(target); } meleeUnitsToAssign.erase(attacker); } // if there's no targets left, attack move to the order destination if (meleeUnitTargets.empty()) { for (auto & unit : meleeUnitsToAssign) { if (unit->getDistance(order.getPosition()) > 100) { // move to it Micro::SmartMove(unit, order.getPosition()); BWAPI::Broodwar->drawLineMap(unit->getPosition(), order.getPosition(), BWAPI::Colors::Yellow); } } } }