Britbot
Group.cs
Go to the documentation of this file.
00001 #region #Usings
00002 
00003 using System;
00004 using System.Collections.Generic;
00005 using System.Collections.ObjectModel;
00006 using System.Linq;
00007 using System.Threading;
00008 using Pirates;
00009 
00010 #endregion
00011 
00012 namespace Britbot
00013 {
00017     public class Group
00018     {
00019         #region Fields & Properies
00020 
00025         public readonly int Id;
00026 
00030         private int _formTurnsAttempt;
00031 
00035         private bool _hasChanged;
00036 
00040         public HeadingVector Heading { get; private set; }
00041 
00045         public ObservableCollection<int> Pirates { get; private set; }
00046 
00050         public ITarget Target { get; private set; }
00051 
00055         public List<Score> Priorities { get; private set; }
00056 
00060         public static int GroupCounter { get; private set; }
00061 
00065         public Dictionary<int, Location> FormOrders { get; private set; }
00066 
00067         
00068 
00072         public int DistanceFromTarget
00073         {
00074             get
00075             {
00076                 return Bot.Game.Distance(Bot.Game.AllMyPirates()[this.Pirates[0]].Loc, this.Target.GetLocation());
00077             }
00078         }
00079 
00080         #endregion
00081 
00082         #region Constructors & Initializers
00083 
00088         public Group(int[] piratesId)
00089             : this()
00090         {
00091             foreach (int index in piratesId)
00092                 this.AddPirate(index);
00093 
00094             //generate forming (getting into structure) instructions
00095             this.GenerateFormationInstructions();
00096         }
00097 
00101         public Group()
00102         {
00103             this.Pirates = new ObservableCollection<int>();
00104 
00105             this.Pirates.CollectionChanged += delegate
00106             {
00107                 this._hasChanged = true;
00108                 Logger.Write("Update Registered at group " + this.Id);
00109             };
00110 
00111             this.Heading = new HeadingVector(1, 0);
00112             this.Priorities = new List<Score>();
00113 
00114             //get id and update counter
00115             this.Id = Group.GroupCounter++;
00116 
00117             Logger.Write(string.Format("===================GROUP {0}===================", this.Id));
00118         }
00119 
00120         //[Obsolete("Please use the constructor that takes spesific pirates")]
00121         public Group(int index, int amount)
00122             : this()
00123         {
00124             //Add pirates
00125             for (; amount > 0; amount--)
00126             {
00127                 this.Pirates.Add(index + amount - 1);
00128             }
00129 
00130             //generate forming (getting into structure) instructions
00131             this.GenerateFormationInstructions();
00132         }
00133 
00134         #endregion
00135 
00136 
00141         public override string ToString()
00142         {
00143             return "Group - id: " + this.Id + " pirate count: " + this.Pirates.Count + "location: " +
00144                    this.FindCenter(true)
00145                    + " heading: " + this.Heading;
00146         }
00147 
00153         public void SetTarget(ITarget target)
00154         {
00155             //log it
00156             Logger.Write(string.Format("Group {0} assinged to {1}", this, target.GetDescription()), true);
00157 
00158             //if it isn't the same target as before update and reset heading
00159             if (this.Target == null)
00160             {
00161                 this.Target = target;
00162                 this.Heading.SetCoordinates();
00163                 //tell the target it got assigned
00164                 this.Target.TargetAssignmentEvent();
00165             }
00166             else if (this.Target.Equals(target))
00167             {
00168                 //tell the previous target it was dessigned
00169                 this.Target.TargetDeAssignmentEvent();
00170 
00171                 //update
00172                 this.Target = target;
00173                 this.Heading.SetCoordinates();
00174 
00175                 //tell new target it got assigned
00176                 this.Target.TargetAssignmentEvent();
00177             }
00178         }
00179 
00184         public Location GetLocation()
00185         {
00186             //assigning X and Y to hold the sum
00187             int x = 0;
00188             int y = 0;
00189 
00190             if (this.Pirates == null)
00191                 this.Pirates = new ObservableCollection<int>();
00192 
00193             foreach (int pirate in this.Pirates)
00194             {
00195                 x += Bot.Game.GetMyPirate(pirate).Loc.Col;
00196                 y += Bot.Game.GetMyPirate(pirate).Loc.Row;
00197             }
00198 
00199             try
00200             {
00201                 return new Location(y / this.Pirates.Count, x / this.Pirates.Count);
00202             }
00203             catch (DivideByZeroException) // altough pirates count shouldn't be 0, just in case
00204             {
00205                 return new Location(0, 0);
00206             }
00207         }
00208 
00213         public int FightCount()
00214         {
00215             int count = 0;
00216             foreach (int pirate in this.Pirates)
00217             {
00218                 if (!Bot.Game.isCapturing(Bot.Game.GetMyPirate(pirate)))
00219                     count++;
00220             }
00221             return count;
00222         }
00223 
00229         public IEnumerable<KeyValuePair<Pirate, Direction>> GetGroupMoves(CancellationToken cancellationToken)
00230         {
00231             Logger.BeginTime("GetGroupMoves at group " + this.Id);
00232             //inital path finding for this group
00233             Logger.BeginTime("UpdateMap");
00234             Navigator.UpdateMap(this);
00235             Logger.StopTime("UpdateMap");
00236 
00237             //Check if the group is formed into structure. If not, get the moves to get into the structure
00238             if (!this.IsFormed())
00239             {
00240                 this.Heading = new HeadingVector();
00241                 //return for each of our pirate its move
00242                 foreach (KeyValuePair<Pirate, Direction> keyValuePair in this.GetStructureMoves(cancellationToken))
00243                     yield return keyValuePair;
00244             }
00245             else //if the group is in structure and ready to attack
00246             {
00247                 if (this.Target == null)
00248                     this.Target = new NoTarget();
00249 
00250                 Direction master = this.Target.GetDirection(this);
00251 
00252                 //Convert the list of pirate indexes we have into a list of pirates
00253                 List<Pirate> myPirates = this.Pirates.ToList().ConvertAll(p => Bot.Game.GetMyPirate(p));
00254 
00255                 //Proceed to moving to the target unless it's a NoTarget - then we stay in place
00256                 if (this.Target.GetTargetType() != TargetType.NoTarget)
00257                 {
00258                     //sort the pirates in a way the closest ones to the target will travel first in order to avoid collisions
00259 
00260                     //return for each pirate the pirate and its direction
00261                     foreach (Pirate myPirate in myPirates)
00262                         yield return new KeyValuePair<Pirate, Direction>(myPirate, master);
00263 
00264                     //update heading
00265                     this.Heading.adjustHeading(master);
00266                 }
00267                 else //stay if we are on target
00268                 {
00269                     //return Direction.NOTHING for all pirates we got
00270                     foreach (Pirate myPirate in myPirates)
00271                         yield return new KeyValuePair<Pirate, Direction>(myPirate, Direction.NOTHING);
00272                 }
00273             }
00274             Logger.StopTime("GetGroupMoves at group " + this.Id);
00275         }
00276 
00282         public double MinimalETATo(Location location)
00283         {
00284             double min = Bot.Game.GetCols() + Bot.Game.GetRows();
00285             foreach (int pirate in this.Pirates)
00286             {
00287                 if (Bot.Game.Distance(location, this.GetLocation()) < min)
00288                     min = Bot.Game.EuclidianDistanceSquared(location, this.GetLocation());
00289             }
00290             return min;
00291         }
00292 
00298         private IEnumerable<KeyValuePair<Pirate, Direction>> GetStructureMoves(CancellationToken cancellationToken)
00299         {
00300             Logger.BeginTime("GetStructureMoves");
00301             //check if we are not stuck try to get into formation for too long
00302             if (this._formTurnsAttempt > this.Pirates.Count * 1)
00303                 //if we are stuck, request new instructions. This will reset the _formTurnsAttempt counter
00304                 this.GenerateFormationInstructions();
00305 
00306             //advance the counter
00307             this._formTurnsAttempt++;
00308 
00309             //iterate over all the structure (each pirate in the group has a reserved location in the structure)
00310             foreach (KeyValuePair<int, Location> formOrder in this.FormOrders)
00311             {
00312                 //Throwing an exception if cancellation was requested.
00313                 cancellationToken.ThrowIfCancellationRequested();
00314 
00315                 //Get the actual pirate object by its ID
00316                 Pirate pete = Bot.Game.GetMyPirate(formOrder.Key);
00317 
00318                 //if the pirate is already in place, make it stay in place
00319                 if (pete.Loc.Col == formOrder.Value.Col && pete.Loc.Row == formOrder.Value.Row)
00320                 {
00321                     yield return new KeyValuePair<Pirate, Direction>(pete, Direction.NOTHING);
00322                 }
00323                 else //if the pirate need to move into position
00324                 {
00325                     //All the possible direction from the pirate to its position the the structure
00326                     List<Direction> possibleDirections = Bot.Game.GetDirections(pete, formOrder.Value);
00327 
00328                     List<Direction> filteredDirections = new List<Direction>(possibleDirections.Count);
00329 
00330                     //iterate over the possible directions
00331                     foreach (Direction t in possibleDirections)
00332                     {
00333                         //check if the direction is NOTHING - it means that the pirate is in place (double check with the previous if)
00334                         if (t == Direction.NOTHING)
00335                         {
00336                             filteredDirections.Add(t);
00337                             break;
00338                         }
00339 
00340                         //if the direction is actually passable add it to the list
00341                         if (Bot.Game.Destination(pete.Loc, t).IsActuallyPassable())
00342                             filteredDirections.Add(t);
00343                     }
00344 
00345                     //and return it
00346                     if (filteredDirections.Count == 0)
00347                         yield return new KeyValuePair<Pirate, Direction>(pete, Direction.NOTHING);
00348                     else if (Bot.Game.Distance(pete.Loc, formOrder.Value) <= 15)
00349                         yield return new KeyValuePair<Pirate, Direction>(pete, filteredDirections.First());
00350                     else if (filteredDirections.Count >= Magic.tryAlternate + 1)
00351                         yield return new KeyValuePair<Pirate, Direction>(pete, filteredDirections[Magic.tryAlternate]);
00352                     else
00353                         yield return new KeyValuePair<Pirate, Direction>(pete, filteredDirections.First());
00354                 }
00355             }
00356             Logger.StopTime("GetStructureMoves");
00357         }
00358 
00363         private double CasualtiesPercent()
00364         {
00365             if(this.Pirates.Count > 0)
00366                 return 100 * (this.Pirates.Count(p => Bot.Game.GetMyPirate(p).IsLost) / this.Pirates.Count);
00367             return 0;
00368         }
00369 
00374         private bool IsFormed(bool checkCasualties = true)
00375         {
00376             Logger.BeginTime("IsFormed at group " + this.Id);
00377 
00378             if (checkCasualties)
00379                 if (this.CasualtiesPercent() > Magic.casualtiesThresholdPercent) //if there are many casualties
00380                 {
00381                     Logger.StopTime("IsFormed at group " + this.Id);
00382                     return false;
00383                 }
00384 
00385             //the offsets from the original location.
00386             //this test assumes that the group has moves but *relatively* everyone is in place.
00387             int deltaCol = 0, deltaRow = 0;
00388 
00389             //a flag to indicate of the current 
00390             bool deltaFlag = true;
00391 
00392             //bool to flag if there is need for another formation test (see below)
00393             bool confirmUnstructured = false;
00394 
00395             //iterate over the forming instructions 
00396             foreach (KeyValuePair<int, Location> formOrder in this.FormOrders)
00397             {
00398                 //get the actual pirate from its ID
00399                 Pirate pete = Bot.Game.GetMyPirate(formOrder.Key);
00400 
00401                 if (pete != null)
00402                 {
00403                     //ignore dead pirates
00404                     if (pete.IsLost)
00405                         continue;
00406 
00407                     //we calculate the deltas for the first pirate only
00408                     if (deltaFlag)
00409                     {
00410                         deltaCol = formOrder.Value.Col - pete.Loc.Col;
00411                         deltaRow = formOrder.Value.Row - pete.Loc.Row;
00412                         deltaFlag = false;
00413 
00414                         //skip to the next pirate
00415                         continue;
00416                     }
00417 
00418                     //check if the pirate is in its right spot relatively to the group
00419                     if (pete.Loc.Col + deltaCol != formOrder.Value.Col || pete.Loc.Row + deltaRow != formOrder.Value.Row)
00420                     {
00421                         //if the pirate is not in place, proceed to the next test. 
00422                         //because there's slight chance one of the pirates got stuck for a little bit but it is still in OK location
00423                         confirmUnstructured = true;
00424                         break;
00425                     }
00426                 }
00427             }
00428 
00429             //if the group passed the previous test, return true (meaning that the group is formed)
00430             if (!confirmUnstructured)
00431             {
00432                 Logger.Write(string.Format("Group {0} is formed", this.Id));
00433                 Logger.StopTime("IsFormed at group " + this.Id);
00434                 return true;
00435             }
00436 
00437             //else, proceed to the next test
00438 
00439             //find the central pirate in the group
00440             Location pivot = this.FindCenter(true);
00441 
00442             //the group's new structure (formation)
00443             Location[][] structureFull = null;
00444 
00445             //try to get a new formation
00446             try
00447             {
00448                 structureFull = this.GenerateGroupStructure(pivot, false);
00449             }
00450             catch //if there's an exception (such as InvalidLocationException) return false
00451             {
00452                 Logger.Write(String.Format("Group {0} is not formed yet", this.Id));
00453                 Logger.StopTime("IsFormed at group " + this.Id);
00454                 return false;
00455             }
00456 
00457             //if we still failed to get a new structure for whatever reason...
00458             if (structureFull == null)
00459             {
00460                 //...return false
00461                 Logger.Write(String.Format("Group {0} is not formed yet", this.Id));
00462                 Logger.StopTime("IsFormed at group " + this.Id);
00463                 return false;
00464             }
00465 
00466             //if we got a new structure successfully...
00467             //this is the number of empty location in the group's structure
00468             int emptyCells = 0;
00469 
00470             //iterate over all the locations in the new structure
00471             for (int i = 0; i < structureFull.Length; i++)
00472             {
00473                 for (int k = 0; k < structureFull[i].Length; k++)
00474                 {
00475                     //try to find a pirate in the location
00476                     Pirate p = Bot.Game.GetPirateOn(structureFull[i][k]);
00477 
00478                     //if there's not pirate of ours on the location
00479                     if (!(p != null && p.Owner == Consts.ME))
00480                     {
00481                         //check if this is the last ring, where some empty spots are OK
00482                         if (i == structureFull.Length - 1)
00483                             //advance the counter
00484                             emptyCells++;
00485                         else
00486                         {
00487                             //quit the iterations because the group is not formed for sure
00488                             //I know goto is bad but this is a legitimate case for this (rare one!)
00489                             goto ReturnFalse;
00490                         }
00491                     }
00492                 }
00493             }
00494 
00495             //check if the empty cells is what it should be
00496             //(the structure array might be larger than the # of pirates in the group,
00497             //like if we have 4 pirates we need the 2nd ring (index 1) but but there will be on free spot)
00498             if (emptyCells == structureFull.Length - this.Pirates.Count)
00499             {
00500                 Logger.Write(String.Format("Group {0} is formed", this.Id));
00501                 Logger.StopTime("IsFormed at group " + this.Id);
00502                 return true;
00503             }
00504 
00505             ReturnFalse:
00506             //if we are still not formed, return the right answer
00507             Logger.Write(String.Format("Group {0} is not formed yet", this.Id));
00508 
00509             Logger.StopTime("IsFormed at group " + this.Id);
00510             return false;
00511         }
00512 
00517         private void GenerateFormationInstructions(Location[] structure = null)
00518         {
00519             //reset the forming attempts counter
00520             this._formTurnsAttempt = 0;
00521 
00522             //find the average location of the group (not the center pirate!)
00523             Location center = this.FindCenter(false);
00524 
00525             //if we didn't get a pre calculated structure, calculate it below
00526             if (structure == null)
00527             {
00528                 Logger.Write("Generating group structure");
00529 
00530                 //Generate location array (structure) for the group
00531                 while (true)
00532                 {
00533                     try
00534                     {
00535                         //generate the structure
00536                         structure = this.GenerateGroupStructure(center).Flatten();
00537                         break;
00538                     }
00539                     catch (InvalidLocationException)
00540                     {
00541                         //if the location is invalid (i.e. the current center requires a location outside the map)
00542                         //advance the location towards the center of the map
00543                         center = center.AdvancePivot();
00544                     }
00545                 }
00546             }
00547 
00548             Logger.Write("Generating group structure OK");
00549 
00550             //flag array to signal if a location in the structure is already taken 
00551             bool[] matchedLocations = new bool[structure.Length];
00552 
00553             //the orders to form to
00554             Dictionary<Pirate, Location> orders = new Dictionary<Pirate, Location>();
00555 
00556             //all the pirates in the group converted from their IDs
00557             List<Pirate> groupPirates = this.Pirates.Distinct().ToList().ConvertAll(p => Bot.Game.GetMyPirate(p));
00558 
00559             //sort the pirates by distance to the center (closer are first)
00560             groupPirates.Sort((b, a) => Bot.Game.Distance(a.Loc, center).CompareTo(Bot.Game.Distance(b.Loc, center)));
00561 
00562             //Match a pirate for each location in the structure
00563             foreach (Pirate pirate in groupPirates)
00564             {
00565                 Location closestLocation = null;
00566                 int minDistance = Bot.Game.GetCols() + Bot.Game.GetRows();
00567                 int matchIndex = 0;
00568 
00569                 //iterate over all the location in the structure
00570                 for (int i = 0; i < structure.Length; i++)
00571                 {
00572                     //Skip over taken spots
00573                     if (matchedLocations[i])
00574                         continue;
00575 
00576                     //find the best location in the structure, which is the closest one
00577                     if (Bot.Game.Distance(pirate.Loc, structure[i]) < minDistance)
00578                     {
00579                         minDistance = Bot.Game.Distance(pirate.Loc, structure[i]);
00580                         closestLocation = structure[i];
00581                         matchIndex = i;
00582                     }
00583                 }
00584 
00585                 //flag that this location is taken
00586                 matchedLocations[matchIndex] = true;
00587 
00588                 //add the instruction
00589                 orders.Add(pirate, closestLocation);
00590             }
00591 
00592             //sort the orders so the closest pirates are first to avoid collisions and set them to the right class property
00593             this.FormOrders = orders.OrderBy(pair => Bot.Game.Distance(pair.Key.Loc, pair.Value))
00594                 .ToDictionary(pair => pair.Key.Id, pair => pair.Value);
00595 
00596             //debug prints
00597             Logger.Write("====FORMING TO====");
00598             foreach (KeyValuePair<int, Location> formOrder in this.FormOrders)
00599             {
00600                 Logger.Write(Bot.Game.GetMyPirate(formOrder.Key) + "," + formOrder.Value);
00601             }
00602             Logger.Write("==================");
00603         }
00604 
00611         private Location[][] GenerateGroupStructure(Location pivot, bool trim = true)
00612         {
00613             //find the required ring index for this group (see proof in calculation folder in the repo)
00614             int maxRing = (int) Math.Ceiling((decimal) (this.Pirates.Count - 1) / 4);
00615 
00616             //list of location in all the rings. Note that we add one because ring # is 0-based
00617             Location[][] rings = new Location[maxRing + 1][];
00618 
00619             //generate the locations for each ring
00620             for (int i = 0; i < rings.Length; i++)
00621             {
00622                 rings[i] = Group.GenerateRingLocations(pivot, i).ToArray();
00623             }
00624 
00625             /*
00626              * trim the last ring if required. i.e. if we have 4 pirate, maxRing will be 1 and it will have 5 spots.
00627              * so if trimming was required the last ring will be trimmed to 3 (so 3 + 1 is the number of pirates in this group).
00628              */
00629             if (trim)
00630             {
00631                 int spareSpots = Group.GetStructureVolume(maxRing) - this.Pirates.Count;
00632                 rings[maxRing] = rings[maxRing].Take(rings[maxRing].Length - spareSpots).ToArray();
00633             }
00634 
00635 
00636             //return the location array
00637             return rings;
00638         }
00639 
00645         public static int GetRingCount(int pirateNum)
00646         {
00647             return (int) Math.Ceiling((decimal) (pirateNum - 1) / 4);
00648         }
00649 
00660         internal static List<Location> GenerateRingLocations(Location pivot, int ringOrdinal)
00661         {
00662             //check if the ring index is OK
00663             if (ringOrdinal < 0)
00664                 throw new InvalidRingException("Ring ordinal must be non-negative");
00665 
00666             //create a list of this ring's locations
00667             List<Location> ring = new List<Location>(ringOrdinal * 4);
00668             int a = pivot.Row;
00669             int b = pivot.Col;
00670 
00671             //this solves the equation I described in the calculations folder
00672             for (int x = a - ringOrdinal; x <= a + ringOrdinal; x++)
00673             {
00674                 //first solution
00675                 Location y1 =
00676                     new Location(x,
00677                         (int)
00678                             ((2 * b +
00679                               Math.Sqrt(4 * Math.Pow(b, 2) +
00680                                         4 * (Math.Pow(ringOrdinal - Math.Abs(a - x), 2) - Math.Pow(b, 2)))) / 2));
00681 
00682                 //if the location is nit passable, throw an expection (caught by the calling function)
00683                 if (!Bot.Game.IsInMap(y1) || !Bot.Game.IsPassable(y1))
00684                     throw new InvalidLocationException("Location is not passable!");
00685 
00686                 //add the location to the ring
00687                 ring.Add(y1);
00688 
00689                 //second solution
00690                 Location y2 =
00691                     new Location(x,
00692                         (int)
00693                             ((2 * b -
00694                               Math.Sqrt(4 * Math.Pow(b, 2) +
00695                                         4 * (Math.Pow(ringOrdinal - Math.Abs(a - x), 2) - Math.Pow(b, 2)))) / 2));
00696 
00697                 //if the location is nit passable, throw an expection (caught by the calling function)
00698                 if (!Bot.Game.IsInMap(y2) || !Bot.Game.IsPassable(y2))
00699                     throw new InvalidLocationException("Location is not passable!");
00700 
00701                 //Check for duplicates
00702                 if (y1.Col != y2.Col || y1.Row != y2.Row)
00703                     //if the two solution are different, add the second one
00704                     ring.Add(y2);
00705             }
00706             //return the list of location of the ring
00707             return ring;
00708         }
00709 
00713         public void Update()
00714         {
00715             this.Pirates.RemoveAll(p => Bot.Game.GetMyPirate(p).IsLost);
00716 
00717             if (this._hasChanged /*|| this.Pirates.Any(p => Bot.Game.GetMyPirate(p).IsLost)*/)
00718             {
00719                 //this.Pirates.RemoveAll(p => Bot.Game.GetMyPirate(p).IsLost);
00720                 this.GenerateFormationInstructions();
00721                 this._hasChanged = false;
00722             }
00723         }
00724 
00733         public Location FindCenter(bool forcePirate)
00734         {
00735             if (this.Pirates.Count == 0)
00736                 return new Location(0, 0);
00737 
00738             //convert all the pirates indexes to actual pirate list
00739             List<Pirate> myPirates = this.Pirates.ToList().ConvertAll(p => Bot.Game.GetMyPirate(p));
00740 
00741             //init the average location
00742             Location averageLocation = new Location(0, 0);
00743 
00744             //iterate over all the pirates and add their locations to the sum
00745             foreach (Pirate myPirate in myPirates)
00746             {
00747                 if (myPirate == null)
00748                     continue;
00749 
00750                 averageLocation.Row += myPirate.Loc.Row;
00751                 averageLocation.Col += myPirate.Loc.Col;
00752             }
00753 
00754             //calc the average
00755             averageLocation.Col /= myPirates.Count;
00756             averageLocation.Row /= myPirates.Count;
00757 
00758             //if the caller strictly wants the central pirate of the group, 
00759             //calcute it by assuming that the center pirate is the closest to the average location
00760             if (forcePirate)
00761             {
00762                 int minDistance = Bot.Game.GetCols() + Bot.Game.GetCols();
00763                 Pirate pete = null;
00764 
00765                 //iterate over all the pirate and find the one with the minimun distance to the average location
00766                 foreach (Pirate pirate in myPirates)
00767                 {
00768                     if (pirate.IsLost)
00769                         continue;
00770 
00771                     int currDistance = Bot.Game.Distance(averageLocation, pirate.Loc);
00772                     if (currDistance < minDistance)
00773                     {
00774                         minDistance = currDistance;
00775                         pete = pirate;
00776                     }
00777                 }
00778 
00779                 //set the returned location to the central pirate location
00780                 if (pete != null)
00781                     averageLocation = pete.Loc;
00782             }
00783 
00784             //return the location
00785             return averageLocation;
00786         }
00787 
00792         public int LiveCount()
00793         {
00794             if(this.Pirates != null)
00795                 return this.Pirates.ToList().ConvertAll(p => Bot.Game.GetMyPirate(p)).Count(p => !p.IsLost);
00796             return 0;
00797         }
00798 
00804         public void CalcPriorities(CancellationToken cancellationToken)
00805         {
00806             Logger.BeginTime("CalcPriorities");
00807             //init some lists
00808             List<ITarget> priorityList = new List<ITarget>();
00809             List<Score> scores = new List<Score>();
00810 
00811             //Add all targets to the list
00812 
00813             //check if we need to chaise ships, if so add them to calculation
00814             if (Enemy.ShouldWeTryToCatchEnemyShips())
00815                 priorityList.AddRange(Enemy.Groups);
00816             priorityList.AddRange(SmartIsland.IslandList);
00817 
00818             //Add a score for each target we got
00819             foreach (ITarget target in priorityList)
00820             {
00821                 //Throwing an exception if cancellation was requested.
00822                 cancellationToken.ThrowIfCancellationRequested();
00823 
00824                 //calculate the score for this specific target
00825                 Score newScore = target.GetScore(this);
00826                 //check if score wasn't null, meaning if target was disqualified
00827                 if (newScore != null)
00828                     scores.Add(newScore);
00829             }
00830 
00831             //set it to this instance of Group
00832             this.Priorities = scores;
00833 
00834             //check if priorities empty, if so add NoTarget To prevent dimension problem
00835             if (this.Priorities.Count == 0)
00836             {
00837                 //create a NoTarget
00838                 NoTarget noTarget = new NoTarget();
00839                 this.Priorities.Add(noTarget.GetScore(this));
00840             }
00841 
00842             //limit outselfs to so and so targets
00843             //first sort by something (meanwhile distance)
00844             this.Priorities = this.Priorities.OrderBy(score => score.Eta).ToList();
00845             //throw away all but CalcMaxPrioritiesNum
00846             this.Priorities = this.Priorities.Take(Commander.CalcMaxPrioritiesNum()).ToList();
00847 
00848             Logger.StopTime("CalcPriorities");
00849         }
00850 
00855         public void AddPirate(int index, bool remove = true)
00856         {
00857             //make sure that the pirate is not controlled by other groups by removing it from them
00858             if(remove)
00859                 foreach (Group g in Commander.Groups)
00860                 {
00861                     //NOTE! that is correct - it does not remove *at* the index but find the right one
00862                     g.Pirates.Remove(index);
00863                 }
00864 
00865             //add the pirate to this group index
00866             this.Pirates.Add(index);
00867         }
00868 
00869         public static int GetStructureVolume(int maxRing)
00870         {
00871             //this is basic summation of arithmetic sequence, excluding the 0th ring because it's special. then we add it back.
00872             return (((2 * maxRing + 2) * maxRing) + 1);
00873         }
00874 
00879         public IEnumerable<Group> Split(int num)
00880         {
00881             int[] outboundPirates = this.Pirates.Take(num).ToArray();
00882             this.Pirates.RemoveAll(outboundPirates.Contains);
00883 
00884             for (int i = 0; i < outboundPirates.Length; i++)
00885             {
00886                 yield return new Group(new[] {outboundPirates[i]});
00887             }
00888         }
00889 
00895         public void Join(Group g, bool remove = true)
00896         {
00897             foreach (int pirate in g.Pirates.Where(p => !this.Pirates.Contains(p)))
00898                 this.AddPirate(pirate, remove);
00899 
00900             if(remove)
00901                 Commander.Groups.RemoveAll(group => group.Id == g.Id);
00902         }
00903 
00910         public static void Switch(Group bigGroup, Group smallGroup)
00911         {
00912             //if we have no heading to work with
00913             if (bigGroup.Heading.Norm() == 0)
00914                 return;
00915 
00916             //define the list of pirates of both groups
00917             List<int> pirateList = new List<int>();
00918 
00919             //add pirates of the biggroup
00920             pirateList.AddRange(bigGroup.Pirates);
00921             pirateList.AddRange(smallGroup.Pirates);
00922             Logger.Write("-------------------------------------Switch",true);
00923             Logger.Write("Biggroup: " + string.Join(", ", bigGroup.Pirates));
00924             Logger.Write("smallgroup: " + string.Join(", ", smallGroup.Pirates));
00925             Logger.Write("pirate list: " + string.Join(", ", pirateList));
00926             Logger.Write("heading: " + bigGroup.Heading);
00927 
00928 
00929             //sort array by allignment with the vector specified
00930             pirateList.Sort((p1, p2) => -1 * Navigator.ComparePirateByDirection(p1, p2, bigGroup.Heading));
00931 
00932             Logger.Write("pirate list: " + string.Join(", ", pirateList));
00933 
00934             //replace pirates
00935             bigGroup._hasChanged = true;
00936             bigGroup.Pirates = new ObservableCollection<int>(pirateList.GetRange(0, bigGroup.Pirates.Count));
00937 
00938             pirateList.RemoveRange(0, bigGroup.Pirates.Count);
00939 
00940             smallGroup._hasChanged = true;
00941             smallGroup.Pirates = new ObservableCollection<int>(pirateList);
00942 
00943             Logger.Write("Biggroup: " + string.Join(", ", bigGroup.Pirates));
00944             Logger.Write("smallgroup: " + string.Join(", ", smallGroup.Pirates));
00945         }
00946 
00954         public static bool CheckIfGroupsIntersects(Group g1, Group g2)
00955         {
00956             //first check direction stuff
00957             HeadingVector diff = HeadingVector.CalcDifference(g1.FindCenter(true), g2.FindCenter(true));
00958 
00959             //if they are not in the same direction or something
00960             if ((diff * g1.Heading < 0) || (g2.Heading * g1.Heading > 0))
00961             {
00962                 if((g1.IsFormed()) && (g2.IsFormed()))
00963                     return false;
00964             }
00965 
00966             //going over all the pirates in G1
00967             foreach (int intPirate1 in g1.Pirates)
00968             {
00969                 //convertion
00970                 Pirate pirate1 = Bot.Game.GetMyPirate(intPirate1);
00971 
00972                 //going over pirates in G2
00973                 foreach (int intPirate2 in g2.Pirates)
00974                 {
00975                     //conversion
00976                     Pirate pirate2 = Bot.Game.GetMyPirate(intPirate2);
00977 
00978                     //check if distance is small enough
00979                     if (Bot.Game.EuclidianDistanceSquared(pirate1.Loc, pirate2.Loc) <= Magic.GroupIntersectionDistance)
00980                         return true;
00981                 }
00982             }
00983 
00984             //otherwise return false
00985             return false;
00986         }
00987 
00993         public int MinDistance(Group b)
00994         {
00995             int minD = Bot.Game.GetRows() + Bot.Game.GetCols();
00996 
00997             foreach (int pA in this.Pirates)
00998             {
00999                 Pirate pat = Bot.Game.GetMyPirate(pA);
01000 
01001                 foreach (int pB in b.Pirates)
01002                 {
01003                     Pirate pete = Bot.Game.GetMyPirate(pB);
01004 
01005                     int d = Bot.Game.Distance(pat, pete);
01006 
01007                     if (d < minD)
01008                         minD = d;
01009                 }
01010             }
01011 
01012             return minD;
01013         }
01014     }
01015 }