|
Britbot
|
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 }
1.7.6.1