#include "Military.h" void Military::addSquadlessUnits(int squadIndex) { for (auto u : Macro::players.at(Macro::selfID).units) { if (u.unit->getType().supplyProvided() == 0 && !u.unit->getType().isBuilding() && u.unit->getType() != UnitTypes::Zerg_Larva && u.unit->getType() != UnitTypes::Zerg_Egg && u.unit->getType() != UnitTypes::Zerg_Drone && u.unit->getType() != UnitTypes::Zerg_Overlord && !u.isInSquad() ) { Macro::squads.at(squadIndex).units.push_back(u); } } } // attacks the enemy with everything except one squad for defense void Military::attackMaxed() { int squadIndex = getAttackSquadsIndex(); addSquadlessUnits(squadIndex); Position attackLocation = Position(getAttackLocation().first); for (size_t i = 0; i < Macro::squads.size(); i++) { if (Macro::squads.at(i).action == "attack") { for (auto u : Macro::squads.at(i).units) { u.unit->attack(attackLocation); if (u.unit->getType() == UnitTypes::Zerg_Overlord) { u.unit->move(attackLocation); } } } } } // attacks the enemy with one squad void Military::attackNonMaxed() { // things to consider: // how strong the most vulnerable enemy base is // how strong the attack squads are std::pair baseToAttack = getAttackLocation(); int damagePadding = 120; int squadIndex = getIdleSquad(); // find which squad to send to attack if (squadIndex > -1) { addSquadlessUnits(squadIndex); Macro::squads.at(squadIndex).updateDamage(); if (Macro::squads.at(squadIndex).groundDamage > baseToAttack.second + damagePadding) { Macro::squads.at(squadIndex).attack(baseToAttack); } } } // determine whether to attack void Military::checkAttack() { // things to consider: // if supply is maxed // if a base is vulnerable (hit and run) // if the enemy is at a disadvantage (do some damage and/or go for the win) if (Broodwar->self()->supplyUsed() > 380) attackMaxed(); else if (shouldAttackNonMaxed()) attackNonMaxed(); } // determine where is being attacked and send units to defend void Military::checkDefense() { bool attacking = false; Position attackLocation; // check if enemies are in certain locations and send defending units if they are for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) { Macro::players.at(Macro::selfID).bases.at(i).getDefense(); double attackerScore = checkEnemiesAt(Macro::players.at(Macro::selfID).bases.at(i)); if (attackerScore > 5) { attacking = true; Macro::setLastAttack(Broodwar->getFrameCount()); sendDefense( Macro::players.at(Macro::selfID).bases.at(i).base->getClosestUnit(Filter::IsEnemy)->getTilePosition(), attackerScore, Macro::players.at(Macro::selfID).bases.at(i).sunkenCount * UnitTypes::Zerg_Sunken_Colony.groundWeapon().damageAmount()/2); //attackLocation = Macro::players.at(Macro::selfID).bases.at(i).base->getClosestUnit(Filter::IsEnemy)->getPosition(); } } for (size_t i = 0; i < Macro::squads.size(); i++) { } /* if (attacking) { for (auto u : Macro::players.at(Macro::selfID).units) { if (u.unit->getType().supplyProvided() == 0 && !u.unit->getType().isBuilding() && u.unit->getType() != UnitTypes::Zerg_Larva && u.unit->getType() != UnitTypes::Zerg_Egg && u.unit->getType() != UnitTypes::Zerg_Drone && u.unit->getType() != UnitTypes::Zerg_Overlord && !Util::isInSquad(u) ) { if (Macro::squads.size() == 0) Macro::squads.push_back(Squad()); Macro::squads.back().units.push_back(u); u.action = "defend"; u.unit->attack(attackLocation); } } }*/ } // returns how much attack power is at the specified CBase double Military::checkEnemiesAt(CBase b) { double score = 0; for (auto u : b.base->getUnitsInRadius(32*20, Filter::IsEnemy)) { if (u->getType().groundWeapon() != WeaponTypes::None) { score += u->getType().groundWeapon().damageAmount(); } } return score; } void Military::checkSquads() { int added = 0; for (size_t i = 0; i < Macro::squads.size(); i++) { Macro::squads.at(i).updateCenter(); Macro::squads.at(i).updateDamage(); if (Macro::squads.at(i).units.size() > 0) { int baseIndex = Util::getClosestOwnedBase(Macro::squads.at(i).units.front().unit); if (baseIndex > -1) { if (Broodwar->getFrameCount() > Macro::players.at(Macro::selfID).bases.at(baseIndex).lastAttack + (10 * 24)) { Macro::squads.at(i).action = ""; for (size_t j = 0; j < Macro::squads.at(i).units.size(); j++) { Macro::squads.at(i).units.at(j).action = ""; } } } } if (!Macro::squads.at(i).containsType(UnitTypes::Zerg_Overlord)) { for (size_t j = 0; j < Macro::players.at(Macro::selfID).units.size(); j++) { if (Macro::players.at(Macro::selfID).units.at(j).unit->getType() == UnitTypes::Zerg_Overlord && Macro::players.at(Macro::selfID).units.at(j).action != "scout" && Macro::players.at(Macro::selfID).units.at(j).action != "squad" && added < 2) { added++; Macro::squads.at(i).units.push_back(Macro::players.at(Macro::selfID).units.at(j)); Macro::players.at(Macro::selfID).units.at(j).action = "squad"; } } } } } // for defending in rushes or other low-eco situations bool Military::droneDefenseNeeded() { return (Util::countUnits(Macro::selfID, BWAPI::UnitTypes::Zerg_Zergling) < 10 && Util::countUnits(Macro::selfID, BWAPI::UnitTypes::Zerg_Hydralisk) < 10); } std::pair Military::getAttackLocation() { // start here - pick the most vulnerable base (getbasedefense) and attack if (Macro::players.at(Macro::enemyIDs.front()).bases.size() > 0) { std::pair pair = getMostVulnerableBase(Macro::enemyIDs.front()); int baseIndex = pair.first; std::pair returnPair; try { returnPair.first = Macro::players.at(Macro::enemyIDs.front()).bases.at(baseIndex).tilePosition; returnPair.second = pair.second; } catch (const std::exception& e) { Broodwar << "out of range error: base index = " << baseIndex << std::endl; returnPair.first = TilePosition(50,50); returnPair.second = 1000; } //Broodwar << " first return: " << returnPair.first << std::endl; return returnPair; } else { for (auto u : Macro::players.at(Macro::enemyIDs.front()).units) { //TODO sometimes a unit isn't being added properly and this returns a wrong value if (u.unit->getType().isBuilding()) { std::pair returnPair; returnPair.first = u.lastSeen; returnPair.second = u.unit->getType().groundWeapon().damageAmount(); Broodwar << " second return: " << returnPair.first << std::endl; return returnPair; } } } std::pair returnPair; returnPair.first = TilePosition(Broodwar->mapWidth() / 2, Broodwar->mapHeight() / 2); returnPair.second = 0; return returnPair; } int Military::getAttackSquadsIndex() { int skippedSquads = 0; int squadIndex = -1; for (size_t i = 0; i < Macro::squads.size(); i++) { if (Macro::squads.at(i).action == "defend" && skippedSquads == 0) { skippedSquads++; } else { Macro::squads.at(i).action = "attack"; squadIndex = i; } } if (squadIndex == -1) { Macro::squads.push_back(Squad()); squadIndex = Macro::squads.size() - 1; } return squadIndex; } double Military::getBaseDefense(CBase base, std::string type, int playerID) { double score = 0; if (type == "ground") { for (size_t i = 0; i < Macro::players.at(playerID).units.size(); i++) { if (Util::getDistance(Macro::players.at(playerID).units.at(i).lastSeen, base.tilePosition) < 15) if (Macro::players.at(playerID).units.at(i).unitType.groundWeapon() != WeaponTypes::None) { score += Macro::players.at(playerID).units.at(i).unitType.groundWeapon().damageAmount(); } } return score + base.sunkenCount; } else { for (auto u : Broodwar->getUnitsInRadius(base.base->getPosition(), 15*32)) { if (u->getType().airWeapon() != WeaponTypes::None && u->getPlayer()->getID() == playerID) { score += u->getType().airWeapon().damageAmount(); } } return score + base.sporeCount; } } int Military::getClosestSquad(TilePosition tp) { int index = -1; double distance = 1000; for (size_t i = 0; i < Macro::squads.size(); i++) { if (Util::getDistance(tp, Macro::squads.at(i).center) < distance && Macro::squads.at(i).action != "defend") { index = i; distance = Util::getDistance(tp, Macro::squads.at(i).center); } } return index; } int Military::getIdleSquad() { int squadIndex = -1; for (size_t i = 0; i < Macro::squads.size(); i++) { if (Macro::squads.at(i).action != "defend" && Macro::squads.at(i).action != "attack") { squadIndex = i; break; } } return squadIndex; } std::pair Military::getMostVulnerableBase(int playerID) { int index = -1; double score = 10000; for (size_t i = 0; i < Macro::players.at(playerID).bases.size(); i++) { int baseScore = getBaseDefense(Macro::players.at(playerID).bases.at(i), "ground", playerID); if (baseScore < score && (Macro::players.at(playerID).bases.at(i).exposedToCenter || Macro::players.at(playerID).bases.size() == 1)) { score = baseScore; index = i; } } if (index == -1 && Macro::players.at(playerID).bases.size() == 1) { index = 0; } std::pair pair; pair.first = index; pair.second = score; return pair; } bool Military::isThreatened(TilePosition tp) { for (auto u : Broodwar->getUnitsInRadius(Position(tp), 20, Filter::IsEnemy)) { if (u->canAttackUnit(UnitTypes::Zerg_Overlord) && inThreatRange(tp, CUnit(u))) return true; } return false; } bool Military::inThreatRange(TilePosition tp, CUnit unit) { return Util::getDistance(tp, unit.unit->getTilePosition()) < unit.unit->getType().seekRange() + 1; } // returns how likely on a scale of 0 to 1 the enemy is to attack soon // things to consider: // how long it's been since the last time they attacked // how many units the enemy might have double Military::likelihoodToAttack() { double score = 1; double timeModifier = 0; double attackTime = 360.0 * 24; if (!Macro::enemyIDs.empty()) { if (Macro::players.at(Macro::enemyIDs.front()).bases.size() == 2) attackTime = 540.0 * 24; } if(Broodwar->getFrameCount() < 360*24) timeModifier = Broodwar->getFrameCount() / attackTime; else timeModifier = (Broodwar->getFrameCount() - Macro::lastAttack) / (540.0 * 24); if (Broodwar->getFrameCount() - Macro::lastAttack < 10*24 && Macro::lastAttack > 0) return 1; if (timeModifier > 1) timeModifier = 1.0; if (Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Barracks) == 0 && Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Gateway) == 0 && Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Zerg_Spawning_Pool) == 0) { score = 0; } return score * timeModifier; } // sends a squad to defend // things to consider: // how important the place being attacked is // if a bigger win can be found by attacking instead (base race) // how powerful the attacking force is // how powerful the defending force is void Military::sendDefense(TilePosition tp, int attackerScore, int defenseScore) { int squadIndex = getClosestSquad(tp); if (squadIndex == -1) { squadIndex = 0; Macro::squads.push_back(Squad()); } int padding = 80; // give the defense some breathing room by spotting the attackers some damage Macro::squads.at(squadIndex).action = "defend"; if (defenseScore + Macro::squads.at(squadIndex).groundDamage < attackerScore + padding) { int index = 0; for (auto u : Macro::players.at(Macro::selfID).units) { if (defenseScore + Macro::squads.at(squadIndex).groundDamage < attackerScore + padding) { if ((Util::isAttackingUnit(u) || droneDefenseNeeded()) && !u.isInSquad()) { Macro::squads.at(squadIndex).addUnit(u); u.squadIndex = squadIndex; if (u.unit->getType() == UnitTypes::Zerg_Drone) { Macro::freeUnit(index); u.unsetTargetUnit(); } /*Macro::squads.at(squadIndex).units.push_back(u); Macro::squads.at(squadIndex).airDamage += u.unitType.airWeapon().damageAmount(); Macro::squads.at(squadIndex).groundDamage += u.unitType.groundWeapon().damageAmount();*/ } } index++; } } for (auto u : Macro::squads.at(squadIndex).units) { u.unit->attack(Position(tp)); u.action = "defend"; } } bool Military::shouldAttackNonMaxed() { return ((Macro::lastAttack > 0 && (Broodwar->getFrameCount() - Macro::lastAttack > 24 * 10)) || Broodwar->getFrameCount() > 24 * 360); }