public TargetPriorityCalculation CalculateTargetPriority(IEnumerable <UnitCalculation> allies, IEnumerable <UnitCalculation> enemies) { var calculation = new TargetPriorityCalculation { TargetPriority = TargetPriority.Attack }; var allyHealth = allies.Sum(e => e.SimulatedHitpoints); var enemyHealth = enemies.Sum(e => e.SimulatedHitpoints); var allyAttributes = allies.SelectMany(e => e.Attributes).Distinct(); var enemyAttributes = enemies.SelectMany(e => e.Attributes).Distinct(); var allyDps = allies.Sum(e => e.SimulatedDamagePerSecond(enemyAttributes, true, true)); var enemyDps = enemies.Sum(e => e.SimulatedDamagePerSecond(allyAttributes, true, true)); var allyHps = allies.Sum(e => e.SimulatedHealPerSecond); var enemyHps = enemies.Sum(e => e.SimulatedHealPerSecond); var secondsToKillEnemies = 1000f; if (allyDps - enemyHps > 0) { secondsToKillEnemies = enemyHealth / (allyDps - enemyHps); } var secondsToKillAllies = 1000f; if (enemyDps - allyHps > 0) { secondsToKillAllies = allyHealth / (enemyDps - allyHps); } calculation.OverallWinnability = secondsToKillAllies / secondsToKillEnemies; // higher the number the better if (secondsToKillEnemies == 0) { calculation.OverallWinnability = 1000f; } calculation.Overwhelm = calculation.OverallWinnability > 20; var airAttackingEnemies = enemies.Where(e => e.DamageAir || e.Unit.UnitType == (uint)UnitTypes.PROTOSS_SHIELDBATTERY); var airKillSeconds = 1000f; if (allies.Count(a => a.Unit.IsFlying && a.DamageAir) > 0) { var seconds = GetKillSeconds(airAttackingEnemies, allies); if (seconds > 0) { airKillSeconds = seconds; } else { airKillSeconds = 0.00001f; } } calculation.AirWinnability = secondsToKillAllies / airKillSeconds; var groundAttackingEnemies = enemies.Where(e => e.DamageGround || e.Unit.UnitType == (uint)UnitTypes.TERRAN_MEDIVAC || e.Unit.UnitType == (uint)UnitTypes.PROTOSS_WARPPRISM || e.Unit.UnitType == (uint)UnitTypes.PROTOSS_SHIELDBATTERY); var groundKillSeconds = 1000f; if (allies.Count(a => !a.Unit.IsFlying && a.DamageGround) > 0) { var seconds = GetKillSeconds(groundAttackingEnemies, allies); if (seconds > 0) { groundKillSeconds = seconds; } else { groundKillSeconds = 0.00001f; } } calculation.GroundWinnability = secondsToKillAllies / groundKillSeconds; if (calculation.OverallWinnability < 1 && calculation.AirWinnability < 1 && calculation.GroundWinnability < 1) { if (enemyHps > enemyDps && groundAttackingEnemies.Count(u => u.Unit.UnitType == (uint)UnitTypes.TERRAN_SCV) > 2) { var lowerHps = enemyHealth / (allyDps - (enemyHps / 5)); if (secondsToKillAllies / lowerHps > 1) { calculation.TargetPriority = TargetPriority.KillWorkers; // can win by targetting repairing scvs return(calculation); } } calculation.TargetPriority = TargetPriority.Retreat; return(calculation); } if (calculation.GroundWinnability > calculation.AirWinnability) { var bunker = groundAttackingEnemies.Where(b => b.Unit.UnitType == (uint)UnitTypes.TERRAN_BUNKER).FirstOrDefault(); if (bunker != null && groundAttackingEnemies.Count() < 10) { if (bunker.Unit.BuildProgress < 1 || (bunker.Unit.Health > 100 && enemyHps > enemyDps && groundAttackingEnemies.Count(u => u.Unit.UnitType == (uint)UnitTypes.TERRAN_SCV) > 2)) { calculation.TargetPriority = TargetPriority.KillWorkers; return(calculation); } calculation.TargetPriority = TargetPriority.KillBunker; return(calculation); } calculation.TargetPriority = TargetPriority.WinGround; } else if (calculation.AirWinnability > calculation.GroundWinnability) { calculation.TargetPriority = TargetPriority.WinAir; } return(calculation); }
public UnitCalculation(Unit unit, int repairers, SharkyUnitData sharkyUnitData, SharkyOptions sharkyOptions, UnitDataService unitDataService, int frame) { TargetPriorityCalculation = new TargetPriorityCalculation(); oneSecondInFrames = sharkyOptions.FramesPerSecond; //PreviousUnits = new Dictionary<int, Unit>(); //PreviousUnits[frame] = unit; PreviousUnit = unit; Unit = unit; UnitTypeData = sharkyUnitData.UnitData[(UnitTypes)unit.UnitType]; Velocity = 0; AverageVelocity = 0; Vector = Vector2.Zero; AverageVector = Vector2.Zero; Position = new Vector2(unit.Pos.X, unit.Pos.Y); FrameLastSeen = frame; var unitRange = unitDataService.GetRange(unit); if (unitRange == 0) { Range = 0; } else { Range = unitRange + unit.Radius; } Start = new Vector2(unit.Pos.X, unit.Pos.Y); if (unit.UnitType == (uint)UnitTypes.PROTOSS_COLOSSUS || unit.UnitType == (uint)UnitTypes.PROTOSS_IMMORTAL || unit.UnitType == (uint)UnitTypes.PROTOSS_PHOTONCANNON || unit.UnitType == (uint)UnitTypes.PROTOSS_MOTHERSHIP || unit.UnitType == (uint)UnitTypes.TERRAN_MISSILETURRET || unit.UnitType == (uint)UnitTypes.ZERG_SPORECRAWLER || unit.UnitType == (uint)UnitTypes.ZERG_SPINECRAWLER) { End = Start; // facing is always 0 for these units, can't calculate where they're aiming } else { var endX = (float)(Range * Math.Sin(unit.Facing + (Math.PI / 2))); var endY = (float)(Range * Math.Cos(unit.Facing + (Math.PI / 2))); End = new Vector2(endX + unit.Pos.X, unit.Pos.Y - endY); } DamageRadius = 1; // TODO: get damage radius EstimatedCooldown = 0; // TODO: get estimated cooldown DamageAir = false; if (unitDataService.CanAttackAir((UnitTypes)unit.UnitType)) { DamageAir = true; } DamageGround = false; if (unitDataService.CanAttackGround((UnitTypes)unit.UnitType)) { DamageGround = true; } Damage = unitDataService.GetDamage(unit); Dps = unitDataService.GetDps(unit); Weapon = unitDataService.GetWeapon(unit); Weapons = UnitTypeData.Weapons.ToList(); if (Weapons == null || Weapons.Count() == 0) { Weapons = new List <Weapon>(); if (Weapon != null) { Weapons.Add(Weapon); } } SimulatedHitpoints = Unit.Health + Unit.Shield; if (Unit.BuffIds.Contains((uint)Buffs.IMMORTALOVERLOAD)) { SimulatedHitpoints += 100; } if (unit.UnitType == (uint)UnitTypes.PROTOSS_WARPPRISM) { SimulatedHitpoints += 500; } if (sharkyUnitData.ZergTypes.Contains((UnitTypes)Unit.UnitType)) { SimulatedHealPerSecond = 0.38f; } else if (repairers > 0 && UnitTypeData.Attributes.Contains(SC2APIProtocol.Attribute.Mechanical)) { SimulatedHealPerSecond = (float)(unit.HealthMax / (UnitTypeData.BuildTime / sharkyOptions.FramesPerSecond)) * repairers; } else if (Unit.UnitType == (uint)UnitTypes.TERRAN_MEDIVAC && Unit.Energy > 10) { SimulatedHealPerSecond = 12.6f; } else if (Unit.UnitType == (uint)UnitTypes.ZERG_QUEEN && Unit.Energy >= 50) { SimulatedHealPerSecond = 20; } else { SimulatedHealPerSecond = 0; } if (Unit.UnitType == (uint)UnitTypes.TERRAN_BUNKER && Unit.BuildProgress == 1) // assume 4 marines { Range = 6; DamageAir = true; DamageGround = true; Damage = 6; Dps = Damage * 4 / 0.61f; } Attributes = UnitTypeData.Attributes; UnitClassifications = new List <UnitClassification>(); if (UnitTypeData.Attributes.Contains(SC2APIProtocol.Attribute.Structure)) { if (sharkyUnitData.ResourceCenterTypes.Contains((UnitTypes)unit.UnitType)) { UnitClassifications.Add(UnitClassification.ResourceCenter); UnitClassifications.Add(UnitClassification.ProductionStructure); } if (sharkyUnitData.DefensiveStructureTypes.Contains((UnitTypes)unit.UnitType)) { UnitClassifications.Add(UnitClassification.DefensiveStructure); } } else if (unit.UnitType == (uint)UnitTypes.TERRAN_SCV || unit.UnitType == (uint)UnitTypes.PROTOSS_PROBE || unit.UnitType == (uint)UnitTypes.ZERG_DRONE) { UnitClassifications.Add(UnitClassification.Worker); } else if (unit.UnitType == (uint)UnitTypes.ZERG_QUEEN || unit.UnitType == (uint)UnitTypes.TERRAN_MULE || unit.UnitType == (uint)UnitTypes.ZERG_OVERLORD || unit.UnitType == (uint)UnitTypes.ZERG_LARVA || unit.UnitType == (uint)UnitTypes.ZERG_EGG) { } else { UnitClassifications.Add(UnitClassification.ArmyUnit); } if (sharkyUnitData.DetectionTypes.Contains((UnitTypes)unit.UnitType)) { UnitClassifications.Add(UnitClassification.Detector); } if (sharkyUnitData.AbilityDetectionTypes.Contains((UnitTypes)unit.UnitType)) { UnitClassifications.Add(UnitClassification.DetectionCaster); } if (sharkyUnitData.CloakableAttackers.Contains((UnitTypes)unit.UnitType)) { UnitClassifications.Add(UnitClassification.Cloakable); } EnemiesInRange = new List <UnitCalculation>(); EnemiesInRangeOf = new List <UnitCalculation>(); NearbyAllies = new List <UnitCalculation>(); NearbyEnemies = new List <UnitCalculation>(); Attackers = new List <UnitCalculation>(); IncomingDamage = 0; }