|
Britbot
|
00001 #region Usings 00002 00003 using System; 00004 using System.Collections.Generic; 00005 using System.Linq; 00006 using Pirates; 00007 00008 #endregion 00009 00010 namespace Britbot 00011 { 00015 public class EnemyGroup : ITarget 00016 { 00017 #region Static Fields & Consts 00018 00022 private static int _idCount; 00023 00024 #endregion 00025 00026 #region Fields & Properies 00027 00031 public readonly int Id; 00032 00036 private int _lastAssignmentTurn; 00037 00041 private Queue<Direction> _lastDirections; 00042 00046 private Queue<int> _lastMaxFightPower; 00047 00051 public Location PrevLoc { get; set; } 00052 00056 public List<int> EnemyPirates { get; private set; } 00057 00058 #endregion 00059 00060 #region Constructors & Initializers 00061 00065 public EnemyGroup() 00066 { 00067 this.Id = EnemyGroup._idCount++; 00068 this.EnemyPirates = new List<int>(); 00069 this.PrevLoc = new Location(0, 0); 00070 this._lastDirections = new Queue<Direction>(); 00071 this._lastMaxFightPower = new Queue<int>(); 00072 } 00073 00074 #endregion 00075 00076 #region Interface Implementations 00077 00083 public Score GetScore(Group origin) 00084 { 00085 //first check if groups direction is stable, otherwise disqualify 00086 if (this.GetHeadingSabilityCoeff() < Magic.HeadingStabilityCoeff) 00087 return null; 00088 00089 //check if the enemy group isn't in spawn - else disqualify 00090 if (!Bot.Game.IsPassable(this.GetLocation())) 00091 return null; 00092 00093 //next check if it even possible to catch the ship, otherwise disqualify 00094 if (!Navigator.IsReachable(origin.GetLocation(), GetLocation(), this.GetHeading())) 00095 return null; 00096 00097 //Reduce the score in proportion to distance. Lower score is worse. Mind the minus sign! 00098 double distance = Navigator.CalcDistFromLine(origin.GetLocation(), this.GetLocation(), this.GetHeading()); 00099 00100 //consider attack radius 00101 distance -= Math.Sqrt(Bot.Game.GetAttackRadius()); 00102 distance = Math.Max(distance, 0); 00103 00104 //if the origin group is strong enough to take the enemy group add its score 00105 if (origin.FightCount() > this.GetMaxFightPower()) 00106 return new Score(this, TargetType.EnemyGroup, 0, this.EnemyPirates.Count, distance, 00107 Bot.Game.GetSpawnTurns(), 0); 00108 00109 //if this enemygroup will defeat the origin group, disqualify 00110 return null; 00111 } 00112 00117 public Location GetLocation() 00118 { 00119 return this.GetLocation(false); 00120 } 00121 00131 private Location GetLocation(bool forcePirate) 00132 { 00133 //Get a list of all location of the enemy pirates in this group 00134 List<Location> locs = new List<Location>(); 00135 00136 if (this.EnemyPirates == null) 00137 return new Location(0, 0); 00138 00139 foreach (int e in this.EnemyPirates) 00140 { 00141 Pirate enemyPirate = Bot.Game.GetEnemyPirate(e); 00142 00143 if (enemyPirate != null) 00144 locs.Add(enemyPirate.Loc); 00145 } 00146 00147 //sum all the locations! 00148 int totalCol = locs.Sum(loc => loc.Col); 00149 int totalRow = locs.Sum(loc => loc.Row); 00150 00151 Location averageLocation = new Location(0, 0); 00152 00153 //return the average location 00154 if (locs.Count != 0) 00155 averageLocation = new Location(totalRow / locs.Count, totalCol / locs.Count); 00156 00157 if (forcePirate) 00158 { 00159 int minDistance = Bot.Game.GetCols() + Bot.Game.GetCols(); 00160 Pirate pete = null; 00161 00162 //iterate over all the pirate and find the one with the minimun distance to the average location 00163 foreach (Pirate pirate in this.EnemyPirates.ConvertAll(p => Bot.Game.GetEnemyPirate(p))) 00164 { 00165 if (pirate.IsLost) 00166 continue; 00167 00168 int currDistance = Bot.Game.Distance(averageLocation, pirate.Loc); 00169 if (currDistance < minDistance) 00170 { 00171 minDistance = currDistance; 00172 pete = pirate; 00173 } 00174 } 00175 00176 //set the returned location to the central pirate location 00177 if (pete != null) 00178 averageLocation = pete.Loc; 00179 } 00180 return averageLocation; 00181 } 00182 00191 public Direction GetDirection(Group group) 00192 { 00193 //calculates the direction based on the geographical data from the game 00194 //first check if stationary 00195 if (Math.Abs(this.GetHeading().Norm()) < Magic.VectorTolerance) 00196 return Navigator.CalculateDirectionToStationeryTarget(group.FindCenter(true), group.Heading, 00197 this.GetLocation()); 00198 00199 //otherwise 00200 return Navigator.CalculateDirectionToMovingTarget(group.FindCenter(true), group.Heading, GetLocation(), 00201 this.GetHeading()); 00202 } 00203 00208 public TargetType GetTargetType() 00209 { 00210 return TargetType.EnemyGroup; 00211 } 00212 00217 public string GetDescription() 00218 { 00219 string s = "Enemy Group, Pirates: "; 00220 foreach (int pirate in EnemyPirates) 00221 s += " " + pirate; 00222 00223 return s; 00224 } 00225 00231 public bool Equals(ITarget operandB) 00232 { 00233 EnemyGroup enemyGroup = operandB as EnemyGroup; 00234 if (enemyGroup != null) 00235 { 00236 EnemyGroup b = enemyGroup; 00237 return this.Id == b.Id; 00238 } 00239 00240 return false; 00241 } 00242 00246 public void TargetAssignmentEvent() 00247 { 00248 this._lastAssignmentTurn = Bot.Game.GetTurn(); 00249 } 00250 00255 public void TargetDeAssignmentEvent() 00256 { 00257 //this defines what is the minimum turn number till it is legit to change target (on average) 00258 //check if time from the last assignment raises suspition of inteligence in the enemy 00259 if (Bot.Game.GetTurn() - this._lastAssignmentTurn < Magic.minimumTillItIsOkToDropTarget) 00260 { 00261 Enemy.EnemyIntelligenceSuspicionCounter++; 00262 } 00263 } 00264 00265 #endregion 00266 00271 public override int GetHashCode() 00272 { 00273 unchecked 00274 { 00275 int hashCode = this.Id; 00276 hashCode = (hashCode * 397) ^ this._lastAssignmentTurn; 00277 hashCode = (hashCode * 397) ^ (this._lastDirections != null ? this._lastDirections.GetHashCode() : 0); 00278 hashCode = (hashCode * 397) ^ 00279 (this._lastMaxFightPower != null ? this._lastMaxFightPower.GetHashCode() : 0); 00280 hashCode = (hashCode * 397) ^ (this.PrevLoc != null ? this.PrevLoc.GetHashCode() : 0); 00281 hashCode = (hashCode * 397) ^ (this.EnemyPirates != null ? this.EnemyPirates.GetHashCode() : 0); 00282 return hashCode; 00283 } 00284 } 00285 00291 private int MaxFightCount() 00292 { 00293 //initialize maximum variable 00294 int maxFightCount = 0; 00295 00296 //go over all pirates 00297 foreach (int pirate in this.EnemyPirates) 00298 { 00299 //convert to pirate 00300 Pirate pete = Bot.Game.GetEnemyPirate(pirate); 00301 00302 //check if this pirate counts 00303 if (Bot.Game.isCapturing(pete)) 00304 continue; 00305 //count how many pirates are supporting him 00306 int supportCount = 0; 00307 00308 //go over all other pirates 00309 foreach (int otherPirate in this.EnemyPirates) 00310 { 00311 //convert to pirate 00312 Pirate otherPete = Bot.Game.GetEnemyPirate(otherPirate); 00313 00314 //check if this pirate counts 00315 if (Bot.Game.isCapturing(otherPete)) 00316 continue; 00317 00318 //check if in range 00319 if (Bot.Game.EuclidianDistanceSquared(pete.Loc, otherPete.Loc) <= Bot.Game.GetAttackRadius()) 00320 supportCount++; 00321 } 00322 00323 //check if the support count is bigger then the maximum 00324 if (maxFightCount < supportCount) 00325 maxFightCount = supportCount; 00326 } 00327 00328 //return result 00329 return maxFightCount; 00330 } 00331 00339 private int InRangeGroupDistance(EnemyGroup eg, Group group) 00340 { 00341 Pirate enemyPirate = null, myPirate = null; 00342 00343 int minDistance = Bot.Game.GetCols() + Bot.Game.GetRows(); 00344 00345 //find the two pirate from the two group with the minimum distance between 00346 for (int i = 0; i < eg.EnemyPirates.Count; i++) 00347 { 00348 Pirate p = Bot.Game.GetEnemyPirate(i); 00349 00350 for (int j = 0; j < group.Pirates.Count; j++) 00351 { 00352 Pirate aPirate = Bot.Game.GetMyPirate(group.Pirates[j]); 00353 00354 int distance = Bot.Game.Distance(p, aPirate); 00355 00356 if (distance >= minDistance) 00357 continue; 00358 00359 minDistance = distance; 00360 enemyPirate = p; 00361 myPirate = aPirate; 00362 } 00363 } 00364 00365 //return the distance between these pirates with the range in mind 00366 return Bot.Game.Distance(enemyPirate.Loc, myPirate.Loc) - Bot.Game.GetAttackRadius() * 2; 00367 } 00368 00374 public bool IsInGroup(int enemyPirate) 00375 { 00376 Pirate ePirate = Bot.Game.GetEnemyPirate(enemyPirate); 00377 00378 //check if the pirate is null 00379 if (ePirate == null) 00380 return false; 00381 00382 //Check if the given pirate is close (max of 2 distance units) to any of the pirates already in this group 00383 if (ePirate.IsLost) 00384 return false; 00385 00386 return 00387 this.EnemyPirates.ConvertAll(e => Bot.Game.GetEnemyPirate(e)) 00388 .Any(e => Bot.Game.IsReallyInRange(ePirate.Loc, e.Loc)); 00389 } 00390 00397 public static bool IsInGroup(List<int> group, int enemyPirate) 00398 { 00399 Pirate ePirate = Bot.Game.GetEnemyPirate(enemyPirate); 00400 00401 if (ePirate.IsLost) 00402 return false; 00403 00404 //Check if the given pirate is close (max of 2 distance units) to any of the pirates already in this group 00405 return 00406 group.ConvertAll(e => Bot.Game.GetEnemyPirate(e)) 00407 .Select(ep => Bot.Game.Distance(ep, ePirate)) 00408 .Concat(new int[] {}) 00409 .Min() <= 2; 00410 } 00411 00417 public double MinimalSquaredDistanceTo(Location location) 00418 { 00419 double min = Bot.Game.GetCols() + Bot.Game.GetRows(); 00420 foreach (int pirate in EnemyPirates) 00421 { 00422 if (Bot.Game.EuclidianDistanceSquared(location, this.GetLocation()) < min) 00423 min = Bot.Game.EuclidianDistanceSquared(location, this.GetLocation()); 00424 } 00425 return min; 00426 } 00427 00433 public double MinimalETATo(Location location) 00434 { 00435 double min = Bot.Game.GetCols() + Bot.Game.GetRows(); 00436 foreach (int pirate in EnemyPirates) 00437 { 00438 if (Bot.Game.Distance(location, this.GetLocation()) < min) 00439 min = Bot.Game.EuclidianDistanceSquared(location, this.GetLocation()); 00440 } 00441 return min; 00442 } 00443 00448 public SmartIsland GuessTarget() 00449 { 00450 //Sort the islands by distance from this enemy group 00451 List<SmartIsland> sortedByDistance = SmartIsland.IslandList; 00452 sortedByDistance.Sort( 00453 (a, b) => 00454 Bot.Game.Distance(b.Loc, this.GetLocation()).CompareTo(Bot.Game.Distance(a.Loc, this.GetLocation()))); 00455 00456 foreach (SmartIsland isle in sortedByDistance) 00457 { 00458 //check if distance is smaller then tolerance margin 00459 if (Navigator.CalcDistFromLine(isle.GetLocation(), GetLocation(), this.GetHeading()) < Magic.toleranceMargin) 00460 return isle; 00461 } 00462 00463 return null; 00464 } 00465 00473 public bool IsApproachingIsland(SmartIsland sIsland) 00474 { 00475 //if the group is stationary just check if it is close 00476 if (this.GetHeading().NormSquared() <= 2) 00477 { 00478 return Bot.Game.EuclidianDistanceSquared(this.GetLocation(), sIsland.Loc) <= 00479 Magic.ApproachDistanceSquaredOfStationaryTarget; 00480 } 00481 //check if the enemy group is on the island 00482 if (this.MinimalSquaredDistanceTo(sIsland.Loc) < 20) 00483 return true; 00484 00485 //calculate the difference vector between the enemy group and the island 00486 HeadingVector difference = HeadingVector.CalcDifference(this.GetLocation(), sIsland.Loc); 00487 00488 //check if it isn't moving in the general direction of the island, if so return false 00489 if (difference * this.GetHeading() <= 0) 00490 { 00491 return false; 00492 } 00493 //read the distance between the trajectory of the enemy group and the island 00494 double distance = Navigator.CalcDistFromLine(sIsland.Loc, this.GetLocation(), this.GetHeading()); 00495 /*Logger.Write("Loc " + sIsland.Loc + " line loc: " + this.GetLocation().ToString() + " heading: " + this.GetHeading().ToString()); 00496 Logger.Write("Distance: " + distance);*/ 00497 //compare it to the constant defined in magic 00498 return distance <= Magic.EnemyPredictionSensitivity; 00499 } 00500 00506 public double GetMaxFightPower() 00507 { 00508 //check if LastMaxFightPower isnt empty 00509 if (this._lastMaxFightPower.Count == 0) 00510 return 0; 00511 //otherwise 00512 return this._lastMaxFightPower.Average(); 00513 } 00514 00519 public void Update() 00520 { 00521 //get the new direction of the last turn 00522 Direction newDirection = Bot.Game.GetDirections(this.PrevLoc, this.GetLocation())[0]; 00523 00524 //update previous location 00525 PrevLoc = GetLocation(); 00526 00527 //update directions 00528 this._lastDirections.Enqueue(newDirection); 00529 00530 //check if we need to throw irrelevant stuff out 00531 if (this._lastDirections.Count > Magic.EnemyLocationQueueSize) 00532 { 00533 this._lastDirections.Dequeue(); 00534 } 00535 00536 //update maximum fire power 00537 this._lastMaxFightPower.Enqueue(this.MaxFightCount()); 00538 00539 //check if we need to throw irrelevant stuff out 00540 if (this._lastMaxFightPower.Count > Magic.EnemyLocationQueueSize) 00541 { 00542 this._lastMaxFightPower.Dequeue(); 00543 } 00544 } 00545 00551 public HeadingVector GetHeading() 00552 { 00553 //creating an accumulator heading vector with (0,0) values 00554 HeadingVector hv = new HeadingVector(); 00555 00556 //going over the last directions of this group and adding them up 00557 foreach (Direction dir in this._lastDirections) 00558 { 00559 //temporal variable for conversion 00560 HeadingVector currHeading = new HeadingVector(dir); 00561 hv += currHeading; 00562 } 00563 00564 //return result 00565 return hv; 00566 } 00567 00574 public double GetHeadingSabilityCoeff() 00575 { 00576 //count actual moves 00577 int moveCount = 0; 00578 00579 //go over all the direction and compare to Direction.Nothing 00580 foreach (Direction dir in this._lastDirections) 00581 { 00582 if (dir != Direction.NOTHING) 00583 moveCount++; 00584 } 00585 00586 //if move count is 0 then target is stationary and thus very stable 00587 if (moveCount == 0) 00588 return 1; 00589 //otherwise return that 00590 return this.GetHeading().Norm1() / moveCount; 00591 } 00592 00593 public override string ToString() 00594 { 00595 return "EnemyGroup- id: " + this.Id + ", fight power: " + this.GetMaxFightPower() 00596 + ", Heading: " + this.GetHeading() + " location: " + GetLocation(); 00597 } 00598 00604 protected bool Equals(EnemyGroup other) 00605 { 00606 if (this.EnemyPirates.Count != other.EnemyPirates.Count) 00607 return false; 00608 00609 for (int i = 0; i < this.EnemyPirates.Count; i++) 00610 if (this.EnemyPirates[i] != other.EnemyPirates[i]) 00611 return false; 00612 00613 return true; 00614 } 00615 00621 public override bool Equals(object obj) 00622 { 00623 if (object.ReferenceEquals(null, obj)) 00624 return false; 00625 if (object.ReferenceEquals(this, obj)) 00626 return true; 00627 if (obj.GetType() != this.GetType()) 00628 return false; 00629 return Equals((EnemyGroup) obj); 00630 } 00631 00632 public bool IsFormed() 00633 { 00634 Location[][] ringLocations = new Location[Group.GetRingCount(this.EnemyPirates.Count)][]; 00635 00636 //set the pivot 00637 Location pivot = this.GetLocation(true); 00638 00639 //set the locations 00640 for (int i = 0; i < ringLocations.Length; i++) 00641 { 00642 ringLocations[i] = Group.GenerateRingLocations(pivot, i).ToArray(); 00643 } 00644 00645 //excluding the 0th ring which is special 00646 int maxRing = ringLocations.Length - 2; 00647 00648 //this is basic summation of arithmetic sequence excluding the first ting 00649 int maxEmptySpots = Group.GetStructureVolume(maxRing) - this.EnemyPirates.Count; 00650 00651 //iterate over all the rings 00652 for (int i = 0; i < ringLocations.Length; i++) 00653 { 00654 //iterate over all the location in each ring 00655 for (int k = 0; k < ringLocations[i].Length; k++) 00656 { 00657 if (Bot.Game.GetPirateOn(ringLocations[i][k]) == null) 00658 if (i == maxRing) 00659 maxEmptySpots--; 00660 else 00661 return false; 00662 } 00663 } 00664 00665 if (maxEmptySpots < 0) 00666 return false; 00667 00668 return true; 00669 } 00670 } 00671 }
1.7.6.1