453 lines
13 KiB
C++
453 lines
13 KiB
C++
#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<TilePosition, int> 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<TilePosition, int> Military::getAttackLocation()
|
|
{
|
|
// start here - pick the most vulnerable base (getbasedefense) and attack
|
|
if (Macro::players.at(Macro::enemyIDs.front()).bases.size() > 0)
|
|
{
|
|
std::pair<int, int> pair = getMostVulnerableBase(Macro::enemyIDs.front());
|
|
int baseIndex = pair.first;
|
|
std::pair<TilePosition, int> 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<TilePosition, int> returnPair;
|
|
returnPair.first = u.lastSeen;
|
|
returnPair.second = u.unit->getType().groundWeapon().damageAmount();
|
|
Broodwar << " second return: " << returnPair.first << std::endl;
|
|
return returnPair;
|
|
}
|
|
}
|
|
}
|
|
std::pair<TilePosition, int> 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<int, int> 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<int, int> 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);
|
|
} |