public void UpdateZoneEvaluations(ContextInfo context)
 {
 }
        public IList<Task> DefineTasks(ContextInfo context)
        {
            if(_tasks == null)
                _tasks = context.Zones.Select(z => new Task { AssociatedZone = z }).ToList();

            foreach(var task in _tasks.AsParallel())
            {
                var zoneInfo = task.AssociatedZone;
                var zone = zoneInfo.Zone;

                task.Type = (zone.OwnerId == context.MyTeamId ? TaskType.Defend : TaskType.Attack);

                Func<Point, int> nbrTurnsToReachZone = p => (int)Math.Ceiling(Math.Max(0, p.DistanceTo(zone.Center) - Zone.Radius) / GameContext.MaxMoveDistance);

                var myDrones = context.MyDrones
                    .Select(d => new DroneWithDistance
                    {
                        Drone = d.Drone,
                        NbrTurns = nbrTurnsToReachZone(d.Drone.Position)
                    })
                    .OrderBy(d => d.NbrTurns)
                    .ToArray();

                var enemyDrones = context.EnemyDrones
                    .Select(d => new DroneWithDistance
                    {
                        Drone = d.Drone,
                        NbrTurns = nbrTurnsToReachZone(d.Drone.Position),
                        TaskType = d.TaskType,
                        AssociatedZone = d.AssociatedZone
                    })
                    .ToArray();

                // calculate nbr of required drones & gainable points
                if(task.Type == TaskType.Attack)
                {
                    var defendingDrones = enemyDrones
                        .Where(d => d.Drone.TeamId == zone.OwnerId)
                        .Where(d => d.NbrTurns <= surroundingDistance || (d.TaskType == TaskType.Defend && d.AssociatedZone == zone))
                        .ToArray();

                    // how much drones do I need to capture this zone?
                    int nbrTurnsToSucceed;
                    int nbrRequiredDrones = this.CalculateRequiredDronesForAttack(zone.Id, myDrones, defendingDrones, out nbrTurnsToSucceed);
                    _log.WriteLine("Req={0}, Turns={1}", nbrRequiredDrones, nbrTurnsToSucceed);

                    // if they are other attacking squads, how much drones do I need to make sure I can recapture the zone if necessary?
                    var otherAttackingDroneSquads = enemyDrones
                        .Where(d => d.Drone.TeamId != zone.OwnerId)
                        .Where(d => d.NbrTurns <= surroundingDistance || (d.TaskType == TaskType.Attack && d.AssociatedZone == zone))
                        .ToLookup(d => d.Drone.TeamId);

                    foreach(var enemySquad in otherAttackingDroneSquads)
                    {
                        int nbrTurnsToSucceed2;
                        int nbrRequiredDrones2 = this.CalculateRequiredDronesForAttack(zone.Id, myDrones, enemySquad, out nbrTurnsToSucceed2);
                        _log.WriteLine("Req={0}, Turns={1}", nbrRequiredDrones2, nbrTurnsToSucceed2);
                        if(nbrTurnsToSucceed2 == nbrTurnsToSucceed)
                        {
                            if(nbrRequiredDrones2 > nbrRequiredDrones)
                                nbrRequiredDrones = nbrRequiredDrones2;
                        }
                        else if(nbrTurnsToSucceed2 > nbrTurnsToSucceed)
                        {
                            nbrTurnsToSucceed = nbrTurnsToSucceed2;
                            nbrRequiredDrones = nbrRequiredDrones2;
                        }
                    }

                    task.NbrRequiredDrones = nbrRequiredDrones;
                    task.AverageGainablePoints = (nbrTurnsConsidered - nbrTurnsToSucceed) / (double)nbrTurnsConsidered;
                }
                else
                {
                    int? nbrTurnsToFail = null;
                    int? nbrRequiredDrones = null;

                    // how much drones do I need to defend the zone against each attacking squad?
                    var attackingDroneSquads = enemyDrones
                        .Where(d => d.NbrTurns <= surroundingDistance || (d.TaskType == TaskType.Attack && d.AssociatedZone == zone))
                        .ToLookup(d => d.Drone.TeamId);

                    foreach(var enemySquad in attackingDroneSquads)
                    {
                        int nbrTurnsToFail2;
                        int nbrRequiredDrones2 = this.CalculateRequiredDronesForDefense(zone.Id, myDrones, enemySquad, out nbrTurnsToFail2);
                        _log.WriteLine("Req={0}, Turns={1}", nbrRequiredDrones2, nbrTurnsToFail2);
                        if(!nbrTurnsToFail.HasValue || nbrTurnsToFail2 < nbrTurnsToFail)
                        {
                            nbrTurnsToFail = nbrTurnsToFail2;
                            nbrRequiredDrones = nbrRequiredDrones2;
                        }
                        else if(nbrTurnsToFail2 == nbrTurnsToFail)
                        {
                            if(nbrRequiredDrones2 > nbrRequiredDrones)
                                nbrRequiredDrones = nbrRequiredDrones2;
                        }
                    }

                    task.NbrRequiredDrones = nbrRequiredDrones ?? 0;
                    task.AverageGainablePoints = (nbrTurnsToFail ?? nbrTurnsConsidered) / (double)nbrTurnsConsidered;
                }
            }

            return _tasks;
        }
        public void AllocateDronesToTasks(ContextInfo context, IList<Task> tasks)
        {
            // calculate importance of attack vs defense
            var ownedZonesRatio = context.Context.Zones.Count(z => z.OwnerId == context.MyTeamId) / (double)context.Zones.Length;
            var necessaryZoneRatio = 1.0 / context.Context.Teams.Count;
            Level defenseImportance = Level.Medium, attackImportance = Level.Medium;

            if(ownedZonesRatio > 0.8 * necessaryZoneRatio)
            {
                defenseImportance = Level.High;
                attackImportance = Level.Low;
            }
            else if(ownedZonesRatio < 0.4 * necessaryZoneRatio)
            {
                defenseImportance = Level.Low;
                attackImportance = Level.High;
            }
            var taskImportances = tasks.ToDictionary(task => task, task => task.Type == TaskType.Attack ? attackImportance : defenseImportance);

            // calculate tasks priorities
            var taskPriorities = tasks.ToDictionary(task => task, task => 0
                + 4.0 * task.AverageGainablePoints
                + 2.0 * (int)taskImportances[task]
                + 0.5 * (int)task.AssociatedZone.StrategicValue
                - 1.0 * task.NbrRequiredDrones);

            foreach(var task in tasks)
            {
                _log.WriteLine("{0}: {1} Priority={2:0.00} Pts={3:0.00} Req={4} Imp={5} Strat={6}",
                    task.AssociatedZone.Zone.Id, task.Type, taskPriorities[task], task.AverageGainablePoints, task.NbrRequiredDrones, taskImportances[task], task.AssociatedZone.StrategicValue);
            }

            // alllocate drones to tasks
            var availableDrones = context.MyDrones.ToList();
            foreach(var drone in availableDrones)
                drone.CurrentTask = null;

            var sortedTasks = tasks.OrderByDescending(t => taskPriorities[t]).ToArray();
            for(int i = 0; i < sortedTasks.Length && availableDrones.Any(); i++)
            {
                var task = sortedTasks[i];
                if(availableDrones.Count < task.NbrRequiredDrones)
                {
                    // there are not enough available drones to handle this task
                    // maybe we should skip it and concentrate on the next task
                    if(i < sortedTasks.Length - 1)
                    {
                        var nextTask = sortedTasks[i + 1];
                        if(availableDrones.Count >= nextTask.NbrRequiredDrones)
                            continue;
                    }
                }

                int nbrAllocatedDrones = 0;
                while(nbrAllocatedDrones < task.NbrRequiredDrones)
                {
                    if(!availableDrones.Any())
                        break;

                    // Pick the drone which is closest to the task target)
                    var candidate = availableDrones.MinBy(d => d.Drone.Position.DistanceTo(task.AssociatedZone.Zone.Center));
                    availableDrones.Remove(candidate);
                    candidate.CurrentTask = task;
                    nbrAllocatedDrones++;
                }
            }

            if(availableDrones.Any()) // some drones were not assigned => do a second pass
            {
                for(int i = 0; i < sortedTasks.Length && availableDrones.Any(); i++)
                {
                    var task = sortedTasks[i];

                    // Pick the drone which is closest to the task target)
                    var candidate = availableDrones.MinBy(d => d.Drone.Position.DistanceTo(task.AssociatedZone.Zone.Center));
                    availableDrones.Remove(candidate);
                    candidate.CurrentTask = task;
                }
            }
        }
        public void AllocateDronesToTasks(ContextInfo context, IList<Task> tasks)
        {
            // how many zones do I need?
            var nbrZonesToTarget = (int)Math.Ceiling(1 + context.Zones.Length / (double)context.Context.Teams.Count);
            nbrZonesToTarget++;

            // get zones to consider
            ZoneInfo[] zonesToTarget;
            var myZones = context.Zones.Where(z => z.Zone.OwnerId == context.MyTeamId).ToArray();
            if(myZones.Any())
            {
                var medianPoint = HelperExtensions.GetMedianPoint(myZones.Select(z => z.Zone.Center).ToArray());
                zonesToTarget = context.Zones.OrderBy(z => medianPoint.DistanceTo(z.Zone.Center)).Take(nbrZonesToTarget).ToArray();
            }
            else
            {
                zonesToTarget = context.Zones;
            }

            _log.WriteLine("nbrZonesToTarget={0} | zonesToTarget={1}",
                nbrZonesToTarget, string.Join(",", zonesToTarget.Select(z => z.Zone.Id)));

            foreach(var task in tasks.OrderBy(t => t.AssociatedZone.Zone.Id))
                _log.WriteLine("{0}: {1} Pts={2:0.00} Req={3}",
                    task.AssociatedZone.Zone.Id, task.Type, task.AverageGainablePoints, task.NbrRequiredDrones);

            // alllocate drones to tasks
            var availableDrones = context.MyDrones.ToList();
            foreach(var drone in availableDrones)
                drone.CurrentTask = null;

            var sortedTasks = tasks
                .Where(t => zonesToTarget.Contains(t.AssociatedZone))
                .OrderByDescending(t => t.NbrRequiredDrones == 0 ? int.MaxValue : t.AverageGainablePoints / t.NbrRequiredDrones)
                .ToArray();
            int nbrAssignedTasks = 0;
            for(int i = 0; i < sortedTasks.Length && nbrAssignedTasks < nbrZonesToTarget && availableDrones.Any(); i++)
            {
                var task = sortedTasks[i];
                if(availableDrones.Count() < task.NbrRequiredDrones)
                    continue;

                for(int nbrAllocatedDrones = 0; nbrAllocatedDrones < task.NbrRequiredDrones; nbrAllocatedDrones++)
                {
                    // Pick the drone which is closest to the task target)
                    var candidate = availableDrones.MinBy(d => d.Drone.Position.DistanceTo(task.AssociatedZone.Zone.Center));
                    availableDrones.Remove(candidate);
                    candidate.CurrentTask = task;
                }
                nbrAssignedTasks++;
            }
        }
        public void SetDroneDestinations(ContextInfo context)
        {
            var myZones = context.Zones.Where(z => z.Zone.OwnerId == context.MyTeamId).ToArray();
            var medianPoint = new Point(GameContext.FieldWidth / 2, GameContext.FieldHeight / 2);
            if(myZones.Any())
                medianPoint = HelperExtensions.GetMedianPoint(myZones.Select(z => z.Zone.Center).ToArray());

            foreach(var droneInfo in context.MyDrones)
            {
                var task = droneInfo.CurrentTask;
                if(task != null)
                {
                    if(task.Type == TaskType.Defend && task.AssociatedZone.Zone.Contains(droneInfo.Drone.Position))
                        droneInfo.Destination = droneInfo.Drone.Position; // no need to move
                    else
                        droneInfo.Destination = droneInfo.Drone.Position.GetZoneBorderPoint(task.AssociatedZone.Zone);
                }
                else
                {
                    // send the drone on patrol
                    droneInfo.Destination = medianPoint;
                }
            }
        }
        public void GuessEnemyDronesActivity(ContextInfo context)
        {
            var zones = context.Context.Zones;
            var zoneCenters = zones.Select(z => z.Center).ToArray();
            var zoneDistances = zones.Select(z => new ZoneWithDistance { Zone = z }).ToArray();

            foreach(var drone in context.EnemyDrones.AsParallel())
            {
                // based on the drone last movement, try to identify the zone it's trying to reach
                var position = drone.Drone.Position;
                var previousPosition = drone.Drone.PreviousPosition;

                if(position.DistanceTo(previousPosition) < 2) // this drone didn't move. Is it already inside a zone?
                {
                    drone.AssociatedZone = zones.FirstOrDefault(z => z.Contains(position));
                }
                else
                {
                    var distances = HelperExtensions.RespectiveDistancesToLine(zoneCenters, previousPosition, position);
                    for(int i = 0; i < zoneCenters.Length; i++)
                        zoneDistances[i].Distance = distances[i];

                    var possibleTargets = zoneDistances
                        .Where(zd => zd.Distance < 1.5 * Zone.Radius)
                        .Where(zd => MathHelper.DotProduct(previousPosition, position, previousPosition, zd.Zone.Center) > 0) // ignore zones behind the drone
                        .Select(zd => zd.Zone)
                        .ToArray();

                    if(possibleTargets.Any())
                    {
                        drone.AssociatedZone = possibleTargets.MinBy(z => z.Center.DistanceTo(position));
                    }
                    else
                    {
                        drone.AssociatedZone = null;

                        _log.WriteLine("*** no targets found for Drone {0}/{1}", drone.Drone.TeamId, drone.Drone.Id);
                        _log.WriteLine("{0},{1} -> {2},{3}", previousPosition.X, previousPosition.Y, position.X, position.Y);

                        for(int i = 0; i < zoneCenters.Length; i++)
                            _log.WriteLine("{0}: {1:0.00}", i, distances[i]);
                        _log.WriteLine("***");
                    }
                }

                if(drone.AssociatedZone == null)
                    drone.TaskType = TaskType.Unknown;
                else
                    drone.TaskType = (drone.AssociatedZone.OwnerId == drone.Drone.TeamId ? TaskType.Defend : TaskType.Attack);
            }

            foreach(var drone in context.EnemyDrones.OrderBy(d => d.Drone.TeamId).ThenBy(d => d.Drone.Id))
                _log.WriteLine("{0}/{1}: {2} {3}",
                    drone.Drone.TeamId, drone.Drone.Id, drone.TaskType, drone.AssociatedZone == null ? "/" : drone.AssociatedZone.Id.ToString());
        }
        public void Initialize(GameContext context)
        {
            var errorlog = Console.Error;
            var dummyLog = new StreamWriter(Stream.Null);

            _droneActivityObserver = new BasicDroneActivityObserver(dummyLog);
            _zoneEvaluator = new BasicZoneEvaluator(dummyLog);
            _taskOrganizer = new OneTaskPerZoneOrganizer(dummyLog);
            _droneAllocator = (context.Teams.Count > 2 ? (IDroneAllocator)new FocusedDroneAllocator(dummyLog) : (IDroneAllocator)new PriorityBasedDroneAllocator(dummyLog));
            _droneCommander = new BasicDroneCommander(dummyLog);

            _contextInfo = new ContextInfo(this.TeamId, context);
            _contextInfo.Zones = context.Zones.Select(z => new ZoneInfo(z)).ToArray();
            _contextInfo.MyDrones = context.GetDronesOfTeam(this.TeamId).Select(d => new MyDroneInfo(d)).ToArray();
            _contextInfo.EnemyDrones = context.GetDronesOfOtherTeams(this.TeamId).Select(d => new EnemyDroneInfo(d)).ToArray();

            _zoneEvaluator.GiveInitialZoneEvaluations(_contextInfo.Zones);
        }
 public void UpdateZoneEvaluations(ContextInfo context)
 {
     // do nothing
 }