Target FindNextResource(Actor actor, MinerTraitWrapper miner)
        {
            var towerInfo    = AIUtils.GetInfoByCommonName(Info.DeployedActorTypes, player);
            var buildingInfo = towerInfo.TraitInfo <BuildingInfo>();
            Func <CPos, bool> isValidResource = cell =>
                                                domainIndex.IsPassable(actor.Location, cell, miner.Locomotor.Info) &&
                                                Info.DeployableTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type) &&
                                                miner.Locomotor.CanStayInCell(cell) &&
                                                world.CanPlaceBuilding(cell + miner.Transforms.Info.Offset, towerInfo, buildingInfo, actor);

            var path = pathfinder.FindPath(
                PathSearch.Search(world, miner.Locomotor, actor, BlockedByActor.Stationary, isValidResource)
                .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius)
                                .Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy)
                                .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length)))
                .FromPoint(actor.Location));

            if (path.Count == 0)
            {
                return(Target.Invalid);
            }

            return(Target.FromCell(world, path[0]));
        }
        void IBotTick.BotTick(IBot bot)
        {
            if (resourceLayer == null || resourceLayer.IsResourceLayerEmpty)
            {
                return;
            }

            if (--scanForIdleMinersTicks > 0)
            {
                return;
            }

            scanForIdleMinersTicks = Info.MinimumScanDelay;

            var toRemove = miners.Keys.Where(unitCannotBeOrdered).ToList();

            foreach (var a in toRemove)
            {
                miners.Remove(a);
            }

            // TODO: Look for a more performance friendly way to update this list
            var newMiners = world.Actors.Where(a => Info.DeployableActorTypes.Contains(a.Info.Name) && a.Owner == player && !miners.ContainsKey(a));

            foreach (var a in newMiners)
            {
                miners[a] = new MinerTraitWrapper(a);
            }

            foreach (var miner in miners)
            {
                if (!miner.Key.IsIdle)
                {
                    continue;
                }

                if (Info.DeployableTerrainTypes.Contains(world.Map.GetTerrainInfo(miner.Key.Location).Type))
                {
                    bot.QueueOrder(new Order("DeployTransform", miner.Key, true));
                    continue;
                }

                // Tell the idle miner to quit slacking:
                var newSafeResourcePatch = FindNextResource(miner.Key, miner.Value);
                if (newSafeResourcePatch.Type == TargetType.Invalid)
                {
                    scanForIdleMinersTicks = Info.LastSearchFailedDelay;
                    return;
                }

                AIUtils.BotDebug("AI: Miner {0} is idle. Ordering to {1} in search for new resources.".F(miner.Key, newSafeResourcePatch));
                bot.QueueOrder(new Order("Move", miner.Key, newSafeResourcePatch, true));
            }

            // Keep the economy running before starving out.
            var unitBuilder = requestUnitProduction.FirstOrDefault(Exts.IsTraitEnabled);

            if (unitBuilder != null)
            {
                var minerInfo    = AIUtils.GetInfoByCommonName(Info.DeployableActorTypes, player);
                var miningTowers = AIUtils.CountBuildingByCommonName(Info.DeployedActorTypes, player);
                if (miningTowers < Info.MinimumDeployedActors && unitBuilder.RequestedProductionCount(bot, minerInfo.Name) == 0)
                {
                    unitBuilder.RequestUnitProduction(bot, minerInfo.Name);
                }
            }
        }