kora-bot/Source/Military.cpp

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