context: #region Copyright & License Information
  /*
   * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation. For more information,
   * see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Linq;
  using OpenRA.Mods.Common.Activities;
  using OpenRA.Mods.Common.Traits;
  using OpenRA.Primitives;
  using OpenRA.Support;
  using OpenRA.Traits;
  
  namespace OpenRA.Mods.Common.AI
  {
  	public sealed class HackyAIInfo : IBotInfo, ITraitInfo
  	{
  		[Desc("Ingame name this bot uses.")]
  		public readonly string Name = "Unnamed Bot";
  
  		[Desc("Minimum number of units AI must have before attacking.")]
  		public readonly int SquadSize = 8;
  
  		[Desc("Random number of up to this many units is added to squad size when creating an attack squad.")]
  		public readonly int SquadSizeRandomBonus = 30;
  
  		[Desc("Production queues AI uses for buildings.")]
  		public readonly string[] BuildingQueues = { "Building" };
  
  		[Desc("Production queues AI uses for defenses.")]
  		public readonly string[] DefenseQueues = { "Defense" };
  
  		[Desc("Delay (in ticks) between giving out orders to units.")]
  		public readonly int AssignRolesInterval = 20;
  
  		[Desc("Delay (in ticks) between attempting rush attacks.")]
  		public readonly int RushInterval = 600;
  
  		[Desc("Delay (in ticks) between updating squads.")]
  		public readonly int AttackForceInterval = 30;
  
  		[Desc("Minimum delay (in ticks) between creating squads.")]
  		public readonly int MinimumAttackForceDelay = 0;
  
  		[Desc("How long to wait (in ticks) between structure production checks when there is no active production.")]
  		public readonly int StructureProductionInactiveDelay = 125;
  
  		[Desc("How long to wait (in ticks) between structure production checks ticks when actively building things.")]
  		public readonly int StructureProductionActiveDelay = 10;
  
  		[Desc("Minimum range at which to build defensive structures near a combat hotspot.")]
  		public readonly int MinimumDefenseRadius = 5;
  
  		[Desc("Maximum range at which to build defensive structures near a combat hotspot.")]
  		public readonly int MaximumDefenseRadius = 20;
  
  		[Desc("Try to build another production building if there is too much cash.")]
  		public readonly int NewProductionCashThreshold = 5000;
  
  		[Desc("Only produce units as long as there are less than this amount of units idling inside the base.")]
  		public readonly int IdleBaseUnitsMaximum = 12;
  
  		[Desc("Radius in cells around enemy BaseBuilder (Construction Yard) where AI scans for targets to rush.")]
  		public readonly int RushAttackScanRadius = 15;
  
  		[Desc("Radius in cells around the base that should be scanned for units to be protected.")]
  		public readonly int ProtectUnitScanRadius = 15;
  
  		[Desc("Radius in cells around a factory scanned for rally points by the AI.")]
  		public readonly int RallyPointScanRadius = 8;
  
  		[Desc("Radius in cells around the center of the base to expand.")]
  		public readonly int MaxBaseRadius = 20;
  
  		[Desc("Production queues AI uses for producing units.")]
  		public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" };
  
  		[Desc("Should the AI repair its buildings if damaged?")]
  		public readonly bool ShouldRepairBuildings = true;
  
  		string IBotInfo.Name { get { return this.Name; } }
  
  		[Desc("What units to the AI should build.", "What % of the total army must be this type of unit.")]
  		[FieldLoader.LoadUsing("LoadUnits")]
  		public readonly Dictionary<string, float> UnitsToBuild = null;
  
  		[Desc("What buildings to the AI should build.", "What % of the total base must be this type of building.")]
  		[FieldLoader.LoadUsing("LoadBuildings")]
  		public readonly Dictionary<string, float> BuildingFractions = null;
  
  		[Desc("Tells the AI what unit types fall under the same common name.")]
  		[FieldLoader.LoadUsing("LoadUnitsCommonNames")]
  		public readonly Dictionary<string, string[]> UnitsCommonNames = null;
  
  		[Desc("Tells the AI what building types fall under the same common name.")]
  		[FieldLoader.LoadUsing("LoadBuildingsCommonNames")]
  		public readonly Dictionary<string, string[]> BuildingCommonNames = null;
  
  		[Desc("What buildings should the AI have max limits n.", "What is the limit of the building.")]
  		[FieldLoader.LoadUsing("LoadBuildingLimits")]
  		public readonly Dictionary<string, int> BuildingLimits = null;
  
  		// TODO Update OpenRA.Utility/Command.cs#L300 to first handle lists and also read nested ones
  		[Desc("Tells the AI how to use its support powers.")]
  		[FieldLoader.LoadUsing("LoadDecisions")]
  		public readonly List<SupportPowerDecision> PowerDecisions = new List<SupportPowerDecision>();
  
  		static object LoadList<T>(MiniYaml y, string field)
  		{
  			var nd = y.ToDictionary();
  			return nd.ContainsKey(field)
  				? nd[field].ToDictionary(my => FieldLoader.GetValue<T>(field, my.Value))
  				: new Dictionary<string, T>();
  		}
  
  		static object LoadUnits(MiniYaml y) { return LoadList<float>(y, "UnitsToBuild"); }
  		static object LoadBuildings(MiniYaml y) { return LoadList<float>(y, "BuildingFractions"); }
  
  		static object LoadUnitsCommonNames(MiniYaml y) { return LoadList<string[]>(y, "UnitsCommonNames"); }
  		static object LoadBuildingsCommonNames(MiniYaml y) { return LoadList<string[]>(y, "BuildingCommonNames"); }
  
  		static object LoadBuildingLimits(MiniYaml y) { return LoadList<int>(y, "BuildingLimits"); }
  
  		static object LoadDecisions(MiniYaml yaml)
  		{
  			var ret = new List<SupportPowerDecision>();
  			foreach (var d in yaml.Nodes)
  				if (d.Key.Split('@')[0] == "SupportPowerDecision")
  					ret.Add(new SupportPowerDecision(d.Value));
  
  			return ret;
  		}
  
  		public object Create(ActorInitializer init) { return new HackyAI(this, init); }
  	}
  
  	public class Enemy { public int Aggro; }
  
  	public enum BuildingType { Building, Defense, Refinery }
  
  	public sealed class HackyAI : ITick, IBot, INotifyDamage
  	{
  		public MersenneTwister Random { get; private set; }
  		public readonly HackyAIInfo Info;
  
  		public CPos GetRandomBaseCenter()
  		{
  			var randomBaseBuilding = World.Actors.Where(
  				a => a.Owner == Player
  					&& a.HasTrait<BaseBuilding>()
  					&& !a.HasTrait<Mobile>())
  				.RandomOrDefault(Random);
  
  			return randomBaseBuilding != null ? randomBaseBuilding.Location : initialBaseCenter;
  		}
  
  		public Player Player { get; private set; }
  
  		Dictionary<SupportPowerInstance, int> waitingPowers = new Dictionary<SupportPowerInstance, int>();
  		Dictionary<string, SupportPowerDecision> powerDecisions = new Dictionary<string, SupportPowerDecision>();
  
  		CPos initialBaseCenter;
  		PowerManager playerPower;
  		SupportPowerManager supportPowerMngr;
  		PlayerResources playerResource;
  		bool enabled;
  		int ticks;
  
  		BitArray resourceTypeIndices;
  
  		RushFuzzy rushFuzzy = new RushFuzzy();
  
  		Cache<Player, Enemy> aggro = new Cache<Player, Enemy>(_ => new Enemy());
  		List<BaseBuilder> builders = new List<BaseBuilder>();
  
  		List<Squad> squads = new List<Squad>();
  		List<Actor> unitsHangingAroundTheBase = new List<Actor>();
  
  		// Units that the ai already knows about. Any unit not on this list needs to be given a role.
  		List<Actor> activeUnits = new List<Actor>();
  
  		public const int FeedbackTime = 30;		// ticks; = a bit over 1s. must be >= netlag.
  
  		public readonly World World;
  		public Map Map { get { return World.Map; } }
  		IBotInfo IBot.Info { get { return this.Info; } }
  
  		public HackyAI(HackyAIInfo info, ActorInitializer init)
  		{
  			Info = info;
  			World = init.World;
  
  			foreach (var decision in info.PowerDecisions)
  				powerDecisions.Add(decision.OrderName, decision);
  		}
  
  		public static void BotDebug(string s, params object[] args)
  		{
  			if (Game.Settings.Debug.BotDebug)
  				Game.Debug(s, args);
  		}
  
  		// Called by the host's player creation code
  		public void Activate(Player p)
  		{
  			Player = p;
  			enabled = true;
  			playerPower = p.PlayerActor.Trait<PowerManager>();
  			supportPowerMngr = p.PlayerActor.Trait<SupportPowerManager>();
  			playerResource = p.PlayerActor.Trait<PlayerResources>();
  
  			foreach (var building in Info.BuildingQueues)
  				builders.Add(new BaseBuilder(this, building, p, playerPower, playerResource));
  			foreach (var defense in Info.DefenseQueues)
  				builders.Add(new BaseBuilder(this, defense, p, playerPower, playerResource));
  
  			Random = new MersenneTwister((int)p.PlayerActor.ActorID);
  
  			resourceTypeIndices = new BitArray(World.TileSet.TerrainInfo.Length); // Big enough
  			foreach (var t in Map.Rules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>())
  				resourceTypeIndices.Set(World.TileSet.GetTerrainIndex(t.TerrainType), true);
  		}
  
  		ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue)
  		{
  			var buildableThings = queue.BuildableItems();
  			if (!buildableThings.Any())
  				return null;
  
  			var unit = buildableThings.Random(Random);
  			return HasAdequateAirUnitReloadBuildings(unit) ? unit : null;
  		}
  
  		ActorInfo ChooseUnitToBuild(ProductionQueue queue)
  		{
  			var buildableThings = queue.BuildableItems();
  			if (!buildableThings.Any())
  				return null;
  
  			var myUnits = Player.World
  				.ActorsWithTrait<IPositionable>()
  				.Where(a => a.Actor.Owner == Player)
  				.Select(a => a.Actor.Info.Name).ToList();
  
  			foreach (var unit in Info.UnitsToBuild.Shuffle(Random))
  				if (buildableThings.Any(b => b.Name == unit.Key))
  					if (myUnits.Count(a => a == unit.Key) < unit.Value * myUnits.Count)
  						if (HasAdequateAirUnitReloadBuildings(Map.Rules.Actors[unit.Key]))
  							return Map.Rules.Actors[unit.Key];
  
  			return null;
  		}
  
  		int CountBuilding(string frac, Player owner)
  		{
  			return World.ActorsWithTrait<Building>()
  				.Count(a => a.Actor.Owner == owner && a.Actor.Info.Name == frac);
  		}
  
  		int CountUnits(string unit, Player owner)
  		{
  			return World.ActorsWithTrait<IPositionable>()
  				.Count(a => a.Actor.Owner == owner && a.Actor.Info.Name == unit);
  		}
  
  		int? CountBuildingByCommonName(string commonName, Player owner)
  		{
  			if (!Info.BuildingCommonNames.ContainsKey(commonName))
  				return null;
  
  			return World.ActorsWithTrait<Building>()
  				.Count(a => a.Actor.Owner == owner && Info.BuildingCommonNames[commonName].Contains(a.Actor.Info.Name));
  		}
  
  		public ActorInfo GetBuildingInfoByCommonName(string commonName, Player owner)
  		{
  			if (commonName == "ConstructionYard")
  				return Map.Rules.Actors.Where(k => Info.BuildingCommonNames[commonName].Contains(k.Key)).Random(Random).Value;
  
  			return GetInfoByCommonName(Info.BuildingCommonNames, commonName, owner);
  		}
  
  		public ActorInfo GetUnitInfoByCommonName(string commonName, Player owner)
  		{
  			return GetInfoByCommonName(Info.UnitsCommonNames, commonName, owner);
  		}
  
  		public ActorInfo GetInfoByCommonName(Dictionary<string, string[]> names, string commonName, Player owner)
  		{
  			if (!names.Any() || !names.ContainsKey(commonName))
  				throw new InvalidOperationException("Can't find {0} in the HackyAI UnitsCommonNames definition.".F(commonName));
  
  			return Map.Rules.Actors.Where(k => names[commonName].Contains(k.Key)).Random(Random).Value;
  		}
  
  		public bool HasAdequateFact()
  		{
  			// Require at least one construction yard, unless we have no vehicles factory (can't build it).
  			return CountBuildingByCommonName("ConstructionYard", Player) > 0 ||
  				CountBuildingByCommonName("VehiclesFactory", Player) == 0;
  		}
  
  		public bool HasAdequateProc()
  		{
  			// Require at least one refinery, unless we have no power (can't build it).
  			return CountBuildingByCommonName("Refinery", Player) > 0 ||
  				CountBuildingByCommonName("Power", Player) == 0;
  		}
  
  		public bool HasMinimumProc()
  		{
  			// Require at least two refineries, unless we have no power (can't build it)
  			// or barracks (higher priority?)
  			return CountBuildingByCommonName("Refinery", Player) >= 2 ||
  				CountBuildingByCommonName("Power", Player) == 0 ||
  				CountBuildingByCommonName("Barracks", Player) == 0;
  		}
  
  		// For mods like RA (number of building must match the number of aircraft)
  		bool HasAdequateAirUnitReloadBuildings(ActorInfo actorInfo)
  		{
  			var aircraftInfo = actorInfo.Traits.GetOrDefault<AircraftInfo>();
  			if (aircraftInfo == null)
  				return true;
  
  			var ammoPoolsInfo = actorInfo.Traits.WithInterface<AmmoPoolInfo>();
  
  			if (ammoPoolsInfo.Any(x => !x.SelfReloads))
  			{
  				var countOwnAir = CountUnits(actorInfo.Name, Player);
  				var countBuildings = CountBuilding(aircraftInfo.RearmBuildings.FirstOrDefault(), Player);
  				if (countOwnAir >= countBuildings)
  					return false;
  			}
  
  			return true;
  		}
  
  		CPos defenseCenter;
  		public CPos? ChooseBuildLocation(string actorType, bool distanceToBaseIsImportant, BuildingType type)
  		{
  			var bi = Map.Rules.Actors[actorType].Traits.GetOrDefault<BuildingInfo>();
  			if (bi == null)
  				return null;
  
  			// Find the buildable cell that is closest to pos and centered around center
  			Func<CPos, CPos, int, int, CPos?> findPos = (center, target, minRange, maxRange) =>
  			{
  				var cells = Map.FindTilesInAnnulus(center, minRange, maxRange);
  
  				// Sort by distance to target if we have one
  				if (center != target)
  					cells = cells.OrderBy(c => (c - target).LengthSquared);
  				else
  					cells = cells.Shuffle(Random);
  
  				foreach (var cell in cells)
  				{
  					if (!World.CanPlaceBuilding(actorType, bi, cell, null))
  						continue;
  
  					if (distanceToBaseIsImportant && !bi.IsCloseEnoughToBase(World, Player, actorType, cell))
  						continue;
  
  					return cell;
  				}
  
  				return null;
  			};
  
  			var baseCenter = GetRandomBaseCenter();
  
  			switch (type)
  			{
  				case BuildingType.Defense:
  
  					// Build near the closest enemy structure
  					var closestEnemy = World.Actors.Where(a => !a.Disposed && a.HasTrait<Building>() && Player.Stances[a.Owner] == Stance.Enemy)
  						.ClosestTo(World.Map.CenterOfCell(defenseCenter));
  
  					var targetCell = closestEnemy != null ? closestEnemy.Location : baseCenter;
  					return findPos(defenseCenter, targetCell, Info.MinimumDefenseRadius, Info.MaximumDefenseRadius);
  
  				case BuildingType.Refinery:
  
  					// Try and place the refinery near a resource field
  					var nearbyResources = Map.FindTilesInCircle(baseCenter, Info.MaxBaseRadius)
  						.Where(a => resourceTypeIndices.Get(Map.GetTerrainIndex(a)))
  						.Shuffle(Random);
  
  					foreach (var c in nearbyResources)
  					{
  						var found = findPos(c, baseCenter, 0, Info.MaxBaseRadius);
  						if (found != null)
  							return found;
  					}
  
  					// Try and find a free spot somewhere else in the base
  					return findPos(baseCenter, baseCenter, 0, Info.MaxBaseRadius);
  
  				case BuildingType.Building:
  					return findPos(baseCenter, baseCenter, 0, distanceToBaseIsImportant ? Info.MaxBaseRadius : Map.MaxTilesInCircleRange);
  			}
  
  			// Can't find a build location
  			return null;
  		}
  
  		public void Tick(Actor self)
  		{
  			if (!enabled)
  				return;
  
  			ticks++;
  
  			if (ticks == 1)
  				InitializeBase(self);
  
  			if (ticks % FeedbackTime == 0)
  				ProductionUnits(self);
  
  			AssignRolesToIdleUnits(self);
  			SetRallyPointsForNewProductionBuildings(self);
  			TryToUseSupportPower(self);
  
  			foreach (var b in builders)
  				b.Tick();
  		}
  
  		internal Actor ChooseEnemyTarget()
  		{
  			if (Player.WinState != WinState.Undefined)
  				return null;
  
  			var liveEnemies = World.Players
  				.Where(p => Player != p && Player.Stances[p] == Stance.Enemy && p.WinState == WinState.Undefined);
  
  			if (!liveEnemies.Any())
  				return null;
  
  			var leastLikedEnemies = liveEnemies
  				.GroupBy(e => aggro[e].Aggro)
  				.MaxByOrDefault(g => g.Key);
  
  			var enemy = (leastLikedEnemies != null) ?
  				leastLikedEnemies.Random(Random) : liveEnemies.FirstOrDefault();
  
  			// Pick something worth attacking owned by that player
  			var target = World.Actors
  				.Where(a => a.Owner == enemy && a.HasTrait<IOccupySpace>())
  				.ClosestTo(World.Map.CenterOfCell(GetRandomBaseCenter()));
  
  			if (target == null)
  			{
  				/* Assume that "enemy" has nothing. Cool off on attacks. */
  				aggro[enemy].Aggro = aggro[enemy].Aggro / 2 - 1;
  				Log.Write("debug", "Bot {0} couldn't find target for player {1}", Player.ClientIndex, enemy.ClientIndex);
  
  				return null;
  			}
  
  			// Bump the aggro slightly to avoid changing our mind
  			if (leastLikedEnemies.Count() > 1)
  				aggro[enemy].Aggro++;
  
  			return target;
  		}
  
  		internal Actor FindClosestEnemy(WPos pos)
  		{
  			var allEnemyUnits = World.Actors
  				.Where(unit => Player.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait<Husk>() &&
  					unit.HasTrait<ITargetable>());
  
  			return allEnemyUnits.ClosestTo(pos);
  		}
  
  		internal Actor FindClosestEnemy(WPos pos, WRange radius)
  		{
  			var enemyUnits = World.FindActorsInCircle(pos, radius)
  				.Where(unit => Player.Stances[unit.Owner] == Stance.Enemy &&
  					!unit.HasTrait<Husk>() && unit.HasTrait<ITargetable>());
  
  			return enemyUnits.ClosestTo(pos);
  		}
  
  		List<Actor> FindEnemyConstructionYards()
  		{
  			return World.Actors.Where(a => Player.Stances[a.Owner] == Stance.Enemy && !a.IsDead
  				&& a.HasTrait<BaseBuilding>() && !a.HasTrait<Mobile>()).ToList();
  		}
  
  		void CleanSquads()
  		{
  			squads.RemoveAll(s => !s.IsValid);
  			foreach (var s in squads)
  				s.Units.RemoveAll(a => a.IsDead || a.Owner != Player);
  		}
  
  		// Use of this function requires that one squad of this type. Hence it is a piece of shit
  		Squad GetSquadOfType(SquadType type)
  		{
  			return squads.FirstOrDefault(s => s.Type == type);
  		}
  
  		Squad RegisterNewSquad(SquadType type, Actor target = null)
  		{
  			var ret = new Squad(this, type, target);
  			squads.Add(ret);
  			return ret;
  		}
  
  		int assignRolesTicks = 0;
  		int rushTicks = 0;
  		int attackForceTicks = 0;
  		int minAttackForceDelayTicks = 0;
  
  		void AssignRolesToIdleUnits(Actor self)
  		{
  			CleanSquads();
  			activeUnits.RemoveAll(a => a.IsDead || a.Owner != Player);
  			unitsHangingAroundTheBase.RemoveAll(a => a.IsDead || a.Owner != Player);
  
  			if (--rushTicks <= 0)
  			{
  				rushTicks = Info.RushInterval;
  				TryToRushAttack();
  			}
  
  			if (--attackForceTicks <= 0)
  			{
  				attackForceTicks = Info.AttackForceInterval;
  				foreach (var s in squads)
  					s.Update();
  			}
  
  			if (--assignRolesTicks > 0)
  				return;
  
  			assignRolesTicks = Info.AssignRolesInterval;
  
  			GiveOrdersToIdleHarvesters();
  			FindNewUnits(self);
  			if (--minAttackForceDelayTicks <= 0)
  			{
  				minAttackForceDelayTicks = Info.MinimumAttackForceDelay;
  				CreateAttackForce();
  			}
  
  			FindAndDeployBackupMcv(self);
  		}
  
  		void GiveOrdersToIdleHarvesters()
  		{
  			// Find idle harvesters and give them orders:
  			foreach (var a in activeUnits)
  			{
  				var harv = a.TraitOrDefault<Harvester>();
  				if (harv == null)
  					continue;
  
  				if (!a.IsIdle)
  				{
  					var act = a.GetCurrentActivity();
  
  					// A Wait activity is technically idle:
  					if ((act.GetType() != typeof(Wait)) &&
  						(act.NextActivity == null || act.NextActivity.GetType() != typeof(FindResources)))
  						continue;
  				}
  
  				if (!harv.IsEmpty)
  					continue;
  
  				// Tell the idle harvester to quit slacking:
  				World.IssueOrder(new Order("Harvest", a, false));
  			}
  		}
  
  		void FindNewUnits(Actor self)
  		{
  			var newUnits = self.World.ActorsWithTrait<IPositionable>()
  				.Where(a => a.Actor.Owner == Player && !a.Actor.HasTrait<BaseBuilding>()
  					&& !activeUnits.Contains(a.Actor))
  				.Select(a => a.Actor);
  
  			foreach (var a in newUnits)
  			{
  				if (a.HasTrait<Harvester>())
  					World.IssueOrder(new Order("Harvest", a, false));
  				else
  					unitsHangingAroundTheBase.Add(a);
  
  				if (a.HasTrait<Aircraft>() && a.HasTrait<AttackBase>())
  				{
  					var air = GetSquadOfType(SquadType.Air);
  					if (air == null)
  						air = RegisterNewSquad(SquadType.Air);
  
  					air.Units.Add(a);
  				}
  
  				activeUnits.Add(a);
  			}
  		}
  
  		void CreateAttackForce()
  		{
  			// Create an attack force when we have enough units around our base.
  			// (don't bother leaving any behind for defense)
  			var randomizedSquadSize = Info.SquadSize + Random.Next(Info.SquadSizeRandomBonus);
  
  			if (unitsHangingAroundTheBase.Count >= randomizedSquadSize)
  			{
  				var attackForce = RegisterNewSquad(SquadType.Assault);
  
  				foreach (var a in unitsHangingAroundTheBase)
  					if (!a.HasTrait<Aircraft>())
  						attackForce.Units.Add(a);
  
  				unitsHangingAroundTheBase.Clear();
  			}
  		}
  
  		void TryToRushAttack()
  		{
  			var allEnemyBaseBuilder = FindEnemyConstructionYards();
  			var ownUnits = activeUnits
  				.Where(unit => unit.HasTrait<AttackBase>() && !unit.HasTrait<Aircraft>() && unit.IsIdle).ToList();
  
  			if (!allEnemyBaseBuilder.Any() || (ownUnits.Count < Info.SquadSize))
  				return;
  
  			foreach (var b in allEnemyBaseBuilder)
  			{
  				var enemies = World.FindActorsInCircle(b.CenterPosition, WRange.FromCells(Info.RushAttackScanRadius))
  					.Where(unit => Player.Stances[unit.Owner] == Stance.Enemy && unit.HasTrait<AttackBase>()).ToList();
  
  				if (rushFuzzy.CanAttack(ownUnits, enemies))
  				{
  					var target = enemies.Any() ? enemies.Random(Random) : b;
  					var rush = GetSquadOfType(SquadType.Rush);
  					if (rush == null)
  						rush = RegisterNewSquad(SquadType.Rush, target);
  
  					foreach (var a3 in ownUnits)
  						rush.Units.Add(a3);
  
  					return;
  				}
  			}
  		}
  
  		void ProtectOwn(Actor attacker)
  		{
  			var protectSq = GetSquadOfType(SquadType.Protection);
  			if (protectSq == null)
  				protectSq = RegisterNewSquad(SquadType.Protection, attacker);
  
  			if (!protectSq.IsTargetValid)
  				protectSq.TargetActor = attacker;
  
  			if (!protectSq.IsValid)
  			{
  				var ownUnits = World.FindActorsInCircle(World.Map.CenterOfCell(GetRandomBaseCenter()), WRange.FromCells(Info.ProtectUnitScanRadius))
  					.Where(unit => unit.Owner == Player && !unit.HasTrait<Building>()
  						&& unit.HasTrait<AttackBase>());
  
  				foreach (var a in ownUnits)
  					protectSq.Units.Add(a);
  			}
  		}
  
  		bool IsRallyPointValid(CPos x, BuildingInfo info)
  		{
  			return info != null && World.IsCellBuildable(x, info);
  		}
  
  		void SetRallyPointsForNewProductionBuildings(Actor self)
  		{
  			var buildings = self.World.ActorsWithTrait<RallyPoint>()
  				.Where(rp => rp.Actor.Owner == Player &&
  					!IsRallyPointValid(rp.Trait.Location, rp.Actor.Info.Traits.GetOrDefault<BuildingInfo>())).ToArray();
  
  			foreach (var a in buildings)
  				World.IssueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor), SuppressVisualFeedback = true });
  		}
  
  		// Won't work for shipyards...
  		CPos ChooseRallyLocationNear(Actor producer)
  		{
  			var possibleRallyPoints = Map.FindTilesInCircle(producer.Location, Info.RallyPointScanRadius)
  				.Where(c => IsRallyPointValid(c, producer.Info.Traits.GetOrDefault<BuildingInfo>()));
  
  			if (!possibleRallyPoints.Any())
  			{
  				BotDebug("Bot Bug: No possible rallypoint near {0}", producer.Location);
  				return producer.Location;
  			}
  
  			return possibleRallyPoints.Random(Random);
  		}
  
  		void InitializeBase(Actor self)
  		{
  			// Find and deploy our mcv
  			var mcv = self.World.Actors
  				.FirstOrDefault(a => a.Owner == Player && a.HasTrait<BaseBuilding>());
  
  			if (mcv != null)
  			{
  				initialBaseCenter = mcv.Location;
  				defenseCenter = mcv.Location;
  
  				// Don't transform the mcv if it is a fact
  				// HACK: This needs to query against MCVs directly
  				if (mcv.HasTrait<Mobile>())
  
gt: 					World.IssueOrder(new Order("DeployTransform", mcv, false));